Skip to content

Commit baa4118

Browse files
authored
Merge branch 'kubernetes-sigs:main' into main
2 parents 7614f43 + 0570dad commit baa4118

File tree

68 files changed

+5679
-426
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+5679
-426
lines changed

backend/cmd/headlamp.go

Lines changed: 70 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1615,7 +1615,7 @@ func (c *HeadlampConfig) deleteCluster(w http.ResponseWriter, r *http.Request) {
16151615
c.getConfig(w, r)
16161616
}
16171617

1618-
// Get path of kubeconfig from source.
1618+
// Get path of kubeconfig we load headlamp with from source.
16191619
func (c *HeadlampConfig) getKubeConfigPath(source string) (string, error) {
16201620
if source == "kubeconfig" {
16211621
return c.kubeConfigPath, nil
@@ -1731,6 +1731,7 @@ func (c *HeadlampConfig) getPathAndLoadKubeconfig(source, clusterName string) (s
17311731
func (c *HeadlampConfig) renameCluster(w http.ResponseWriter, r *http.Request) {
17321732
vars := mux.Vars(r)
17331733
clusterName := vars["name"]
1734+
17341735
// Parse request body.
17351736
var reqBody RenameClusterRequest
17361737
if err := json.NewDecoder(r.Body).Decode(&reqBody); err != nil {
@@ -1755,40 +1756,95 @@ func (c *HeadlampConfig) renameCluster(w http.ResponseWriter, r *http.Request) {
17551756
return
17561757
}
17571758

1758-
// Find the context with the given cluster name
1759+
isUnique := CheckUniqueName(config.Contexts, clusterName, reqBody.NewClusterName)
1760+
if !isUnique {
1761+
http.Error(w, "custom name already in use", http.StatusBadRequest)
1762+
logger.Log(logger.LevelError, map[string]string{"cluster": clusterName},
1763+
err, "cluster name already exists in the kubeconfig")
1764+
1765+
return
1766+
}
1767+
1768+
contextName := findMatchingContextName(config, clusterName)
1769+
1770+
if err := customNameToExtenstions(config, contextName, reqBody.NewClusterName, path); err != nil {
1771+
http.Error(w, "writing custom extension to kubeconfig", http.StatusInternalServerError)
1772+
return
1773+
}
1774+
1775+
if errs := c.updateCustomContextToCache(config, clusterName); len(errs) > 0 {
1776+
http.Error(w, "setting up contexts from kubeconfig", http.StatusBadRequest)
1777+
return
1778+
}
1779+
1780+
w.WriteHeader(http.StatusCreated)
1781+
c.getConfig(w, r)
1782+
}
1783+
1784+
// findMatchingContextName checks all contexts, returning the key for whichever
1785+
// has a matching customObj.CustomName, if any.
1786+
func findMatchingContextName(config *api.Config, clusterName string) string {
17591787
contextName := clusterName
17601788

1761-
// Iterate over the contexts to find the context with the given cluster name
17621789
for k, v := range config.Contexts {
17631790
info := v.Extensions["headlamp_info"]
17641791
if info != nil {
17651792
customObj, err := MarshalCustomObject(info, contextName)
17661793
if err != nil {
17671794
logger.Log(logger.LevelError, map[string]string{"cluster": contextName},
17681795
err, "marshaling custom object")
1769-
1770-
return
1796+
continue
17711797
}
17721798

1773-
// Check if the CustomName field matches the cluster name
17741799
if customObj.CustomName != "" && customObj.CustomName == clusterName {
17751800
contextName = k
17761801
}
17771802
}
17781803
}
17791804

1780-
if err := customNameToExtenstions(config, contextName, reqBody.NewClusterName, path); err != nil {
1781-
http.Error(w, "writing custom extension to kubeconfig", http.StatusInternalServerError)
1782-
return
1805+
return contextName
1806+
}
1807+
1808+
// checkUniqueName returns false if 'newName' is already in 'names', otherwise returns true.
1809+
// It is used for checking context names.
1810+
//
1811+
// Parameters:
1812+
// - contexts: The Kubernetes API configuration containing contexts.
1813+
// - currentName: The name of the current context being checked.
1814+
// - newName: The new name to check for uniqueness.
1815+
func CheckUniqueName(contexts map[string]*api.Context, currentName string, newName string) bool {
1816+
contextNames := make([]string, 0, len(contexts))
1817+
1818+
for name := range contexts {
1819+
contextNames = append(contextNames, name)
1820+
logger.Log(logger.LevelInfo, map[string]string{"context added": name},
1821+
nil, "context name")
1822+
}
1823+
1824+
// Iterate over the contexts and add the custom names
1825+
for _, y := range contexts {
1826+
info := y.Extensions["headlamp_info"]
1827+
if info != nil {
1828+
customObj, err := MarshalCustomObject(info, currentName)
1829+
if err != nil {
1830+
logger.Log(logger.LevelError, map[string]string{"context": currentName},
1831+
err, "marshaling custom object")
1832+
}
1833+
1834+
// add custom name if it is not empty
1835+
if customObj.CustomName != "" {
1836+
contextNames = append(contextNames, customObj.CustomName)
1837+
}
1838+
}
17831839
}
17841840

1785-
if errs := c.updateCustomContextToCache(config, clusterName); len(errs) > 0 {
1786-
http.Error(w, "setting up contexts from kubeconfig", http.StatusBadRequest)
1787-
return
1841+
for _, current := range contextNames {
1842+
if current == newName {
1843+
return false
1844+
}
17881845
}
17891846

1790-
w.WriteHeader(http.StatusCreated)
1791-
c.getConfig(w, r)
1847+
return true
17921848
}
17931849

17941850
func (c *HeadlampConfig) addClusterSetupRoute(r *mux.Router) {

backend/cmd/headlamp_test.go

Lines changed: 96 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import (
3939
"github.com/kubernetes-sigs/headlamp/backend/pkg/kubeconfig"
4040
"github.com/stretchr/testify/assert"
4141
"github.com/stretchr/testify/require"
42+
"k8s.io/client-go/tools/clientcmd"
4243
"k8s.io/client-go/tools/clientcmd/api"
4344
)
4445

@@ -649,6 +650,84 @@ func TestHandleClusterAPI_XForwardedHost(t *testing.T) {
649650
assert.Equal(t, "OK", rr.Body.String())
650651
}
651652

653+
// handleClusterRenameRequest handles a cluster rename request.
654+
func handleClusterRenameRequest(
655+
t *testing.T,
656+
handler http.Handler,
657+
tc struct {
658+
name string
659+
clusterReq RenameClusterRequest
660+
expectedState int
661+
},
662+
) {
663+
var r *httptest.ResponseRecorder
664+
665+
var err error
666+
667+
if tc.clusterReq.Source == "kubeconfig" {
668+
url := "/cluster/minikubetestnondynamic?ClusterID=./headlamp_testdata/kubeconfig_rename:minikubetestnondynamic"
669+
r, err = getResponseFromRestrictedEndpoint(handler, "PUT", url, tc.clusterReq)
670+
require.NoError(t, err)
671+
assert.Equal(t, tc.expectedState, r.Code)
672+
} else {
673+
url := "/cluster/minikubetest?ClusterID=minikubetest"
674+
r, err = getResponseFromRestrictedEndpoint(handler, "PUT", url, tc.clusterReq)
675+
require.NoError(t, err)
676+
assert.Equal(t, tc.expectedState, r.Code)
677+
}
678+
}
679+
680+
// TestCheckUniqueName checks the CheckUniqueName function which checks if a new name is unique among existing contexts.
681+
func TestCheckUniqueName(t *testing.T) {
682+
// Need the parsed *api.Config so we can reference the contexts
683+
kubeConfig, err := clientcmd.LoadFromFile("./headlamp_testdata/name_validation_test")
684+
require.NoError(t, err)
685+
686+
cases := []struct {
687+
label string
688+
newName string
689+
expectUnique bool
690+
}{
691+
{"default name usage", "random-cluster-x", false},
692+
{"custom name usage", "superfly-name", false},
693+
{"another default name usage", "random-cluster-y", false},
694+
{"unique name usage", "amazing-name", true},
695+
}
696+
697+
for _, tc := range cases {
698+
t.Run(tc.label, func(t *testing.T) {
699+
got := CheckUniqueName(kubeConfig.Contexts, "random-cluster-y", tc.newName)
700+
if got != tc.expectUnique {
701+
t.Fatalf("CheckUniqueName(%q) = %v; want %v", tc.newName, got, tc.expectUnique)
702+
}
703+
})
704+
}
705+
}
706+
707+
// runClusterRenameTests used to run the cluster rename tests.
708+
func runClusterRenameTests(
709+
t *testing.T,
710+
handler http.Handler,
711+
tests []struct {
712+
name string
713+
clusterReq RenameClusterRequest
714+
expectedState int
715+
},
716+
) {
717+
resetConfigByte, err := os.ReadFile("./headlamp_testdata/kubeconfig_rename")
718+
require.NoError(t, err)
719+
720+
for _, tc := range tests {
721+
handleClusterRenameRequest(t, handler, tc)
722+
}
723+
724+
// This test modifies the test file, so we have to restore the test file at the end of the test.
725+
err = os.WriteFile("./headlamp_testdata/kubeconfig_rename", resetConfigByte, 0o600)
726+
require.NoError(t, err)
727+
}
728+
729+
// TestRenameCluster checks the cluster rename functionality.
730+
// note: needed to split into multiple parts for linter.
652731
func TestRenameCluster(t *testing.T) {
653732
kubeConfigByte, err := os.ReadFile("./headlamp_testdata/kubeconfig")
654733
require.NoError(t, err)
@@ -674,40 +753,42 @@ func TestRenameCluster(t *testing.T) {
674753
t.Fatal(err)
675754
}
676755

677-
clusters := c.getClusters()
678-
679756
assert.Equal(t, http.StatusCreated, r.Code)
680-
assert.Equal(t, 2, len(clusters))
681757

682758
tests := []struct {
683759
name string
684760
clusterReq RenameClusterRequest
685761
expectedState int
686762
}{
687763
{
688-
name: "passStatefull",
764+
name: "stateless",
689765
clusterReq: RenameClusterRequest{
690-
NewClusterName: "minikubetestworks",
691-
Stateless: false,
692-
Source: "kubeconfig",
766+
NewClusterName: "minikubetestworksnew",
767+
Stateless: true,
693768
},
694769
expectedState: http.StatusCreated,
695770
},
696771
{
697-
name: "stateless",
772+
name: "passStatefull",
698773
clusterReq: RenameClusterRequest{
699-
NewClusterName: "minikubetestworks",
700-
Stateless: true,
774+
NewClusterName: "minikubetestworkskubeconfig",
775+
Stateless: false,
776+
Source: "kubeconfig",
701777
},
702778
expectedState: http.StatusCreated,
703779
},
704780
}
705781

706-
for _, tc := range tests {
707-
r, err = getResponseFromRestrictedEndpoint(handler, "PUT", "/cluster/minikubetest", tc.clusterReq)
708-
require.NoError(t, err)
709-
assert.Equal(t, tc.expectedState, r.Code)
710-
}
782+
runClusterRenameTests(t, handler, tests)
783+
784+
remErr := c.kubeConfigStore.RemoveContext("minikubetest")
785+
require.NoError(t, remErr, "Failed to remove context: minikubetest")
786+
787+
remErrNonDy := c.kubeConfigStore.RemoveContext("minikubetestworkskubeconfig")
788+
require.NoError(t, remErrNonDy, "Failed to remove context: minikubetestworkskubeconfig")
789+
790+
clusters := c.getClusters()
791+
assert.Equal(t, 2, len(clusters))
711792
}
712793

713794
func TestFileExists(t *testing.T) {
Lines changed: 50 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,57 @@
11
apiVersion: v1
22
clusters:
3-
- cluster:
4-
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMvakNDQWVhZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJeU1EZ3lOakV4TURRMU0xb1hEVE15TURneU16RXhNRFExTTFvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTnk3Ci9kREMxV0w3TXNSWGV2Z2tUQXkzcFZHMVVLa1VQeXd4cS9ETHBPdmRzQmloQjZoVmN1bWNZUTkzYUxLbERzSXMKR0Q0QUJkUFM4cEFPMzhMb3RBWWVDeDIwcDFPem9LYVMvVkp6ZlJKQWVUSStCY3dzRjh2U1VXYU0reWZ4STBPUgpnalE0OVR0eUppYURyS2tzbnd4R3Y0K0U3aWFhZUVPMG55U01EcnpON1RvYkVyb1pObHRzNkdMN2tpTDB0TG5ZCkorNnNtSHlhSGh6WThaR0JZMFdWUXpzNENFMnJ0Q1k5eTV4N2F3bUlDUWE2anBXVFVQazNqa0RMcU93bEQyRmMKcHNkeXI4a1Z3UUhTUUVnRkg2Yzgwdnp3Ny9RSUVDdGRYNlZRRnE1bzYzOWlvc3hQcXVKV3ZtMGVjdkx5dC81cApxNXZpNzMxWThEb0VDMjFtS2NzQ0F3RUFBYU5aTUZjd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZJNFFkU2FSRFVodi8wWjk0ZzV5RmlVdWlMZHBNQlVHQTFVZEVRUU8KTUF5Q0NtdDFZbVZ5Ym1WMFpYTXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQ3pWWUpBUzQ1UFBOSFVSaDJKWQpKWDFycmFMdGNTbzVuNG1DVy9oeE5YdHpCMlIzWkhPU0hnNmF2R3JNeFY4ZlpCdmtBdEJFaUYzM2JvRThzZzVhCjhhWHRFTjR5bzlZQ2FZc2ZXK2tNNlZDRUdtVWd5bm13aXltYTBzSW5USlZ1R3ZVbDVucVhjUHJJdW9OTVVrTUwKdCsrckxCb0NwY2xrN09VSTA0dXZvanpxc2hsQ0JiMURSOXRwT0s0Kys0UGdPait6OXZ0N3g0dzhMYlhvQmtvegozOEJyVEoyQ3NqbU0xS2ZqZXlpNWdHVmFjeE9YSXRjbXprNzRpQzZ0SjdqVm1MVmNacEc5ZElvcFk5WTBaTkQ0CmQzZjlmOGdCWkJzaXA0a3gxMmFxMlJ5dzFYNGVOaFY2dW5OaCtHVHNhNlFDSlJ0Zk9FK1Q4Njd2ZHlPbjZMb2wKYWQ4PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
5-
extensions:
6-
- extension:
7-
last-update: Mon, 26 Dec 2022 20:33:03 IST
8-
provider: minikubetest.sigs.k8s.io
9-
version: v1.28.0
10-
name: cluster_info
11-
server: https://127.0.0.1:60279
12-
name: minikubetest
3+
- cluster:
4+
certificate-authority-data: dGVzdC1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YQ==
5+
extensions:
6+
- extension:
7+
last-update: Mon, 26 Dec 2022 20:33:03 IST
8+
provider: minikubetest.sigs.k8s.io
9+
version: v1.28.0
10+
name: cluster_info
11+
server: https://127.0.0.1:60279
12+
name: minikubetest
13+
- cluster:
14+
certificate-authority-data: dGVzdC1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YQ==
15+
extensions:
16+
- extension:
17+
last-update: Mon, 26 Dec 2022 20:33:03 IST
18+
provider: minikubetestnondynamic.sigs.k8s.io
19+
version: v1.28.0
20+
name: cluster_info
21+
server: https://127.0.0.1:60279
22+
name: minikubetestnondynamic
1323
contexts:
14-
- context:
15-
cluster: minikubetest
16-
extensions:
17-
- extension:
18-
last-update: Mon, 26 Dec 2022 20:33:03 IST
19-
provider: minikubetest.sigs.k8s.io
20-
version: v1.28.0
21-
name: context_info
22-
- extension:
23-
creationTimestamp: null
24-
customName: minikubetestworks
25-
name: headlamp_info
26-
namespace: default
27-
user: minikubetest
28-
name: minikubetest
24+
- context:
25+
cluster: minikubetest
26+
extensions:
27+
- extension:
28+
last-update: Mon, 26 Dec 2022 20:33:03 IST
29+
provider: minikubetest.sigs.k8s.io
30+
version: v1.28.0
31+
name: context_info
32+
namespace: default
33+
user: minikubetest
34+
name: minikubetest
35+
- context:
36+
cluster: minikubetestnondynamic
37+
extensions:
38+
- extension:
39+
last-update: Mon, 26 Dec 2022 20:33:03 IST
40+
provider: minikubetestnondynamic.sigs.k8s.io
41+
version: v1.28.0
42+
name: context_info
43+
namespace: default
44+
user: minikubetestnondynamic
45+
name: minikubetestnondynamic
2946
current-context: minikubetest
3047
kind: Config
3148
preferences: {}
3249
users:
33-
- name: minikubetest
34-
user:
35-
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURJekNDQWd1Z0F3SUJBZ0lJZlJpZk1qZWl1eFV3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TWpBNE1qWXhNVEEwTlROYUZ3MHlOREF4TURJeE1ESXhNelZhTURZeApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sc3dHUVlEVlFRREV4SmtiMk5yWlhJdFptOXlMV1JsCmMydDBiM0F3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQzRLUmwrS0lsN0NJYVgKbzIwYjdBOVEvaURDbUN6dWFXMSs3WEJyelhiZHNmNkNaRzhVMWZYWTdVWXl3bXVhYkZldUFrUHRBT1hyWVg0YQpMTGZtWTRvdkZYc1RQWmtPUktJeWRFUmNnLy9hOStPd3d2c1ZCUUp4NFplbUtrN1NzaFYxcjl3WGVqVnJIUkFOCm5xQ3JIQVhFNHA5bmFKZHNkTXIyQWdDa0VIK01tTFNqTExNL1lWcnExdmJpRWRtUVFSWHduVnFwcmNyRXBIQzUKWWJJenl4cVZRWWZIZVdWc2N0SUxFeVdPMFQwMS9tYkZ2RVY4QW9BL3phekIycjF3Y0VaeUNSRXFXbExrS2RXTwpNYmU1WnlwMDNhQzlBOSs4cThQNFBEOUNnVXlrcVovN0xydGlja2k0TVBsK2VmaGFlUk9YSEJMSURuQmplTHJkCmJGdHpaOVhKQWdNQkFBR2pWakJVTUE0R0ExVWREd0VCL3dRRUF3SUZvREFUQmdOVkhTVUVEREFLQmdnckJnRUYKQlFjREFqQU1CZ05WSFJNQkFmOEVBakFBTUI4R0ExVWRJd1FZTUJhQUZJNFFkU2FSRFVodi8wWjk0ZzV5RmlVdQppTGRwTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFBRjNvaTFvNVlNM1UvOWxPRElhaUpmaGllNzdieG1pN3NwCitiL0NEOGRCTXhIdWpPVnBSaTFNaHRJa2U3N2U1RVVuZEFGRzYvQTQwK3c2TGtCYXJFUEl5R2daRlBvZkttcSsKRGlIMGxPZHBYY0hFd3laTjhWSmdRd0JKUkhKcDhBc0p3TGFYWGplU1FQdmZyeHhLdUFGenRzeXNaYlBMUkxoYQpjeXZmeDNwTE91ZVJ4MDJqQVZUUlNJUGNPZEV4SERPa0FGWFFCdDV4TFo2eGFKTU1VQjZXNUYwcVpPelFuVUZsCk80QUNNOEhnOEdKc2xqLzFqZnpZaGlneWdwL2psQ0Jkd1Izb2c1ZXFqaC9ZRzlxWHVsU2Z0WUNhMURaOEp2QnAKaGRSYzZxOVM0ZFdtRW9zMmkxTDA1WUs3ZFBaQk5JVHRLNkVzQS9CRCs0VlVWRHczZldkNQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
36-
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBdUNrWmZpaUpld2lHbDZOdEcrd1BVUDRnd3BnczdtbHRmdTF3YTgxMjNiSCtnbVJ2CkZOWDEyTzFHTXNKcm1teFhyZ0pEN1FEbDYyRitHaXkzNW1PS0x4VjdFejJaRGtTaU1uUkVYSVAvMnZmanNNTDcKRlFVQ2NlR1hwaXBPMHJJVmRhL2NGM28xYXgwUURaNmdxeHdGeE9LZloyaVhiSFRLOWdJQXBCQi9qSmkwb3l5egpQMkZhNnRiMjRoSFprRUVWOEoxYXFhM0t4S1J3dVdHeU04c2FsVUdIeDNsbGJITFNDeE1sanRFOU5mNW14YnhGCmZBS0FQODJzd2RxOWNIQkdjZ2tSS2xwUzVDblZqakczdVdjcWROMmd2UVBmdkt2RCtEdy9Rb0ZNcEttZit5NjcKWW5KSXVERDVmbm40V25rVGx4d1N5QTV3WTNpNjNXeGJjMmZWeVFJREFRQUJBb0lCQUdYMWUwTzV0Y1FFU0dBVAovd2lDZlVoZUtrMFNhMjNqdU5lWkpiREpwSkhCUmlOeTczMGRxR3Rka292djBCdEMrSmhDY05ENnVsRERQVW5JCmtGaGhxOU85bE5KbVBDTUdKTGJDWUViSVhoTWhRMUpONFMwV0JQQi84Ykh4b29wTVJrMU4vQkNUZkplOUUzSTIKN01WUFVuSmE5ZDRPcmlkQjBreTVkeGxlZVAraGFvT2NTejJGamhXbDEycnlqbm1ad0draU1BdnhHazBaejFkZgpxZ0QyUE5CRHYzMTdtMkJxYjdkcENaTmZsSi90MGtqQ3hGbm81UmRsUUl5NDhSeml4LytaV1ZSeWlsaVFjL2srCnY4UzRTWGljZjBDK1RzV0orZzBNU3NoaGs1SWV4OURzTmR1bmJrSTcxMDNoR2ZOcGZYbGlRVlZHTlZ2eHdNd2kKenV4eE9nRUNnWUVBeGFYbU9GSUhkbm1tU0tMbEp4KzhBc3pPcmdTNDFKM2RMemtUZUZhWU5STHY0Z1AyK05SQwpQcXAzYVRCRmNjNWFMTDFXcG1ZRkdGSkthejRUbm05dlRKN0Nhell6K1RZRWc4OWlidGJESmhFSlFaSGtRaDQxCnJXRlBHTERWL1ZVSk5kaStSRk9TU2xMVDVGY2gvS1NkdFFRazhkbStUempiWi94a29ZOVpKbmtDZ1lFQTdvZlQKRnQ0MytQY0w5T0ZoTGZjYnB2SUJkeDdzWlAyK09NVzF3eG92TnhTbElSelBSQi9QbXNsai9hUzV2VnFWOVlVdgp0YjVFaUM3cUVYN2JVS0lZT1hMY1c3N3ozcmxpODdMRW5CWkNDemFhdHk3Nmw1U2lneU16VDY3MS9DdjNSSVJYCkw5citoQ2ZUUThPVHBUVDR6R3ZhRWJrQldBcnRmeVZOTDhkVGxkRUNnWUF1TE44SFEyckk3QXpFSllKaHpKRXgKR2tZaTg2bDJ5dGJVNUlHKytJUWd5aWJPNTl3NEwrYTJHejlBak8xOGRCZ3ZJYUR2eVIvaG1jQVhJKzZUY2pkUApjRHU5cm5FZ0JOV3pNYTB1ZGZBcm9ZbEhEMTJEY09sYmMwTjJZa0hzS0lTNVZzVEUwNzEycmJraFBKWU5IeXhWCkVQM01udkZPTXR0WGhPakJzZXJEQ1FLQmdEOCtBOW1zVVdyUkZYcDN4eXhJdUN3clBmZzNXclhzRU9NOGlGU1MKUExKOTVzcEF1VE4ydTdSdWNQUnZHRS84Rklaa0thSW1NRVZyS3VRNG5pMWl6TWx1aXI1SWdxQXF4dkdXRkVyTwpHL1NkSmFncjdJVUVBNUtCWXJsZHlocHlEYjA4MldEMnowUjZ5cWpNMGZpYmN0dkFQTEUyUEFUNzRMdzFSNkhEClY0WUJBb0dCQUo4bUdVNWJnejBQUnJZL1hoUERER2tIa0Z3SHVocHhjL2tieWNweVVTZ2NDR2dsczFXTTdKOFUKQ05MV2pQc0pnSTNxeTJSN0xxaGxmNHJRK1orLy80WHdyNW1NYWpqSEVGZE93Z2xqbFZRdEljNWM4a2U3MzM1SwprbCtxblpJbUcwRy83R1hUZHhSOW9mQXBBZVFkT3pJTFY0K1YzZm5DRGNYMnBHWjZOOTJUCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==
50+
- name: minikubetest
51+
user:
52+
client-certificate-data: dGVzdC1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YQ==
53+
client-key-data: dGVzdC1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YQ==
54+
- name: minikubetestnondynamic
55+
user:
56+
client-certificate-data: dGVzdC1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YQ==
57+
client-key-data: dGVzdC1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YQ==

0 commit comments

Comments
 (0)