Skip to content

Commit 2f2d4a9

Browse files
committed
Add remote.ClusterClient to access remote workload clusters
Signed-off-by: Vince Prignano <[email protected]>
1 parent d48025d commit 2f2d4a9

File tree

6 files changed

+182
-5
lines changed

6 files changed

+182
-5
lines changed

pkg/controller/BUILD.bazel

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ go_library(
66
"add_machinedeployment.go",
77
"add_machineset.go",
88
"add_node.go",
9+
"add_noderef.go",
910
"controller.go",
1011
],
1112
importpath = "sigs.k8s.io/cluster-api/pkg/controller",
@@ -14,6 +15,7 @@ go_library(
1415
"//pkg/controller/machinedeployment:go_default_library",
1516
"//pkg/controller/machineset:go_default_library",
1617
"//pkg/controller/node:go_default_library",
18+
"//vendor/sigs.k8s.io/cluster-api/pkg/controller/noderef:go_default_library",
1719
"//vendor/sigs.k8s.io/controller-runtime/pkg/manager:go_default_library",
1820
],
1921
)

pkg/controller/remote/BUILD.bazel

+15-2
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,35 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
22

33
go_library(
44
name = "go_default_library",
5-
srcs = ["util.go"],
5+
srcs = [
6+
"cluster.go",
7+
"util.go",
8+
],
69
importpath = "sigs.k8s.io/cluster-api/pkg/controller/remote",
710
visibility = ["//visibility:public"],
811
deps = [
12+
"//pkg/apis/cluster/v1alpha1:go_default_library",
913
"//vendor/github.com/pkg/errors:go_default_library",
1014
"//vendor/k8s.io/api/core/v1:go_default_library",
15+
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
16+
"//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
17+
"//vendor/k8s.io/client-go/rest:go_default_library",
18+
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
1119
"//vendor/sigs.k8s.io/controller-runtime/pkg/client:go_default_library",
1220
],
1321
)
1422

