Skip to content

Commit

Permalink
ctp-list: adjust logic
Browse files Browse the repository at this point in the history
Signed-off-by: Piotr Zaniewski <piotr@upbound.io>
Signed-off-by: Dr. Stefan Schimanski <stefan.schimanski@upbound.com>
  • Loading branch information
sttts committed Apr 16, 2024
1 parent dd1db77 commit b7c8d85
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 35 deletions.
33 changes: 31 additions & 2 deletions cmd/up/controlplane/controlplane.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ import (
"github.com/alecthomas/kong"
"github.com/posener/complete"
"k8s.io/apimachinery/pkg/util/duration"
"k8s.io/utils/ptr"

xpcommonv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"

spacesv1beta1 "github.com/upbound/up-sdk-go/apis/spaces/v1beta1"
cp "github.com/upbound/up-sdk-go/service/controlplanes"
"github.com/upbound/up/cmd/up/controlplane/connector"
"github.com/upbound/up/cmd/up/controlplane/kubeconfig"
Expand Down Expand Up @@ -134,7 +138,7 @@ func extractCloudFields(obj any) []string {
}
}

func extractSpaceFields(obj any) []string {
func extractSpaceFieldsLegacy(obj any) []string {
resp, ok := obj.(*controlplane.Response)
if !ok {
return []string{"unknown", "unknown", "", "", "", "", ""}
Expand All @@ -151,6 +155,28 @@ func extractSpaceFields(obj any) []string {
}
}

func extractSpaceFields(obj any) []string {
ctp, ok := obj.(spacesv1beta1.ControlPlane)
if !ok {
return []string{"unknown", "unknown", "", "", "", "", ""}
}

v := ""
if pv := ctp.Spec.Crossplane.Version; pv != nil {
v = *pv
}

return []string{
ctp.GetNamespace(),
ctp.GetName(),
v,
string(ctp.GetCondition(xpcommonv1.TypeSynced).Status),
string(ctp.GetCondition(xpcommonv1.TypeReady).Status),
ctp.Annotations["internal.spaces.upbound.io/message"],
formatAge(ptr.To(time.Since(ctp.CreationTimestamp.Time))),
}
}

func formatAge(age *time.Duration) string {
if age == nil {
return ""
Expand All @@ -160,8 +186,11 @@ func formatAge(age *time.Duration) string {
}

func tabularPrint(obj any, printer upterm.ObjectPrinter, upCtx *upbound.Context) error {
if upCtx.Profile.IsSpace() {
if obj, ok := obj.([]spacesv1beta1.ControlPlane); ok {
return printer.Print(obj, spacefieldNames, extractSpaceFields)
}
if upCtx.Profile.IsSpace() {
return printer.Print(obj, spacefieldNames, extractSpaceFieldsLegacy)
}
return printer.Print(obj, cloudfieldNames, extractCloudFields)
}
88 changes: 56 additions & 32 deletions cmd/up/controlplane/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@ import (

"github.com/alecthomas/kong"
"github.com/pterm/pterm"
"k8s.io/client-go/dynamic"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"

"github.com/crossplane/crossplane-runtime/pkg/errors"

spacesv1beta1 "github.com/upbound/up-sdk-go/apis/spaces/v1beta1"
"github.com/upbound/up-sdk-go/service/configurations"
cp "github.com/upbound/up-sdk-go/service/controlplanes"
"github.com/upbound/up/internal/controlplane"
"github.com/upbound/up/internal/controlplane/cloud"
"github.com/upbound/up/internal/controlplane/space"
"github.com/upbound/up/internal/profile"
"github.com/upbound/up/internal/upbound"
"github.com/upbound/up/internal/upterm"
)
Expand All @@ -38,58 +41,79 @@ type ctpLister interface {
type listCmd struct {
Group string `short:"g" help:"The control plane group that the control plane is contained in. This defaults to the group specified in the current profile."`
AllGroups bool `short:"A" default:"false" help:"List control planes across all groups."`

client ctpLister
}

// AfterApply sets default values in command after assignment and validation.
func (c *listCmd) AfterApply(kongCtx *kong.Context, upCtx *upbound.Context) error {
func (c *listCmd) AfterApply(kongCtx *kong.Context) error {
kongCtx.Bind(pterm.DefaultTable.WithWriter(kongCtx.Stdout).WithSeparator(" "))

return nil
}

// Run executes the list command.
func (c *listCmd) Run(ctx context.Context, printer upterm.ObjectPrinter, upCtx *upbound.Context, p pterm.TextPrinter) error { // nolint:gocyclo
var lister ctpLister
if upCtx.Profile.IsSpace() {
kubeconfig, ns, err := upCtx.Profile.GetSpaceRestConfig()
// get context
_, currentProfile, ctp, err := upCtx.Cfg.GetCurrentContext(ctx)
if err != nil {
return err
}
if c.Group == "" {
c.Group = ns
if currentProfile == nil {
return errors.New(profile.NoSpacesContextMsg)
}
if ctp.Namespace == "" {
return errors.New(profile.NoGroupMsg)
}
if ctp.Name != "" {
return errors.New("Cannot list control planes from inside a control plane, use `up ctx ..` to switch to a group level.")
}

client, err := dynamic.NewForConfig(kubeconfig)
// create client
restConfig, _, err := currentProfile.GetSpaceRestConfig()
if err != nil {
return err
}
cl, err := ctrlclient.New(restConfig, ctrlclient.Options{})
if err != nil {
return err
}
c.client = space.New(client)

// list control planes
ns := ctp.Namespace
if c.AllGroups {
ns = ""
}
var ctps spacesv1beta1.ControlPlaneList
if err := cl.List(ctx, &ctps, ctrlclient.InNamespace(ns)); err != nil {
return err
}

return tabularPrint(ctps.Items, printer, upCtx)
} else {
cfg, err := upCtx.BuildSDKConfig()
if err != nil {
return err
}
ctpclient := cp.NewClient(cfg)
cfgclient := configurations.NewClient(cfg)
lister = cloud.New(ctpclient, cfgclient, upCtx.Account)

c.client = cloud.New(ctpclient, cfgclient, upCtx.Account)
}

kongCtx.Bind(pterm.DefaultTable.WithWriter(kongCtx.Stdout).WithSeparator(" "))
return nil
}

// Run executes the list command.
func (c *listCmd) Run(ctx context.Context, printer upterm.ObjectPrinter, p pterm.TextPrinter, upCtx *upbound.Context) error {
l, err := c.client.List(ctx, c.deriveGroup())
if controlplane.IsNotFound(err) {
p.Printfln("No Control planes found in %s group", c.deriveGroup())
return nil
}
if err != nil {
return err
}
l, err := lister.List(ctx, c.deriveGroup())
if controlplane.IsNotFound(err) {
p.Printfln("No Control planes found in %s group", c.deriveGroup())
return nil
}
if err != nil {
return err
}

if len(l) == 0 {
p.Println("No control planes found")
return nil
if len(l) == 0 {
p.Println("No control planes found")
return nil
}
return tabularPrint(l, printer, upCtx)
}

return tabularPrint(l, printer, upCtx)
}

func (c *listCmd) deriveGroup() string {
Expand Down
25 changes: 25 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@ package config

import (
"bytes"
"context"
"encoding/json"
"io"
"os"
"path/filepath"

"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/clientcmd"

"github.com/crossplane/crossplane-runtime/pkg/errors"

"github.com/upbound/up/internal/profile"
Expand Down Expand Up @@ -122,6 +126,27 @@ func (c *Config) GetUpboundProfile(name string) (profile.Profile, error) {
return p, nil
}

// GetCurrentContext returns the current context from the kubeconfig, profile and config
func (c *Config) GetCurrentContext(ctx context.Context) (profileName string, currentProfile *profile.Profile, ctp types.NamespacedName, err error) {
po := clientcmd.NewDefaultPathOptions()
conf, err := po.GetStartingConfig()
if err != nil {
return "", nil, types.NamespacedName{}, err
}

profiles, err := c.GetUpboundProfiles()
if err != nil {
return "", nil, types.NamespacedName{}, err
}

profileName, currentProfile, ctp, err = profile.FromKubeconfig(ctx, profiles, conf)
if err != nil {
return "", nil, types.NamespacedName{}, err
}

return profileName, currentProfile, ctp, nil
}

// GetUpboundProfiles returns the list of existing profiles. If no profiles
// exist, then an error will be returned.
func (c *Config) GetUpboundProfiles() (map[string]profile.Profile, error) {
Expand Down
5 changes: 4 additions & 1 deletion internal/profile/kubeconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ import (
// FromKubeconfig finds the profile by a given user kubeconfig. It returns
// a related profile, the current group/namespace, and the controlplane if the
// kubeconfig points to a controlplane through mxe-router.
func FromKubeconfig(ctx context.Context, profiles map[string]Profile, conf *clientcmdapi.Config) (string, *Profile, types.NamespacedName, error) {
//
// If the current kubeconfig context does not point to a Space or a controlplane,
// it returns NO ERROR, but all other values are zero values.
func FromKubeconfig(ctx context.Context, profiles map[string]Profile, conf *clientcmdapi.Config) (profileName string, profile *Profile, namespace types.NamespacedName, err error) {
return findProfileByKubeconfig(ctx, profiles, conf, GetIngressHost)
}

Expand Down
5 changes: 5 additions & 0 deletions internal/profile/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ import (
"k8s.io/client-go/tools/clientcmd"
)

const (
NoSpacesContextMsg = "This cluster does not have spaces installed, use `up space init` to install spaces."
NoGroupMsg = "The current kubeconfig context does not point to a group, use `up ctx` to select a group."
)

// Type is a type of Upbound profile.
type Type string

Expand Down

0 comments on commit b7c8d85

Please sign in to comment.