Remove assigning by default first alphabetical kube cluster to a user. (#37242)

* Remove assigning by default first alphabetical kube cluster to a user.

* Fix tests.

* Address review comments

* Fix tests.

* Fix error message.

* Use default value.

Co-authored-by: rosstimothy <[email protected]>

* Fix error message.

Co-authored-by: rosstimothy <[email protected]>

---------

Co-authored-by: rosstimothy <[email protected]>
This commit is contained in:
Anton Miniailo
2024-01-29 22:47:34 +00:00
committed by GitHub
co-authored by rosstimothy
parent 6841c77574
commit a576cb2a91
12 changed files with 142 additions and 188 deletions
+8 -4
View File
@@ -120,14 +120,18 @@ func awsEKSDiscoveryMatchedCluster(t *testing.T) {
return err == nil && len(kubeServers) == 1
}, 2*time.Minute, time.Second, "wait for the kubernetes service to create a KubernetesServer")
clusters, err := authC.GetKubernetesClusters(context.Background())
require.NoError(t, err)
// kubeClient is a Kubernetes client for the user created above
// that will be used to verify that the user can access the cluster and
// the permissions are correct.
kubeClient, _, err := kube.ProxyClient(kube.ProxyConfig{
T: teleport,
Username: username,
KubeUsers: kubeUsers,
KubeGroups: kubeGroups,
T: teleport,
Username: username,
KubeUsers: kubeUsers,
KubeGroups: kubeGroups,
KubeCluster: clusters[0].GetName(),
})
require.NoError(t, err)
+14 -6
View File
@@ -45,6 +45,7 @@ type ProxyConfig struct {
PinnedIP string
KubeUsers []string
KubeGroups []string
KubeCluster string
Impersonation *rest.ImpersonationConfig
RouteToCluster string
CustomTLSServerName string
@@ -95,13 +96,20 @@ func ProxyClient(cfg ProxyConfig) (*kubernetes.Clientset, *rest.Config, error) {
return nil, nil, trace.Wrap(err)
}
kubeServers, _ := authServer.GetKubernetesServers(ctx)
kubeCluster := cfg.KubeCluster
if cfg.KubeCluster == "" && len(kubeServers) > 0 {
kubeCluster = kubeServers[0].GetCluster().GetName()
}
id := tlsca.Identity{
Username: cfg.Username,
Groups: user.GetRoles(),
KubernetesUsers: cfg.KubeUsers,
KubernetesGroups: cfg.KubeGroups,
RouteToCluster: cfg.RouteToCluster,
PinnedIP: cfg.PinnedIP,
Username: cfg.Username,
Groups: user.GetRoles(),
KubernetesUsers: cfg.KubeUsers,
KubernetesGroups: cfg.KubeGroups,
RouteToCluster: cfg.RouteToCluster,
KubernetesCluster: kubeCluster,
PinnedIP: cfg.PinnedIP,
}
subj, err := id.Subject()
if err != nil {
+2
View File
@@ -683,6 +683,7 @@ func testKubeTrustedClustersClientCert(t *testing.T, suite *KubeSuite) {
T: main,
Username: username,
KubeGroups: mainKubeGroups,
KubeCluster: clusterAux,
Impersonation: &rest.ImpersonationConfig{UserName: "bob", Groups: []string{kube.TestImpersonationGroup}},
RouteToCluster: clusterAux,
})
@@ -697,6 +698,7 @@ func testKubeTrustedClustersClientCert(t *testing.T, suite *KubeSuite) {
T: main,
Username: username,
KubeGroups: mainKubeGroups,
KubeCluster: clusterAux,
RouteToCluster: clusterAux,
})
require.NoError(t, err)
+4
View File
@@ -375,6 +375,7 @@ func TestALPNSNIProxyKube(t *testing.T) {
PinnedIP: "127.0.0.1",
KubeUsers: kubeRoleSpec.Allow.KubeGroups,
KubeGroups: kubeRoleSpec.Allow.KubeUsers,
KubeCluster: "root.example.com",
CustomTLSServerName: localK8SNI,
TargetAddress: suite.root.Config.Proxy.WebAddr,
})
@@ -491,6 +492,7 @@ func TestALPNSNIProxyKubeV2Leaf(t *testing.T) {
PinnedIP: "127.0.0.1",
KubeUsers: kubeRoleSpec.Allow.KubeGroups,
KubeGroups: kubeRoleSpec.Allow.KubeUsers,
KubeCluster: "gke_project_europecentral2a_cluster1",
CustomTLSServerName: localK8SNI,
TargetAddress: suite.root.Config.Proxy.WebAddr,
RouteToCluster: suite.leaf.Secrets.SiteName,
@@ -642,6 +644,7 @@ func TestKubePROXYProtocol(t *testing.T) {
Username: kubeRoleSpec.Allow.Logins[0],
KubeUsers: kubeRoleSpec.Allow.KubeGroups,
KubeGroups: kubeRoleSpec.Allow.KubeUsers,
KubeCluster: kubeClusterName,
CustomTLSServerName: kubeCluster,
TargetAddress: targetAddr,
RouteToCluster: testCluster.Secrets.SiteName,
@@ -769,6 +772,7 @@ func TestKubeIPPinning(t *testing.T) {
PinnedIP: tc.pinnedIP,
KubeUsers: kubeRoleSpec.Allow.KubeGroups,
KubeGroups: kubeRoleSpec.Allow.KubeUsers,
KubeCluster: kubeClusterName,
CustomTLSServerName: kubeCluster,
TargetAddress: suite.root.Config.Proxy.WebAddr,
RouteToCluster: tc.routeToCluster,
+7 -10
View File
@@ -2608,16 +2608,13 @@ func generateCert(a *Server, req certRequest, caType types.CertAuthType) (*proto
if err != nil && !trace.IsNotFound(err) {
return nil, trace.Wrap(err)
}
// Only validate/default kubernetes cluster name for the current teleport
// cluster. If this cert is targeting a trusted teleport cluster, leave all
// the kubernetes cluster validation up to them.
if req.routeToCluster == clusterName {
req.kubernetesCluster, err = kubeutils.CheckOrSetKubeCluster(a.closeCtx, a, req.kubernetesCluster, clusterName)
if err != nil {
if !trace.IsNotFound(err) {
return nil, trace.Wrap(err)
}
log.Debug("Failed setting default kubernetes cluster for user login (user did not provide a cluster); leaving KubernetesCluster extension in the TLS certificate empty")
// Ensure that the Kubernetes cluster name specified in the request exists
// when the certificate is intended for a local Kubernetes cluster.
// If the certificate is targeting a trusted Teleport cluster, it is the
// responsibility of the cluster to ensure its existence.
if req.routeToCluster == clusterName && req.kubernetesCluster != "" {
if err := kubeutils.CheckKubeCluster(a.closeCtx, a, req.kubernetesCluster); err != nil {
return nil, trace.Wrap(err)
}
}
+20 -26
View File
@@ -365,8 +365,6 @@ func TestAuthenticateSSHUser(t *testing.T) {
require.NoError(t, err)
require.Equal(t, wantID, *gotID)
// Register a kubernetes cluster to verify the defaulting logic in TLS cert
// generation.
kubeCluster, err := types.NewKubernetesClusterV3(
types.Metadata{
Name: "root-kube-cluster",
@@ -411,8 +409,7 @@ func TestAuthenticateSSHUser(t *testing.T) {
require.NoError(t, err)
require.Equal(t, wantID, *gotID)
// Login without specifying kube cluster. A registered one should be picked
// automatically.
// Login without specifying kube cluster. Kube cluster in the certificate should be empty.
resp, err = s.a.AuthenticateSSHUser(ctx, AuthenticateSSHRequest{
AuthenticateUserRequest: AuthenticateUserRequest{
Username: user,
@@ -430,16 +427,15 @@ func TestAuthenticateSSHUser(t *testing.T) {
gotTLSCert, err = tlsca.ParseCertificatePEM(resp.TLSCert)
require.NoError(t, err)
wantID = tlsca.Identity{
Username: user,
Groups: []string{role.GetName()},
Principals: []string{user, teleport.SSHSessionJoinPrincipal},
KubernetesUsers: []string{user},
KubernetesGroups: []string{"system:masters"},
KubernetesCluster: "root-kube-cluster",
Expires: gotTLSCert.NotAfter,
RouteToCluster: s.clusterName.GetClusterName(),
TeleportCluster: s.clusterName.GetClusterName(),
PrivateKeyPolicy: keys.PrivateKeyPolicyNone,
Username: user,
Groups: []string{role.GetName()},
Principals: []string{user, teleport.SSHSessionJoinPrincipal},
KubernetesUsers: []string{user},
KubernetesGroups: []string{"system:masters"},
Expires: gotTLSCert.NotAfter,
RouteToCluster: s.clusterName.GetClusterName(),
TeleportCluster: s.clusterName.GetClusterName(),
PrivateKeyPolicy: keys.PrivateKeyPolicyNone,
}
gotID, err = tlsca.FromSubject(gotTLSCert.Subject, gotTLSCert.NotAfter)
require.NoError(t, err)
@@ -476,8 +472,7 @@ func TestAuthenticateSSHUser(t *testing.T) {
require.NoError(t, err)
require.Equal(t, wantID, *gotID)
// Login without specifying kube cluster. A registered one should be picked
// automatically.
// Login without specifying kube cluster. Kube cluster in the certificate should be empty.
resp, err = s.a.AuthenticateSSHUser(ctx, AuthenticateSSHRequest{
AuthenticateUserRequest: AuthenticateUserRequest{
Username: user,
@@ -495,16 +490,15 @@ func TestAuthenticateSSHUser(t *testing.T) {
gotTLSCert, err = tlsca.ParseCertificatePEM(resp.TLSCert)
require.NoError(t, err)
wantID = tlsca.Identity{
Username: user,
Groups: []string{role.GetName()},
Principals: []string{user, teleport.SSHSessionJoinPrincipal},
KubernetesUsers: []string{user},
KubernetesGroups: []string{"system:masters"},
KubernetesCluster: "root-kube-cluster",
Expires: gotTLSCert.NotAfter,
RouteToCluster: s.clusterName.GetClusterName(),
TeleportCluster: s.clusterName.GetClusterName(),
PrivateKeyPolicy: keys.PrivateKeyPolicyNone,
Username: user,
Groups: []string{role.GetName()},
Principals: []string{user, teleport.SSHSessionJoinPrincipal},
KubernetesUsers: []string{user},
KubernetesGroups: []string{"system:masters"},
Expires: gotTLSCert.NotAfter,
RouteToCluster: s.clusterName.GetClusterName(),
TeleportCluster: s.clusterName.GetClusterName(),
PrivateKeyPolicy: keys.PrivateKeyPolicyNone,
}
gotID, err = tlsca.FromSubject(gotTLSCert.Subject, gotTLSCert.NotAfter)
require.NoError(t, err)
+3 -19
View File
@@ -71,7 +71,6 @@ import (
"github.com/gravitational/teleport/lib/httplib/reverseproxy"
"github.com/gravitational/teleport/lib/kube/proxy/responsewriters"
"github.com/gravitational/teleport/lib/kube/proxy/streamproto"
kubeutils "github.com/gravitational/teleport/lib/kube/utils"
"github.com/gravitational/teleport/lib/multiplexer"
"github.com/gravitational/teleport/lib/reversetunnelclient"
"github.com/gravitational/teleport/lib/service/servicecfg"
@@ -739,29 +738,14 @@ func (f *Forwarder) setupContext(
return nil, trace.AccessDenied("access denied: remote user can not access remote cluster")
}
kubeCluster := identity.KubernetesCluster
// Only set a default kube cluster if the user is not accessing a specific cluster.
// The check for kubeCluster != "" is happens in the next code section.
if !isRemoteCluster && kubeCluster == "" {
kc, err := kubeutils.CheckOrSetKubeCluster(ctx, f.cfg.CachingAuthClient, identity.KubernetesCluster, teleportClusterName)
if err != nil {
if !trace.IsNotFound(err) {
return nil, trace.Wrap(err)
}
// Fallback for old clusters and old user certs. Assume that the
// user is trying to access the default cluster name.
kubeCluster = teleportClusterName
} else {
kubeCluster = kc
}
}
var (
kubeServers []types.KubeServer
kubeResource *types.KubernetesResource
apiResource apiResource
err error
)
kubeCluster := identity.KubernetesCluster
// Only check k8s principals for local clusters.
//
// For remote clusters, everything will be remapped to new roles on the
@@ -769,7 +753,7 @@ func (f *Forwarder) setupContext(
if !isRemoteCluster {
kubeServers, err = f.getKubernetesServersForKubeCluster(ctx, kubeCluster)
if err != nil || len(kubeServers) == 0 {
return nil, trace.NotFound("cluster %q not found", kubeCluster)
return nil, trace.NotFound("Kubernetes cluster %q not found", kubeCluster)
}
}
if f.isLocalKubeCluster(isRemoteCluster, kubeCluster) {
+50 -43
View File
@@ -167,12 +167,13 @@ func TestAuthenticate(t *testing.T) {
wantAuthErr bool
}{
{
desc: "local user and cluster with active access request",
user: authz.LocalUser{},
roleKubeGroups: []string{"kube-group-a", "kube-group-b"},
routeToCluster: "local",
haveKubeCreds: true,
tunnel: tun,
desc: "local user and cluster with active access request",
user: authz.LocalUser{},
roleKubeGroups: []string{"kube-group-a", "kube-group-b"},
routeToCluster: "local",
kubernetesCluster: "local",
haveKubeCreds: true,
tunnel: tun,
kubeServers: newKubeServersFromKubeClusters(
t,
&types.KubernetesClusterV3{
@@ -220,12 +221,13 @@ func TestAuthenticate(t *testing.T) {
},
},
{
desc: "local user and cluster",
user: authz.LocalUser{},
roleKubeGroups: []string{"kube-group-a", "kube-group-b"},
routeToCluster: "local",
haveKubeCreds: true,
tunnel: tun,
desc: "local user and cluster",
user: authz.LocalUser{},
roleKubeGroups: []string{"kube-group-a", "kube-group-b"},
routeToCluster: "local",
kubernetesCluster: "local",
haveKubeCreds: true,
tunnel: tun,
kubeServers: newKubeServersFromKubeClusters(
t,
&types.KubernetesClusterV3{
@@ -296,12 +298,13 @@ func TestAuthenticate(t *testing.T) {
},
},
{
desc: "local user and cluster, no kubeconfig",
user: authz.LocalUser{},
roleKubeGroups: []string{"kube-group-a", "kube-group-b"},
routeToCluster: "local",
haveKubeCreds: false,
tunnel: tun,
desc: "local user and cluster, no kubeconfig",
user: authz.LocalUser{},
roleKubeGroups: []string{"kube-group-a", "kube-group-b"},
routeToCluster: "local",
kubernetesCluster: "local",
haveKubeCreds: false,
tunnel: tun,
kubeServers: newKubeServersFromKubeClusters(
t,
&types.KubernetesClusterV3{
@@ -340,12 +343,13 @@ func TestAuthenticate(t *testing.T) {
},
},
{
desc: "remote user and local cluster",
user: authz.RemoteUser{},
roleKubeGroups: []string{"kube-group-a", "kube-group-b"},
routeToCluster: "local",
haveKubeCreds: true,
tunnel: tun,
desc: "remote user and local cluster",
user: authz.RemoteUser{},
roleKubeGroups: []string{"kube-group-a", "kube-group-b"},
routeToCluster: "local",
kubernetesCluster: "local",
haveKubeCreds: true,
tunnel: tun,
kubeServers: newKubeServersFromKubeClusters(
t,
&types.KubernetesClusterV3{
@@ -384,13 +388,14 @@ func TestAuthenticate(t *testing.T) {
},
},
{
desc: "remote user and local cluster with active request id",
user: authz.RemoteUser{},
roleKubeGroups: []string{"kube-group-a", "kube-group-b"},
routeToCluster: "local",
haveKubeCreds: true,
tunnel: tun,
activeRequests: activeAccessRequests,
desc: "remote user and local cluster with active request id",
user: authz.RemoteUser{},
roleKubeGroups: []string{"kube-group-a", "kube-group-b"},
routeToCluster: "local",
kubernetesCluster: "local",
haveKubeCreds: true,
tunnel: tun,
activeRequests: activeAccessRequests,
kubeServers: newKubeServersFromKubeClusters(
t,
&types.KubernetesClusterV3{
@@ -490,13 +495,14 @@ func TestAuthenticate(t *testing.T) {
wantAuthErr: true,
},
{
desc: "kube users passed in request",
user: authz.LocalUser{},
roleKubeUsers: []string{"kube-user-a", "kube-user-b"},
roleKubeGroups: []string{"kube-group-a", "kube-group-b"},
routeToCluster: "local",
haveKubeCreds: true,
tunnel: tun,
desc: "kube users passed in request",
user: authz.LocalUser{},
roleKubeUsers: []string{"kube-user-a", "kube-user-b"},
roleKubeGroups: []string{"kube-group-a", "kube-group-b"},
routeToCluster: "local",
kubernetesCluster: "local",
haveKubeCreds: true,
tunnel: tun,
kubeServers: newKubeServersFromKubeClusters(
t,
&types.KubernetesClusterV3{
@@ -552,11 +558,12 @@ func TestAuthenticate(t *testing.T) {
wantAuthErr: true,
},
{
desc: "local user and cluster, no tunnel",
user: authz.LocalUser{},
roleKubeGroups: []string{"kube-group-a", "kube-group-b"},
routeToCluster: "local",
haveKubeCreds: true,
desc: "local user and cluster, no tunnel",
user: authz.LocalUser{},
roleKubeGroups: []string{"kube-group-a", "kube-group-b"},
routeToCluster: "local",
kubernetesCluster: "local",
haveKubeCreds: true,
kubeServers: newKubeServersFromKubeClusters(
t,
&types.KubernetesClusterV3{
+10 -21
View File
@@ -217,29 +217,18 @@ func extractAndSortKubeClusters(kss []types.KubeServer) []types.KubeCluster {
return []types.KubeCluster(sorted)
}
// CheckOrSetKubeCluster validates kubeClusterName if it's set, or a sane
// default based on registered clusters.
//
// If no clusters are registered, a NotFound error is returned.
func CheckOrSetKubeCluster(ctx context.Context, p KubeServicesPresence, kubeClusterName, teleportClusterName string) (string, error) {
// CheckKubeCluster validates kubeClusterName is registered with this Teleport cluster.
func CheckKubeCluster(ctx context.Context, p KubeServicesPresence, kubeClusterName string) error {
if kubeClusterName == "" {
return trace.BadParameter("kube cluster name should not be empty.")
}
kubeClusterNames, err := KubeClusterNames(ctx, p)
if err != nil {
return "", trace.Wrap(err)
return trace.Wrap(err, "failed to get list of available Kubernetes clusters.")
}
if kubeClusterName != "" {
if !slices.Contains(kubeClusterNames, kubeClusterName) {
return "", trace.BadParameter("kubernetes cluster %q is not registered in this teleport cluster; you can list registered kubernetes clusters using 'tsh kube ls'", kubeClusterName)
}
return kubeClusterName, nil
if !slices.Contains(kubeClusterNames, kubeClusterName) {
return trace.BadParameter("Kubernetes cluster %q is not registered in this Teleport cluster; you can list registered Kubernetes clusters using 'tsh kube ls'", kubeClusterName)
}
// Default is the cluster with a name matching the Teleport cluster
// name (for backwards-compatibility with pre-5.0 behavior) or the
// first name alphabetically.
if len(kubeClusterNames) == 0 {
return "", trace.NotFound("no kubernetes clusters registered")
}
if slices.Contains(kubeClusterNames, teleportClusterName) {
return teleportClusterName, nil
}
return kubeClusterNames[0], nil
return nil
}
+16 -55
View File
@@ -27,91 +27,52 @@ import (
"github.com/gravitational/teleport/api/types"
)
func TestCheckOrSetKubeCluster(t *testing.T) {
func TestCheckKubeCluster(t *testing.T) {
t.Parallel()
ctx := context.TODO()
ctx := context.Background()
kubeServers := []types.KubeServer{
kubeServer(t, "k8s-1", "server1", "uuuid"),
kubeServer(t, "k8s-2", "server1", "uuuid"),
kubeServer(t, "k8s-3", "server1", "uuuid"),
kubeServer(t, "k8s-4", "server1", "uuuid"),
}
tests := []struct {
desc string
services []types.KubeServer
kubeCluster string
teleCluster string
want string
assertErr require.ErrorAssertionFunc
}{
{
desc: "valid cluster name",
services: []types.KubeServer{
kubeServer(t, "k8s-1", "server1", "uuuid"),
kubeServer(t, "k8s-2", "server1", "uuuid"),
kubeServer(t, "k8s-3", "server2", "uuuid2"),
kubeServer(t, "k8s-4", "server2", "uuuid2"),
},
desc: "valid cluster name",
services: kubeServers,
kubeCluster: "k8s-4",
teleCluster: "zzz-tele-cluster",
want: "k8s-4",
assertErr: require.NoError,
},
{
desc: "invalid cluster name",
services: []types.KubeServer{
kubeServer(t, "k8s-1", "server1", "uuuid"),
kubeServer(t, "k8s-2", "server1", "uuuid"),
kubeServer(t, "k8s-3", "server2", "uuuid2"),
kubeServer(t, "k8s-4", "server2", "uuuid2"),
},
desc: "invalid cluster name",
services: kubeServers,
kubeCluster: "k8s-5",
teleCluster: "zzz-tele-cluster",
assertErr: require.Error,
},
{
desc: "no registered clusters",
services: []types.KubeServer{},
kubeCluster: "k8s-1",
teleCluster: "zzz-tele-cluster",
assertErr: require.Error,
},
{
desc: "no registered clusters and empty cluster provided",
services: []types.KubeServer{},
desc: "empty cluster provided",
services: kubeServers,
kubeCluster: "",
teleCluster: "zzz-tele-cluster",
assertErr: require.Error,
},
{
desc: "no cluster provided, default to first alphabetically",
services: []types.KubeServer{
kubeServer(t, "k8s-1", "server1", "uuuid"),
kubeServer(t, "k8s-2", "server1", "uuuid"),
kubeServer(t, "k8s-3", "server2", "uuuid2"),
kubeServer(t, "k8s-4", "server2", "uuuid2"),
},
kubeCluster: "",
teleCluster: "zzz-tele-cluster",
want: "k8s-1",
assertErr: require.NoError,
},
{
desc: "no cluster provided, default to teleport cluster name",
services: []types.KubeServer{
kubeServer(t, "k8s-1", "server1", "uuuid"),
kubeServer(t, "k8s-2", "server1", "uuuid"),
kubeServer(t, "k8s-3", "server2", "uuuid2"),
kubeServer(t, "zzz-tele-cluster", "server2", "uuuid2"),
kubeServer(t, "k8s-4", "server2", "uuuid2"),
},
kubeCluster: "",
teleCluster: "zzz-tele-cluster",
want: "zzz-tele-cluster",
assertErr: require.NoError,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
got, err := CheckOrSetKubeCluster(ctx, mockKubeServicesPresence(tt.services), tt.kubeCluster, tt.teleCluster)
err := CheckKubeCluster(ctx, mockKubeServicesPresence(tt.services), tt.kubeCluster)
tt.assertErr(t, err)
require.Equal(t, tt.want, got)
})
}
}
+5 -2
View File
@@ -983,6 +983,9 @@ func (a *AuthCommand) checkLeafCluster(clusterAPI auth.ClientI) error {
}
func (a *AuthCommand) checkKubeCluster(ctx context.Context, clusterAPI auth.ClientI) error {
if a.kubeCluster == "" {
return nil
}
if a.outputFormat != identityfile.FormatKubernetes && a.kubeCluster != "" {
// User set --kube-cluster-name but it's not actually used for the chosen --format.
// Print a warning but continue.
@@ -1002,10 +1005,10 @@ func (a *AuthCommand) checkKubeCluster(ctx context.Context, clusterAPI auth.Clie
return nil
}
a.kubeCluster, err = kubeutils.CheckOrSetKubeCluster(ctx, clusterAPI, a.kubeCluster, a.leafCluster)
if err != nil && !trace.IsNotFound(err) {
if err := kubeutils.CheckKubeCluster(ctx, clusterAPI, a.kubeCluster); err != nil {
return trace.Wrap(err)
}
return nil
}
+3 -2
View File
@@ -513,7 +513,6 @@ func TestCheckKubeCluster(t *testing.T) {
leafCluster: teleportCluster,
registeredClusters: []*types.KubernetesClusterV3{{Metadata: types.Metadata{Name: "foo"}}},
outputFormat: identityfile.FormatKubernetes,
want: "foo",
assertErr: require.NoError,
},
{
@@ -573,7 +572,9 @@ func TestCheckKubeCluster(t *testing.T) {
}
err := a.checkKubeCluster(context.Background(), client)
tt.assertErr(t, err)
require.Equal(t, tt.want, a.kubeCluster)
if err == nil {
require.Equal(t, tt.want, a.kubeCluster)
}
})
}
}