diff --git a/cmd/up/ctx/actions_test.go b/cmd/up/ctx/actions_test.go index 7182e12f..0048b0c0 100644 --- a/cmd/up/ctx/actions_test.go +++ b/cmd/up/ctx/actions_test.go @@ -31,6 +31,8 @@ import ( ) func TestDisconnectedGroupAccept(t *testing.T) { + t.Parallel() + spaceExtension := upbound.NewDisconnectedV1Alpha1SpaceExtension("profile") extensionMap := map[string]runtime.Object{upbound.ContextExtensionKeySpace: spaceExtension} @@ -61,7 +63,7 @@ func TestDisconnectedGroupAccept(t *testing.T) { "upbound": {Namespace: "group", Cluster: "upbound", AuthInfo: "upbound", Extensions: extensionMap}, "profile": {Namespace: "group", Cluster: "profile", AuthInfo: "profile"}, }, - Clusters: map[string]*clientcmdapi.Cluster{"upbound": {Server: "profile"}, "upbound-previous": {Server: "server1"}, "profile": {Server: "profile"}}, + Clusters: map[string]*clientcmdapi.Cluster{"upbound": {Server: "https://ingress", CertificateAuthorityData: []uint8{1, 2, 3}}, "upbound-previous": {Server: "server1"}, "profile": {Server: "profile"}}, AuthInfos: map[string]*clientcmdapi.AuthInfo{"upbound": {Token: "profile"}, "upbound-previous": {Token: "token1"}, "profile": {Token: "profile"}}, }, wantLast: "profile", @@ -87,7 +89,7 @@ func TestDisconnectedGroupAccept(t *testing.T) { "upbound-previous": {Namespace: "namespace1", Cluster: "upbound-previous", AuthInfo: "upbound-previous"}, "profile": {Namespace: "group", Cluster: "profile", AuthInfo: "profile"}, }, - Clusters: map[string]*clientcmdapi.Cluster{"upbound": {Server: "profile"}, "upbound-previous": {Server: "server1"}, "profile": {Server: "profile"}}, + Clusters: map[string]*clientcmdapi.Cluster{"upbound": {Server: "https://ingress", CertificateAuthorityData: []uint8{1, 2, 3}}, "upbound-previous": {Server: "server1"}, "profile": {Server: "profile"}}, AuthInfos: map[string]*clientcmdapi.AuthInfo{"upbound": {Token: "profile"}, "upbound-previous": {Token: "token1"}, "profile": {Token: "profile"}}, }, wantLast: "upbound-previous", @@ -113,7 +115,7 @@ func TestDisconnectedGroupAccept(t *testing.T) { "upbound-previous": {Namespace: "namespace2", Cluster: "upbound", AuthInfo: "upbound"}, "profile": {Namespace: "group", Cluster: "profile", AuthInfo: "profile"}, }, - Clusters: map[string]*clientcmdapi.Cluster{"upbound": {Server: "profile"}, "upbound-previous": {Server: "server1"}, "profile": {Server: "profile"}}, + Clusters: map[string]*clientcmdapi.Cluster{"upbound": {Server: "https://ingress", CertificateAuthorityData: []uint8{1, 2, 3}}, "upbound-previous": {Server: "server1"}, "profile": {Server: "profile"}}, AuthInfos: map[string]*clientcmdapi.AuthInfo{"upbound": {Token: "profile"}, "upbound-previous": {Token: "token1"}, "profile": {Token: "profile"}}, }, wantLast: "upbound-previous", @@ -122,6 +124,8 @@ func TestDisconnectedGroupAccept(t *testing.T) { } for name, tt := range tests { t.Run(name, func(t *testing.T) { + t.Parallel() + var last string var conf *clientcmdapi.Config upCtx := &upbound.Context{Kubecfg: clientcmd.NewDefaultClientConfig(*tt.conf, nil)} @@ -166,6 +170,8 @@ func TestDisconnectedGroupAccept(t *testing.T) { } func TestCloudGroupAccept(t *testing.T) { + t.Parallel() + spaceExtension := upbound.NewCloudV1Alpha1SpaceExtension("org", "space") extensionMap := map[string]runtime.Object{upbound.ContextExtensionKeySpace: spaceExtension} spaceAuth := clientcmdapi.AuthInfo{Token: "space"} @@ -258,6 +264,8 @@ func TestCloudGroupAccept(t *testing.T) { } for name, tt := range tests { t.Run(name, func(t *testing.T) { + t.Parallel() + var last string var conf *clientcmdapi.Config upCtx := &upbound.Context{Kubecfg: clientcmd.NewDefaultClientConfig(*tt.conf, nil)} @@ -303,6 +311,8 @@ func TestCloudGroupAccept(t *testing.T) { } func TestDisconnectedControlPlaneAccept(t *testing.T) { + t.Parallel() + spaceExtension := upbound.NewDisconnectedV1Alpha1SpaceExtension("profile") extensionMap := map[string]runtime.Object{upbound.ContextExtensionKeySpace: spaceExtension} @@ -453,6 +463,8 @@ func TestDisconnectedControlPlaneAccept(t *testing.T) { } for name, tt := range tests { t.Run(name, func(t *testing.T) { + t.Parallel() + var last string var conf *clientcmdapi.Config upCtx := &upbound.Context{Kubecfg: clientcmd.NewDefaultClientConfig(*tt.conf, nil)} @@ -500,6 +512,8 @@ func TestDisconnectedControlPlaneAccept(t *testing.T) { } func TestCloudControlPlaneAccept(t *testing.T) { + t.Parallel() + spaceExtension := upbound.NewCloudV1Alpha1SpaceExtension("org", "space") extensionMap := map[string]runtime.Object{upbound.ContextExtensionKeySpace: spaceExtension} @@ -650,6 +664,8 @@ func TestCloudControlPlaneAccept(t *testing.T) { } for name, tt := range tests { t.Run(name, func(t *testing.T) { + t.Parallel() + var last string var conf *clientcmdapi.Config upCtx := &upbound.Context{Kubecfg: clientcmd.NewDefaultClientConfig(*tt.conf, nil)} diff --git a/cmd/up/ctx/navigation.go b/cmd/up/ctx/navigation.go index 49d8030f..cabf7467 100644 --- a/cmd/up/ctx/navigation.go +++ b/cmd/up/ctx/navigation.go @@ -487,12 +487,10 @@ func (s *Space) GetClient(upCtx *upbound.Context) (client.Client, error) { // buildSpacesClient creates a new kubeconfig hardcoded to match the provided // spaces access configuration and pointed directly at the resource. If the // resource only specifies a namespace, then the client will point at the space -// hub and the context will be set at the namespace. If the resource specifies -// both a namespace and a name, then the client will point directly at the -// control plane ingress and set the namespace to "default". -// TODO(redbackthomson): Refactor into smaller methods (one for space-level, one -// for ctp-level) -func (s *Space) buildClient(upCtx *upbound.Context, resource types.NamespacedName) (clientcmd.ClientConfig, error) { // nolint:gocyclo +// and the context will be set at the group. If the resource specifies both a +// namespace and a name, then the client will point directly at the control +// plane ingress and set the namespace to "default". +func (s *Space) buildClient(upCtx *upbound.Context, resource types.NamespacedName) (clientcmd.ClientConfig, error) { // reference name for all context, cluster and authinfo for in-memory // kubeconfig ref := "upbound" @@ -511,65 +509,49 @@ func (s *Space) buildClient(upCtx *upbound.Context, resource types.NamespacedNam AuthInfos: make(map[string]*clientcmdapi.AuthInfo), } + // Build a new context with a new cluster that points to the space's + // ingress. refContext := &clientcmdapi.Context{ Extensions: make(map[string]runtime.Object), + Cluster: ref, } - if resource.Name == "" { - // point at the space hub - refContext.Namespace = resource.Namespace + if s.Ingress.Host == "" { + return nil, errors.New("missing ingress address for context") + } + if len(s.Ingress.CAData) == 0 { + return nil, errors.New("missing ingress CA for context") + } + + config.Clusters[ref] = &clientcmdapi.Cluster{ + Server: profile.ToSpacesK8sURL(s.Ingress.Host, resource), + CertificateAuthorityData: s.Ingress.CAData, + } + // Use the space's authinfo if we have it, otherwise fall back to the hub + // context's auth. + switch { + case s.AuthInfo != nil: + config.AuthInfos[ref] = s.AuthInfo + refContext.AuthInfo = ref + case s.HubContext != "": hubContext, ok := prev.Contexts[s.HubContext] - if s.HubContext != "" && ok { - // import the cluster and authinfo from the hub context - refContext.Cluster = hubContext.Cluster - config.Clusters[hubContext.Cluster] = ptr.To(*prev.Clusters[hubContext.Cluster]) + if ok { + // import the authinfo from the hub context refContext.AuthInfo = hubContext.AuthInfo config.AuthInfos[hubContext.AuthInfo] = ptr.To(*prev.AuthInfos[hubContext.AuthInfo]) - } else { - // fall back to ingress if hub context not available - config.Clusters[ref] = &clientcmdapi.Cluster{ - Server: profile.ToSpacesK8sURL(s.Ingress.Host, resource), - } - if len(s.Ingress.CAData) == 0 { - config.Clusters[ref].InsecureSkipTLSVerify = true - } else { - config.Clusters[ref].CertificateAuthorityData = s.Ingress.CAData - } - refContext.Cluster = ref - - if s.AuthInfo != nil { - config.AuthInfos[ref] = s.AuthInfo - refContext.AuthInfo = ref - } } + default: + return nil, errors.New("no auth info for context") + } + + if resource.Name == "" { + // point at the relevant namespace in the space hub + refContext.Namespace = resource.Namespace } else { // since we are pointing at an individual control plane, point at the // "default" namespace inside it refContext.Namespace = "default" - - config.Clusters[ref] = &clientcmdapi.Cluster{ - Server: profile.ToSpacesK8sURL(s.Ingress.Host, resource), - } - refContext.Cluster = ref - - if len(s.Ingress.CAData) == 0 { - config.Clusters[ref].InsecureSkipTLSVerify = true - } else { - config.Clusters[ref].CertificateAuthorityData = s.Ingress.CAData - } - - if s.AuthInfo != nil { - config.AuthInfos[ref] = s.AuthInfo - refContext.AuthInfo = ref - } else if s.HubContext != "" { - hubContext, ok := prev.Contexts[s.HubContext] - if ok { - // import the authinfo from the hub context - refContext.AuthInfo = hubContext.AuthInfo - config.AuthInfos[hubContext.AuthInfo] = ptr.To(*prev.AuthInfos[hubContext.AuthInfo]) - } - } } if s.IsCloud() {