1523
go_test(
1624
name = "go_default_test",
17-
srcs = ["util_test.go"],
25+
srcs = [
26+
"cluster_test.go",
27+
"util_test.go",
28+
],
1829
embed = [":go_default_library"],
1930
deps = [
31+
"//pkg/apis/cluster/v1alpha1:go_default_library",
2032
"//vendor/k8s.io/api/core/v1:go_default_library",
33+
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
2134
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
2235
"//vendor/sigs.k8s.io/controller-runtime/pkg/client/fake:go_default_library",
2336
],

pkg/controller/remote/cluster.go

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package remote
18+
19+
import (
20+
"github.com/pkg/errors"
21+
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
22+
restclient "k8s.io/client-go/rest"
23+
"k8s.io/client-go/tools/clientcmd"
24+
"sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1"
25+
"sigs.k8s.io/controller-runtime/pkg/client"
26+
)
27+
28+
// ClusterClient is a helper struct to connect to remote workload clusters.
29+
type ClusterClient struct {
30+
restConfig *restclient.Config
31+
cluster *v1alpha1.Cluster
32+
}
33+
34+
// NewClusterClient creates a new ClusterClient instance.
35+
func NewClusterClient(c client.Client, cluster *v1alpha1.Cluster) (*ClusterClient, error) {
36+
secret, err := GetKubeConfigSecret(c, cluster.Name, cluster.Namespace)
37+
if err != nil {
38+
return nil, errors.Wrapf(err, "failed to retrieve kubeconfig secret for Cluster %q in namespace %q",
39+
cluster.Name, cluster.Namespace)
40+
}
41+
42+
kubeconfig, err := DecodeKubeConfigSecret(secret)
43+
if err != nil {
44+
return nil, errors.Wrapf(err, "failed to decode kubeconfig secret for Cluster %q in namespace %q",
45+
cluster.Name, cluster.Namespace)
46+
}
47+
48+
restConfig, err := clientcmd.RESTConfigFromKubeConfig(kubeconfig)
49+
if err != nil {
50+
return nil, errors.Wrapf(err, "failed to create client configuration for Cluster %q in namespace %q",
51+
cluster.Name, cluster.Namespace)
52+
}
53+
54+
return &ClusterClient{
55+
restConfig: restConfig,
56+
cluster: cluster,
57+
}, nil
58+
}
59+
60+
// RESTConfig returns a configuration instance to be used with a Kubernetes client.
61+
func (c *ClusterClient) RESTConfig() *restclient.Config {
62+
return c.restConfig
63+
}
64+
65+
// CoreV1 returns a new Kubernetes CoreV1 client.
66+
func (c *ClusterClient) CoreV1() (corev1.CoreV1Interface, error) {
67+
return corev1.NewForConfig(c.RESTConfig())
68+
}

pkg/controller/remote/cluster_test.go

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
Copyright 2019 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package remote
18+
19+
import (
20+
"testing"
21+
22+
apierrors "k8s.io/apimachinery/pkg/api/errors"
23+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24+
"sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1"
25+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
26+
)
27+
28+
var (
29+
clusterWithValidKubeConfig = &v1alpha1.Cluster{
30+
ObjectMeta: metav1.ObjectMeta{
31+
Name: "test1",
32+
Namespace: "test",
33+
},
34+
}
35+
36+
clusterWithInvalidKubeConfig = &v1alpha1.Cluster{
37+
ObjectMeta: metav1.ObjectMeta{
38+
Name: "test2",
39+
Namespace: "test",
40+
},
41+
}
42+
43+
clusterWithNoKubeConfig = &v1alpha1.Cluster{
44+
ObjectMeta: metav1.ObjectMeta{
45+
Name: "test3",
46+
Namespace: "test",
47+
},
48+
}
49+
)
50+
51+
func TestNewClusterClient(t *testing.T) {
52+
t.Run("cluster with valid kubeconfig", func(t *testing.T) {
53+
client := fake.NewFakeClient(validSecret)
54+
c, err := NewClusterClient(client, clusterWithValidKubeConfig)
55+
if err != nil {
56+
t.Fatalf("Expected no errors, got %v", err)
57+
}
58+
59+
if c == nil {
60+
t.Fatal("Expected actual client, got nil")
61+
}
62+
63+
restConfig := c.RESTConfig()
64+
if restConfig.Host != "https://test-cluster-api:6443" {
65+
t.Fatalf("Unexpected Host value in RESTConfig: %q", restConfig.Host)
66+
}
67+
})
68+
69+
t.Run("cluster with no kubeconfig", func(t *testing.T) {
70+
client := fake.NewFakeClient()
71+
_, err := NewClusterClient(client, clusterWithNoKubeConfig)
72+
if err == nil || !apierrors.IsNotFound(err) {
73+
t.Fatalf("Expected not found error, got %v", err)
74+
}
75+
})
76+
77+
t.Run("cluster with invalid kubeconfig", func(t *testing.T) {
78+
client := fake.NewFakeClient(invalidSecret)
79+
_, err := NewClusterClient(client, clusterWithInvalidKubeConfig)
80+
if err == nil || apierrors.IsNotFound(err) {
81+
t.Fatalf("Expected error, got %v", err)
82+
}
83+
})
84+
85+
}

pkg/controller/remote/util.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,19 @@ import (
2323

2424
"github.com/pkg/errors"
2525
corev1 "k8s.io/api/core/v1"
26+
apierrors "k8s.io/apimachinery/pkg/api/errors"
2627
"sigs.k8s.io/controller-runtime/pkg/client"
2728
)
2829

2930
const (
3031
kubeconfigSecretKey = "value"
3132
)
3233

34+
var (
35+
ErrSecretNotFound = errors.New("secret not found")
36+
ErrSecretMissingValue = errors.New("missing value in secret")
37+
)
38+
3339
// KubeConfigSecretName generates the expected name for the Kubeconfig secret
3440
// to access a remote cluster given the cluster's name.
3541
func KubeConfigSecretName(cluster string) string {
@@ -46,6 +52,9 @@ func GetKubeConfigSecret(c client.Client, cluster, namespace string) (*corev1.Se
4652
}
4753

4854
if err := c.Get(context.TODO(), secretKey, secret); err != nil {
55+
if apierrors.IsNotFound(err) {
56+
return nil, ErrSecretNotFound
57+
}
4958
return nil, err
5059
}
5160

@@ -56,12 +65,12 @@ func GetKubeConfigSecret(c client.Client, cluster, namespace string) (*corev1.Se
5665
func DecodeKubeConfigSecret(secret *corev1.Secret) ([]byte, error) {
5766
encodedKubeconfig, ok := secret.Data[kubeconfigSecretKey]
5867
if !ok {
59-
return nil, errors.Errorf("missing value in secret %s/%s", secret.Namespace, secret.Name)
68+
return nil, ErrSecretMissingValue
6069
}
6170

6271
kubeconfig, err := base64.StdEncoding.DecodeString(string(encodedKubeconfig))
6372
if err != nil {
64-
return nil, errors.Wrapf(err, "cannot decode kubeconfig secret %s/%s", secret.Namespace, secret.Name)
73+
return nil, err
6574
}
6675

6776
return kubeconfig, nil

pkg/controller/remote/util_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ users:
4545
- name: kubernetes-admin
4646
user:
4747
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM4akNDQWRxZ0F3SUJBZ0lJUTJFS3c0cU0wbFV3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB4T1RBeE1UQXhPREF3TkRCYUZ3MHlNREF4TVRBeE9EQXdOREphTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXUxeWNHcVdLMlZBUGpmWlYKeUdHWmhvQWdxZ1oreVFqU0pYQlVwNkR4VkZyYStENHJDVkNpRDNhWTVmTWNXaVpYVy9uanJsNFRudWJydndhWgpTN0hhMUxELzFZdmhFUFhLcnlBMzVNMStsN0JkUjA3T3NlRnFqNXNJQk9xWDNoNEJmckQ0SFQ5VGxSS082TXgxClMycSt5NzVaYjI5eXN0UTk3SGk4ZXVBS0sxN0JuSmJ5Zk80NlMvOFVxc2tlb0JXT3VnRkJHMlQrTFd6RXluK1oKVjdUUHZxdDE0MG1lQU40TStZUy91dFp2VmE0WFFmKy80czB4TjVwMGw4M0RrYnVWbnErcjR5dzBiSHM4cHdWdwo0Z3RuTVhrbjFhcGUwOGN4YUtBMGpodnZ4cFgyVnhHbEsxUTR0aDk1S2JNQWlpMlVINFcyNE1GSnlxOHloOUljCk54UldZd0lEQVFBQm95Y3dKVEFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFCbDUwSmNVblhGRHB4TWtkL2JOdlRHMkxVTnVWTVNnQmlCNQpmbFlpWTJibUQ3NVMreEE0eEwrZEYxanlJZHRDTGVtZURoS2MwRWtyVUpyYk5XSTZnVDB0OFAwWVNuYmxiY09ICmxBRHdmMjhoVW5TYTdIR1NOaWNBdDdKOGpnd296ZzFDVnNWbE9YM2cvcWdmSkdYeld0QzJMRFVvdjR3MFVNMVgKQ2pLNFdtMk8xTDFybEpzaHE1VysxalZzTEllYnZIcjlYb0cxRlcyY0ovcHJTK0dFS2dtNWc4YjZ1MWdJQXVFNAozOHNVQTltU3ZBYlgwR1RWdXI3Um9taWhSR2QwTktZK0k1S3A2bWtIRnpLVEVRbks2YXcrQWMvVFJObmFwUEh6Ci9IMXA0eGkyWlFHYi84MURhQjVTZDRwTURzK09FK1BNNitudkN4amN2eFdFbEdTdjE2Yz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
48-
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBdTF5Y0dxV0syVkFQamZaVnlHR1pob0FncWdaK3lRalNKWEJVcDZEeFZGcmErRDRyCkNWQ2lEM2FZNWZNY1dpWlhXL25qcmw0VG51YnJ2d2FaUzdIYTFMRC8xWXZoRVBYS3J5QTM1TTErbDdCZFIwN08Kc2VGcWo1c0lCT3FYM2g0QmZyRDRIVDlUbFJLTzZNeDFTMnEreTc1WmIyOXlzdFE5N0hpOGV1QUtLMTdCbkpieQpmTzQ2Uy84VXFza2VvQldPdWdGQkcyVCtMV3pFeW4rWlY3VFB2cXQxNDBtZUFONE0rWVMvdXRadlZhNFhRZisvCjRzMHhONXAwbDgzRGtidVZucStyNHl3MGJIczhwd1Z3NGd0bk1Ya24xYXBlMDhjeGFLQTBqaHZ2eHBYMlZ4R2wKSzFRNHRoOTVLYk1BaWkyVUg0VzI0TUZKeXE4eWg5SWNOeFJXWXdJREFRQUJBb0lCQVFDc0JLamw1aHNHemZTWgorQkptT1FXRmNWbU1BUTZpY0ZEUVFzUFdhM05tYVV3bElwN01uSlZOOFNzTDVCcWh3aFh1d2cwQjZDbkhlR2YxCktJL1I2V2JxWTk5ZkpsL3EvRitzVGI1RGVVL0M0UStqQ24zRzN4akE1Q3VHcUFQcTBFMjdEYXVlM3FkVWRJZDAKd1ZMbmZRZlRjOTRVNjVPNUVCZ1NaZjlXS1IvdEZDNHpGSlVselhHTlYxT2hOTWVyeXovbllmSVRZZGppUWNiRwplcDJucHk1cHZ5dEFPY1RiV0xXUEw4T2RKTDMvTER3b0h2aHlSa3huZXhWRTc0K3ZGd2lYbkRkNEp6ODVJVzBvCkFyeGEyRlJzOGZyWXFreHNSQ1VGYmRXNUpMTzhRVFBWYnl3S1c3Z0Z4S0c1U1c4Y004cmJLTHEzT01JOXBXVkoKTzNscVQxc1JBb0dCQU50QUxzR2VodzZsZWN3Zi9VU0RFN0FWNFQrdDJsWit6a3J1d3dodloyWXJYZHpXbGVqTAprNGpZWjhkQUNUUVdDRkJlaWtmYi9XdzZtMFF3ZUZudzExdVd1WndWRVJPS3BnRDFTa0krcVRtdGd0V2J2Y2lBClg4U0t4SU5qTGNzTzRLZUoxdEdkaVVDVEg3MW8zV0pBOXYzR3NaTlkrdW1WTVhnaGQ2d2YrTnB0QW9HQkFOckUKR3djOWNLVGVtZWZWSTcraFVtS2YvNm9SQ2NYdWxIK3gwSEZwNVBIQzl3NEhTMVp0Zk9NS3F6QzlJMWt6a200RwpjYW11WHovRy9iQXg4WGdaa3lURnRxTk5hdjE3Y0UzV25GRlMxejRHeGRQNDMvSkdLVWJrUzhkM1dZc0pkZnRYCkt5Vm45anl3Yjc0VG5hSnFIVlBSWFJRSkNFR3E2VlR4RVVGNlIzSVBBb0dBSmFTYlluckpUV1p6eHV3bkc4QTEKZlNJRWpsNVhBa3E3T0hwTjJnRG1pOUFlU1hBK1JMM1BFc3UwNWF6RTU4QndwUHZXV2dnWE5xSEpUcWZUd2Yxcgp2RG5nbkQreHN0MDNLeXJ5R1BXUk1HbnQ4S2JRcXIvL3NVcngrbXpveTlnK0VnWEVjRERRQTlvK3ROSndVQkkvClZjcnJhaFQ0MzJuU0dJSUdmZkx2VXZFQ2dZQmtNRGVvb3l5NWRQRExTY09yZVhnL2pzTUo0ZSsxNUVQQ0QyOUUKNFpobVdFSEkvUEkxek1MTFFCR1NxcXhMcCtEQjN0V2pQaWFGRU44U0dHMWI4V3FBQnNSVUdacU1LRUlRZzk3bgpKNmRIMHRZNjg5bXNIUkcrVThPWXdFSVQrT3M5aG5oT0UwU2tHckd5UFUyT0drY0FJZndjdHQ0L0pNVGpqOXUxClB3a0ZaUUtCZ1FDTWppdkpGL3crQXlUZUo0K1piWWpvZ091QkJFRE9oeXdiRnN5NC9ubVduTm4rQXRYQklDaGkKR2J6LzFuWkZTOGc2Nlh2cGFTWEQ5blVpS1BOUW5ORzUralJmclluTlI4WERCL3ZiNk9TMVFHbXBvWWxJZ2Q3UgpjTVpSRm1sbTUvbkJMWkdoVVpjOXZVN1pRVis4RXdLK2lHaGNrTFduVGhHNURZTkRWaksxcFE9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
48+
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBdTF5Y0dxV0syVkFQamZaVnlHR1pob0FncWdaK3lRalNKWEJVcDZEeFZGcmErRDRyCkNWQ2lEM2FZNWZNY1dpWlhXL25qcmw0VG51YnJ2d2FaUzdIYTFMRC8xWXZoRVBYS3J5QTM1TTErbDdCZFIwN08Kc2VGcWo1c0lCT3FYM2g0QmZyRDRIVDlUbFJLTzZNeDFTMnEreTc1WmIyOXlzdFE5N0hpOGV1QUtLMTdCbkpieQpmTzQ2Uy84VXFza2VvQldPdWdGQkcyVCtMV3pFeW4rWlY3VFB2cXQxNDBtZUFONE0rWVMvdXRadlZhNFhRZisvCjRzMHhONXAwbDgzRGtidVZucStyNHl3MGJIczhwd1Z3NGd0bk1Ya24xYXBlMDhjeGFLQTBqaHZ2eHBYMlZ4R2wKSzFRNHRoOTVLYk1BaWkyVUg0VzI0TUZKeXE4eWg5SWNOeFJXWXdJREFRQUJBb0lCQVFDc0JLamw1aHNHemZTWgorQkptT1FXRmNWbU1BUTZpY0ZEUVFzUFdhM05tYVV3bElwN01uSlZOOFNzTDVCcWh3aFh1d2cwQjZDbkhlR2YxCktJL1I2V2JxWTk5ZkpsL3EvRitzVGI1RGVVL0M0UStqQ24zRzN4akE1Q3VHcUFQcTBFMjdEYXVlM3FkVWRJZDAKd1ZMbmZRZlRjOTRVNjVPNUVCZ1NaZjlXS1IvdEZDNHpGSlVselhHTlYxT2hOTWVyeXovbllmSVRZZGppUWNiRwplcDJucHk1cHZ5dEFPY1RiV0xXUEw4T2RKTDMvTER3b0h2aHlSa3huZXhWRTc0K3ZGd2lYbkRkNEp6ODVJVzBvCkFyeGEyRlJzOGZyWXFreHNSQ1VGYmRXNUpMTzhRVFBWYnl3S1c3Z0Z4S0c1U1c4Y004cmJLTHEzT01JOXBXVkoKTzNscVQxc1JBb0dCQU50QUxzR2VodzZsZWN3Zi9VU0RFN0FWNFQrdDJsWit6a3J1d3dodloyWXJYZHpXbGVqTAprNGpZWjhkQUNUUVdDRkJlaWtmYi9XdzZtMFF3ZUZudzExdVd1WndWRVJPS3BnRDFTa0krcVRtdGd0V2J2Y2lBClg4U0t4SU5qTGNzTzRLZUoxdEdkaVVDVEg3MW8zV0pBOXYzR3NaTlkrdW1WTVhnaGQ2d2YrTnB0QW9HQkFOckUKR3djOWNLVGVtZWZWSTcraFVtS2YvNm9SQ2NYdWxIK3gwSEZwNVBIQzl3NEhTMVp0Zk9NS3F6QzlJMWt6a200RwpjYW11WHovRy9iQXg4WGdaa3lURnRxTk5hdjE3Y0UzV25GRlMxejRHeGRQNDMvSkdLVWJrUzhkM1dZc0pkZnRYCkt5Vm45anl3Yjc0VG5hSnFIVlBSWFJRSkNFR3E2VlR4RVVGNlIzSVBBb0dBSmFTYlluckpUV1p6eHV3bkc4QTEKZlNJRWpsNVhBa3E3T0hwTjJnRG1pOUFlU1hBK1JMM1BFc3UwNWF6RTU4QndwUHZXV2dnWE5xSEpUcWZUd2Yxcgp2RG5nbkQreHN0MDNLeXJ5R1BXUk1HbnQ4S2JRcXIvL3NVcngrbXpveTlnK0VnWEVjRERRQTlvK3ROSndVQkkvClZjcnJhaFQ0MzJuU0dJSUdmZkx2VXZFQ2dZQmtNRGVvb3l5NWRQRExTY09yZVhnL2pzTUo0ZSsxNUVQQ0QyOUUKNFpobVdFSEkvUEkxek1MTFFCR1NxcXhMcCtEQjN0V2pQaWFGRU44U0dHMWI4V3FBQnNSVUdacU1LRUlRZzk3bgpKNmRIMHRZNjg5bXNIUkcrVThPWXdFSVQrT3M5aG5oT0UwU2tHckd5UFUyT0drY0FJZndjdHQ0L0pNVGpqOXUxClB3a0ZaUUtCZ1FDTWppdkpGL3crQXlUZUo0K1piWWpvZ091QkJFRE9oeXdiRnN5NC9ubVduTm4rQXRYQklDaGkKR2J6LzFuWkZTOGc2Nlh2cGFTWEQ5blVpS1BOUW5ORzUralJmclluTlI4WERCL3ZiNk9TMVFHbXBvWWxJZ2Q3UgpjTVpSRm1sbTUvbkJMWkdoVVpjOXZVN1pRVis4RXdLK2lHaGNrTFduVGhHNURZTkRWaksxcFE9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
4949
`
5050

5151
validSecret = &corev1.Secret{

0 commit comments

Comments
 (0)