istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/credentials/kube/secrets_test.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package kube
    16  
    17  import (
    18  	"fmt"
    19  	"testing"
    20  
    21  	authorizationv1 "k8s.io/api/authorization/v1"
    22  	corev1 "k8s.io/api/core/v1"
    23  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    24  	"k8s.io/apimachinery/pkg/runtime"
    25  	"k8s.io/client-go/kubernetes/fake"
    26  	k8stesting "k8s.io/client-go/testing"
    27  
    28  	cluster2 "istio.io/istio/pkg/cluster"
    29  	"istio.io/istio/pkg/kube"
    30  	"istio.io/istio/pkg/kube/multicluster"
    31  	"istio.io/istio/pkg/test"
    32  	"istio.io/istio/pkg/util/sets"
    33  )
    34  
    35  func makeSecret(name string, data map[string]string, secretType corev1.SecretType) *corev1.Secret {
    36  	bdata := map[string][]byte{}
    37  	for k, v := range data {
    38  		bdata[k] = []byte(v)
    39  	}
    40  	return &corev1.Secret{
    41  		ObjectMeta: metav1.ObjectMeta{
    42  			Name:      name,
    43  			Namespace: "default",
    44  		},
    45  		Data: bdata,
    46  		Type: secretType,
    47  	}
    48  }
    49  
    50  var (
    51  	genericCert = makeSecret("generic", map[string]string{
    52  		GenericScrtCert: "generic-cert", GenericScrtKey: "generic-key",
    53  	}, corev1.SecretTypeTLS)
    54  	genericMtlsCert = makeSecret("generic-mtls", map[string]string{
    55  		GenericScrtCert: "generic-mtls-cert", GenericScrtKey: "generic-mtls-key", GenericScrtCaCert: "generic-mtls-ca",
    56  	}, corev1.SecretTypeTLS)
    57  	genericMtlsCertSplit = makeSecret("generic-mtls-split", map[string]string{
    58  		GenericScrtCert: "generic-mtls-split-cert", GenericScrtKey: "generic-mtls-split-key",
    59  	}, corev1.SecretTypeTLS)
    60  	genericMtlsCertSplitCa = makeSecret("generic-mtls-split-cacert", map[string]string{
    61  		GenericScrtCaCert: "generic-mtls-split-ca",
    62  	}, corev1.SecretTypeTLS)
    63  	overlapping = makeSecret("overlap", map[string]string{
    64  		GenericScrtCert: "cert", GenericScrtKey: "key", GenericScrtCaCert: "main-ca",
    65  	}, corev1.SecretTypeTLS)
    66  	overlappingCa = makeSecret("overlap-cacert", map[string]string{
    67  		GenericScrtCaCert: "split-ca",
    68  	}, corev1.SecretTypeTLS)
    69  	tlsCert = makeSecret("tls", map[string]string{
    70  		TLSSecretCert: "tls-cert", TLSSecretKey: "tls-key", TLSSecretOcspStaple: "tls-ocsp-staple",
    71  	}, corev1.SecretTypeTLS)
    72  	tlsMtlsCert = makeSecret("tls-mtls", map[string]string{
    73  		TLSSecretCert: "tls-mtls-cert", TLSSecretKey: "tls-mtls-key", TLSSecretCaCert: "tls-mtls-ca",
    74  	}, corev1.SecretTypeTLS)
    75  	tlsMtlsCertWithCrl = makeSecret("tls-mtls-crl", map[string]string{
    76  		TLSSecretCert: "tls-mtls-cert", TLSSecretKey: "tls-mtls-key", TLSSecretCaCert: "tls-mtls-ca", TLSSecretCrl: "tls-mtls-crl",
    77  	}, corev1.SecretTypeTLS)
    78  	tlsMtlsCertSplit = makeSecret("tls-mtls-split", map[string]string{
    79  		TLSSecretCert: "tls-mtls-split-cert", TLSSecretKey: "tls-mtls-split-key",
    80  	}, corev1.SecretTypeTLS)
    81  	tlsMtlsCertSplitCa = makeSecret("tls-mtls-split-cacert", map[string]string{
    82  		TLSSecretCaCert: "tls-mtls-split-ca",
    83  	}, corev1.SecretTypeTLS)
    84  	tlsMtlsCertSplitCaWithCrl = makeSecret("tls-mtls-split-crl-cacert", map[string]string{
    85  		TLSSecretCaCert: "tls-mtls-split-ca", TLSSecretCrl: "tls-mtls-split-crl",
    86  	}, corev1.SecretTypeTLS)
    87  	tlsMtlsCertSplitWithCrl = makeSecret("tls-mtls-split-crl", map[string]string{
    88  		TLSSecretCert: "tls-mtls-split-crl-cert", TLSSecretKey: "tls-mtls-split-crl-key",
    89  	}, corev1.SecretTypeTLS)
    90  	emptyCert = makeSecret("empty-cert", map[string]string{
    91  		TLSSecretCert: "", TLSSecretKey: "tls-key",
    92  	}, corev1.SecretTypeTLS)
    93  	wrongKeys = makeSecret("wrong-keys", map[string]string{
    94  		"foo-bar": "my-cert", TLSSecretKey: "tls-key",
    95  	}, corev1.SecretTypeTLS)
    96  	dockerjson = makeSecret("docker-json", map[string]string{
    97  		corev1.DockerConfigJsonKey: "docker-cred",
    98  	}, corev1.SecretTypeDockerConfigJson)
    99  	badDockerjson = makeSecret("bad-docker-json", map[string]string{
   100  		"docker-key": "docker-cred",
   101  	}, corev1.SecretTypeDockerConfigJson)
   102  )
   103  
   104  func TestSecretsController(t *testing.T) {
   105  	secrets := []runtime.Object{
   106  		genericCert,
   107  		genericMtlsCert,
   108  		genericMtlsCertSplit,
   109  		genericMtlsCertSplitCa,
   110  		overlapping,
   111  		overlappingCa,
   112  		tlsCert,
   113  		tlsMtlsCert,
   114  		tlsMtlsCertWithCrl,
   115  		tlsMtlsCertSplit,
   116  		tlsMtlsCertSplitCa,
   117  		tlsMtlsCertSplitCaWithCrl,
   118  		tlsMtlsCertSplitWithCrl,
   119  		emptyCert,
   120  		wrongKeys,
   121  	}
   122  	client := kube.NewFakeClient(secrets...)
   123  	sc := NewCredentialsController(client, nil)
   124  	client.RunAndWait(test.NewStop(t))
   125  	cases := []struct {
   126  		name            string
   127  		namespace       string
   128  		cert            string
   129  		key             string
   130  		staple          string
   131  		caCert          string
   132  		caCrl           string
   133  		crl             string
   134  		expectedError   string
   135  		expectedCAError string
   136  	}{
   137  		{
   138  			name:            "generic",
   139  			namespace:       "default",
   140  			cert:            "generic-cert",
   141  			key:             "generic-key",
   142  			expectedCAError: "found secret, but didn't have expected keys cacert or ca.crt; found: cert, key",
   143  		},
   144  		{
   145  			name:      "generic-mtls",
   146  			namespace: "default",
   147  			cert:      "generic-mtls-cert",
   148  			key:       "generic-mtls-key",
   149  			caCert:    "generic-mtls-ca",
   150  		},
   151  		{
   152  			name:            "generic-mtls-split",
   153  			namespace:       "default",
   154  			cert:            "generic-mtls-split-cert",
   155  			key:             "generic-mtls-split-key",
   156  			expectedCAError: "found secret, but didn't have expected keys cacert or ca.crt; found: cert, key",
   157  		},
   158  		{
   159  			name:          "generic-mtls-split-cacert",
   160  			namespace:     "default",
   161  			caCert:        "generic-mtls-split-ca",
   162  			expectedError: "found secret, but didn't have expected keys (cert and key) or (tls.crt and tls.key); found: cacert",
   163  		},
   164  		// The -cacert secret has precedence
   165  		{
   166  			name:          "overlap-cacert",
   167  			namespace:     "default",
   168  			caCert:        "split-ca",
   169  			expectedError: "found secret, but didn't have expected keys (cert and key) or (tls.crt and tls.key); found: cacert",
   170  		},
   171  		{
   172  			name:            "tls",
   173  			namespace:       "default",
   174  			cert:            "tls-cert",
   175  			key:             "tls-key",
   176  			staple:          "tls-ocsp-staple",
   177  			expectedCAError: "found secret, but didn't have expected keys cacert or ca.crt; found: tls.crt, tls.key, tls.ocsp-staple, and 0 more...",
   178  		},
   179  		{
   180  			name:      "tls-mtls",
   181  			namespace: "default",
   182  			cert:      "tls-mtls-cert",
   183  			key:       "tls-mtls-key",
   184  			caCert:    "tls-mtls-ca",
   185  		},
   186  		{
   187  			name:      "tls-mtls-crl",
   188  			namespace: "default",
   189  			cert:      "tls-mtls-cert",
   190  			key:       "tls-mtls-key",
   191  			caCert:    "tls-mtls-ca",
   192  			crl:       "tls-mtls-crl",
   193  			caCrl:     "tls-mtls-crl",
   194  		},
   195  		{
   196  			name:            "tls-mtls-split",
   197  			namespace:       "default",
   198  			cert:            "tls-mtls-split-cert",
   199  			key:             "tls-mtls-split-key",
   200  			expectedCAError: "found secret, but didn't have expected keys cacert or ca.crt; found: tls.crt, tls.key",
   201  		},
   202  		{
   203  			name:          "tls-mtls-split-cacert",
   204  			namespace:     "default",
   205  			caCert:        "tls-mtls-split-ca",
   206  			expectedError: "found secret, but didn't have expected keys (cert and key) or (tls.crt and tls.key); found: ca.crt",
   207  		},
   208  		{
   209  			name:          "tls-mtls-split-crl-cacert",
   210  			namespace:     "default",
   211  			caCert:        "tls-mtls-split-ca",
   212  			expectedError: "found secret, but didn't have expected keys (cert and key) or (tls.crt and tls.key); found: ca.crl, ca.crt",
   213  			caCrl:         "tls-mtls-split-crl",
   214  		},
   215  		{
   216  			name:            "tls-mtls-split-crl",
   217  			namespace:       "default",
   218  			cert:            "tls-mtls-split-crl-cert",
   219  			key:             "tls-mtls-split-crl-key",
   220  			expectedCAError: "found secret, but didn't have expected keys cacert or ca.crt; found: tls.crt, tls.key",
   221  		},
   222  		{
   223  			name:            "generic",
   224  			namespace:       "wrong-namespace",
   225  			expectedError:   `secret wrong-namespace/generic not found`,
   226  			expectedCAError: `secret wrong-namespace/generic not found`,
   227  		},
   228  		{
   229  			name:            "empty-cert",
   230  			namespace:       "default",
   231  			expectedError:   `found keys "tls.crt" and "tls.key", but they were empty`,
   232  			expectedCAError: "found secret, but didn't have expected keys cacert or ca.crt; found: tls.crt, tls.key",
   233  		},
   234  		{
   235  			name:            "wrong-keys",
   236  			namespace:       "default",
   237  			expectedError:   `found secret, but didn't have expected keys (cert and key) or (tls.crt and tls.key); found: foo-bar, tls.key`,
   238  			expectedCAError: "found secret, but didn't have expected keys cacert or ca.crt; found: foo-bar, tls.key",
   239  		},
   240  	}
   241  	for _, tt := range cases {
   242  		t.Run(tt.name, func(t *testing.T) {
   243  			certInfo, err := sc.GetCertInfo(tt.name, tt.namespace)
   244  			var actualKey []byte
   245  			var actualCert []byte
   246  			var actualStaple []byte
   247  			var actualCrl []byte
   248  			if certInfo != nil {
   249  				actualKey = certInfo.Key
   250  				actualCert = certInfo.Cert
   251  				actualStaple = certInfo.Staple
   252  				actualCrl = certInfo.CRL
   253  			}
   254  			if tt.key != string(actualKey) {
   255  				t.Errorf("got key %q, wanted %q", string(actualKey), tt.key)
   256  			}
   257  			if tt.cert != string(actualCert) {
   258  				t.Errorf("got cert %q, wanted %q", string(actualCert), tt.cert)
   259  			}
   260  			if tt.staple != string(actualStaple) {
   261  				t.Errorf("got staple %q, wanted %q", string(actualStaple), tt.staple)
   262  			}
   263  			if tt.crl != string(actualCrl) {
   264  				t.Errorf("got crl %q, wanted %q", string(actualCrl), tt.crl)
   265  			}
   266  			if tt.expectedError != errString(err) {
   267  				t.Errorf("got err %q, wanted %q", errString(err), tt.expectedError)
   268  			}
   269  			caCertInfo, err := sc.GetCaCert(tt.name, tt.namespace)
   270  			if caCertInfo != nil && tt.caCert != string(caCertInfo.Cert) {
   271  				t.Errorf("got caCert %q, wanted %q", string(caCertInfo.Cert), tt.caCert)
   272  			}
   273  			if caCertInfo != nil && tt.caCrl != string(caCertInfo.CRL) {
   274  				t.Errorf("got caCrl %q, wanted %q", string(caCertInfo.CRL), tt.crl)
   275  			}
   276  			if tt.expectedCAError != errString(err) {
   277  				t.Errorf("got ca err %q, wanted %q", errString(err), tt.expectedCAError)
   278  			}
   279  		})
   280  	}
   281  }
   282  
   283  func TestDockerCredentials(t *testing.T) {
   284  	secrets := []runtime.Object{
   285  		dockerjson,
   286  		badDockerjson,
   287  		genericCert,
   288  	}
   289  	client := kube.NewFakeClient(secrets...)
   290  	sc := NewCredentialsController(client, nil)
   291  	client.RunAndWait(test.NewStop(t))
   292  	cases := []struct {
   293  		name                string
   294  		namespace           string
   295  		expectedType        corev1.SecretType
   296  		expectedDockerCred  string
   297  		expectedDockerError string
   298  	}{
   299  		{
   300  			name:               "docker-json",
   301  			namespace:          "default",
   302  			expectedType:       corev1.SecretTypeDockerConfigJson,
   303  			expectedDockerCred: "docker-cred",
   304  		},
   305  		{
   306  			name:                "bad-docker-json",
   307  			namespace:           "default",
   308  			expectedDockerError: "cannot find docker config at secret default/bad-docker-json",
   309  		},
   310  		{
   311  			name:                "wrong-name",
   312  			namespace:           "default",
   313  			expectedDockerError: "secret default/wrong-name not found",
   314  		},
   315  		{
   316  			name:                "generic",
   317  			namespace:           "default",
   318  			expectedDockerError: "type of secret default/generic is not kubernetes.io/dockerconfigjson",
   319  		},
   320  	}
   321  	for _, tt := range cases {
   322  		t.Run(tt.name, func(t *testing.T) {
   323  			dockerCred, err := sc.GetDockerCredential(tt.name, tt.namespace)
   324  			if tt.expectedDockerCred != "" && tt.expectedDockerCred != string(dockerCred) {
   325  				t.Errorf("got docker credential %q, want %q", string(dockerCred), tt.expectedDockerCred)
   326  			}
   327  			if tt.expectedDockerError != "" && tt.expectedDockerError != errString(err) {
   328  				t.Errorf("got docker err %q, wanted %q", errString(err), tt.expectedDockerError)
   329  			}
   330  		})
   331  	}
   332  }
   333  
   334  func errString(e error) string {
   335  	if e == nil {
   336  		return ""
   337  	}
   338  	return e.Error()
   339  }
   340  
   341  func allowIdentities(c kube.Client, identities ...string) {
   342  	allowed := sets.New(identities...)
   343  	c.Kube().(*fake.Clientset).Fake.PrependReactor("create", "subjectaccessreviews", func(action k8stesting.Action) (bool, runtime.Object, error) {
   344  		a := action.(k8stesting.CreateAction).GetObject().(*authorizationv1.SubjectAccessReview)
   345  		if allowed.Contains(a.Spec.User) {
   346  			return true, &authorizationv1.SubjectAccessReview{
   347  				Status: authorizationv1.SubjectAccessReviewStatus{
   348  					Allowed: true,
   349  				},
   350  			}, nil
   351  		}
   352  		return true, &authorizationv1.SubjectAccessReview{
   353  			Status: authorizationv1.SubjectAccessReviewStatus{
   354  				Allowed: false,
   355  				Reason:  fmt.Sprintf("user %s cannot access secrets", a.Spec.User),
   356  			},
   357  		}, nil
   358  	})
   359  }
   360  
   361  func TestForCluster(t *testing.T) {
   362  	stop := test.NewStop(t)
   363  	localClient := kube.NewFakeClient()
   364  	localClient.RunAndWait(stop)
   365  	remoteClient := kube.NewFakeClient()
   366  	remoteClient.RunAndWait(stop)
   367  	mc := multicluster.NewFakeController()
   368  	sc := NewMulticluster("local", mc)
   369  	mc.Add("local", localClient, stop)
   370  	mc.Add("remote", remoteClient, stop)
   371  	mc.Add("remote2", remoteClient, stop)
   372  	cases := []struct {
   373  		cluster cluster2.ID
   374  		allowed bool
   375  	}{
   376  		{"local", true},
   377  		{"remote", true},
   378  		{"remote2", true},
   379  		{"invalid", false},
   380  	}
   381  	for _, tt := range cases {
   382  		t.Run(string(tt.cluster), func(t *testing.T) {
   383  			_, err := sc.ForCluster(tt.cluster)
   384  			if (err == nil) != tt.allowed {
   385  				t.Fatalf("expected allowed=%v, got err=%v", tt.allowed, err)
   386  			}
   387  		})
   388  	}
   389  }
   390  
   391  func TestAuthorize(t *testing.T) {
   392  	stop := test.NewStop(t)
   393  	localClient := kube.NewFakeClient()
   394  	localClient.RunAndWait(stop)
   395  	remoteClient := kube.NewFakeClient()
   396  	remoteClient.RunAndWait(stop)
   397  	allowIdentities(localClient, "system:serviceaccount:ns-local:sa-allowed")
   398  	allowIdentities(remoteClient, "system:serviceaccount:ns-remote:sa-allowed")
   399  	mc := multicluster.NewFakeController()
   400  	sc := NewMulticluster("local", mc)
   401  	mc.Add("local", localClient, stop)
   402  	mc.Add("remote", remoteClient, stop)
   403  	cases := []struct {
   404  		sa      string
   405  		ns      string
   406  		cluster cluster2.ID
   407  		allowed bool
   408  	}{
   409  		{"sa-denied", "ns-local", "local", false},
   410  		{"sa-allowed", "ns-local", "local", true},
   411  		{"sa-denied", "ns-local", "remote", false},
   412  		{"sa-allowed", "ns-local", "remote", false},
   413  		{"sa-denied", "ns-remote", "local", false},
   414  		{"sa-allowed", "ns-remote", "local", false},
   415  		{"sa-denied", "ns-remote", "remote", false},
   416  		{"sa-allowed", "ns-remote", "remote", true},
   417  	}
   418  	for _, tt := range cases {
   419  		t.Run(fmt.Sprintf("%v/%v/%v", tt.sa, tt.ns, tt.cluster), func(t *testing.T) {
   420  			con, err := sc.ForCluster(tt.cluster)
   421  			if err != nil {
   422  				t.Fatal(err)
   423  			}
   424  			got := con.Authorize(tt.sa, tt.ns)
   425  			if (got == nil) != tt.allowed {
   426  				t.Fatalf("expected allowed=%v, got error=%v", tt.allowed, got)
   427  			}
   428  		})
   429  	}
   430  }
   431  
   432  func TestSecretsControllerMulticluster(t *testing.T) {
   433  	stop := make(chan struct{})
   434  	defer close(stop)
   435  	secretsLocal := []runtime.Object{
   436  		tlsCert,
   437  		tlsMtlsCert,
   438  		tlsMtlsCertSplit,
   439  		tlsMtlsCertSplitCa,
   440  	}
   441  	tlsCertModified := makeSecret("tls", map[string]string{
   442  		TLSSecretCert: "tls-cert-mod", TLSSecretKey: "tls-key",
   443  	}, corev1.SecretTypeTLS)
   444  	secretsRemote := []runtime.Object{
   445  		tlsCertModified,
   446  		genericCert,
   447  		genericMtlsCert,
   448  		genericMtlsCertSplit,
   449  		genericMtlsCertSplitCa,
   450  	}
   451  
   452  	localClient := kube.NewFakeClient(secretsLocal...)
   453  	remoteClient := kube.NewFakeClient(secretsRemote...)
   454  	otherRemoteClient := kube.NewFakeClient()
   455  	mc := multicluster.NewFakeController()
   456  	sc := NewMulticluster("local", mc)
   457  	mc.Add("local", localClient, stop)
   458  	mc.Add("remote", remoteClient, stop)
   459  	mc.Add("other", otherRemoteClient, stop)
   460  
   461  	// normally the remote secrets controller would start these
   462  	localClient.RunAndWait(stop)
   463  	remoteClient.RunAndWait(stop)
   464  	otherRemoteClient.RunAndWait(stop)
   465  
   466  	cases := []struct {
   467  		name      string
   468  		namespace string
   469  		cluster   cluster2.ID
   470  		cert      string
   471  		key       string
   472  		caCert    string
   473  	}{
   474  		// From local cluster
   475  		// These are only in remote cluster, we do not have access
   476  		{"generic", "default", "local", "", "", ""},
   477  		{"generic-mtls", "default", "local", "", "", ""},
   478  		{"generic-mtls-split", "default", "local", "", "", ""},
   479  		{"generic-mtls-split-cacert", "default", "local", "", "", ""},
   480  		// These are in local cluster, we can access
   481  		{"tls", "default", "local", "tls-cert", "tls-key", ""},
   482  		{"tls-mtls", "default", "local", "tls-mtls-cert", "tls-mtls-key", "tls-mtls-ca"},
   483  		{"tls-mtls-split", "default", "local", "tls-mtls-split-cert", "tls-mtls-split-key", ""},
   484  		{"tls-mtls-split-cacert", "default", "local", "", "", "tls-mtls-split-ca"},
   485  		{"generic", "wrong-namespace", "local", "", "", ""},
   486  
   487  		// From remote cluster
   488  		// We can access all credentials - local and remote
   489  		{"generic", "default", "remote", "generic-cert", "generic-key", ""},
   490  		{"generic-mtls", "default", "remote", "generic-mtls-cert", "generic-mtls-key", "generic-mtls-ca"},
   491  		{"generic-mtls-split", "default", "remote", "generic-mtls-split-cert", "generic-mtls-split-key", ""},
   492  		{"generic-mtls-split-cacert", "default", "remote", "", "", "generic-mtls-split-ca"},
   493  		// This is present in local and remote, but with a different value. We have the remote.
   494  		{"tls", "default", "remote", "tls-cert-mod", "tls-key", ""},
   495  		{"tls-mtls", "default", "remote", "tls-mtls-cert", "tls-mtls-key", "tls-mtls-ca"},
   496  		{"tls-mtls-split", "default", "remote", "tls-mtls-split-cert", "tls-mtls-split-key", ""},
   497  		{"tls-mtls-split-cacert", "default", "remote", "", "", "tls-mtls-split-ca"},
   498  		{"generic", "wrong-namespace", "remote", "", "", ""},
   499  
   500  		// From other remote cluster
   501  		// We have no in cluster credentials; can only access those in config cluster
   502  		{"generic", "default", "other", "", "", ""},
   503  		{"generic-mtls", "default", "other", "", "", ""},
   504  		{"generic-mtls-split", "default", "other", "", "", ""},
   505  		{"generic-mtls-split-cacert", "default", "other", "", "", ""},
   506  		{"tls", "default", "other", "tls-cert", "tls-key", ""},
   507  		{"tls-mtls", "default", "other", "tls-mtls-cert", "tls-mtls-key", "tls-mtls-ca"},
   508  		{"tls-mtls-split", "default", "other", "tls-mtls-split-cert", "tls-mtls-split-key", ""},
   509  		{"tls-mtls-split-cacert", "default", "other", "", "", "tls-mtls-split-ca"},
   510  		{"generic", "wrong-namespace", "other", "", "", ""},
   511  	}
   512  	for _, tt := range cases {
   513  		t.Run(fmt.Sprintf("%s-%v", tt.name, tt.cluster), func(t *testing.T) {
   514  			con, err := sc.ForCluster(tt.cluster)
   515  			if err != nil {
   516  				t.Fatal(err)
   517  			}
   518  			certInfo, _ := con.GetCertInfo(tt.name, tt.namespace)
   519  			var actualKey []byte
   520  			var actualCert []byte
   521  			if certInfo != nil {
   522  				actualKey = certInfo.Key
   523  				actualCert = certInfo.Cert
   524  			}
   525  			if tt.key != string(actualKey) {
   526  				t.Errorf("got key %q, wanted %q", string(actualKey), tt.key)
   527  			}
   528  			if tt.cert != string(actualCert) {
   529  				t.Errorf("got cert %q, wanted %q", string(actualCert), tt.cert)
   530  			}
   531  			caCertInfo, err := con.GetCaCert(tt.name, tt.namespace)
   532  			if caCertInfo != nil && tt.caCert != string(caCertInfo.Cert) {
   533  				t.Errorf("got caCert %q, wanted %q with err %v", string(caCertInfo.Cert), tt.caCert, err)
   534  			}
   535  		})
   536  	}
   537  }