Skip to content

Commit ccb7453

Browse files
committed
✨ Add support to clusterctl for inserting workload kubeconfig into an existing kubeconfig file
1 parent e389055 commit ccb7453

File tree

6 files changed

+186
-1
lines changed

6 files changed

+186
-1
lines changed

Diff for: cmd/clusterctl/cmd/get_kubeconfig.go

+34
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@ package cmd
1919
import (
2020
"context"
2121
"fmt"
22+
"os"
2223

2324
"github.com/pkg/errors"
2425
"github.com/spf13/cobra"
26+
"k8s.io/client-go/tools/clientcmd"
2527

2628
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta2"
2729
"sigs.k8s.io/cluster-api/cmd/clusterctl/client"
@@ -32,6 +34,7 @@ type getKubeconfigOptions struct {
3234
kubeconfig string
3335
kubeconfigContext string
3436
namespace string
37+
intoKubeconfig string
3538
}
3639

3740
var gk = &getKubeconfigOptions{}
@@ -67,6 +70,8 @@ func init() {
6770
"Path to the kubeconfig file to use for accessing the management cluster. If unspecified, default discovery rules apply.")
6871
getKubeconfigCmd.Flags().StringVar(&gk.kubeconfigContext, "kubeconfig-context", "",
6972
"Context to be used within the kubeconfig file. If empty, current context will be used.")
73+
getKubeconfigCmd.Flags().StringVar(&gk.intoKubeconfig, "into-kubeconfig", "",
74+
"Path to the kubeconfig file where the resulting kubeconfig will be inserted.")
7075

7176
// completions
7277
getKubeconfigCmd.ValidArgsFunction = resourceNameCompletionFunc(
@@ -98,6 +103,35 @@ func runGetKubeconfig(workloadClusterName string) error {
98103
if err != nil {
99104
return err
100105
}
106+
if gk.intoKubeconfig != "" {
107+
return intoKubeconfig(gk.intoKubeconfig, out)
108+
}
109+
101110
fmt.Println(out)
102111
return nil
103112
}
113+
114+
func intoKubeconfig(path, kubeconfig string) error {
115+
kubeconfigFile, err := os.CreateTemp("", "kubeconfig")
116+
if err != nil {
117+
return err
118+
}
119+
defer os.Remove(kubeconfigFile.Name())
120+
121+
if _, err = kubeconfigFile.WriteString(kubeconfig); err != nil {
122+
return err
123+
}
124+
if err = kubeconfigFile.Close(); err != nil {
125+
return err
126+
}
127+
128+
rules := &clientcmd.ClientConfigLoadingRules{
129+
Precedence: []string{path, kubeconfigFile.Name()},
130+
}
131+
config, err := rules.Load()
132+
if err != nil {
133+
return err
134+
}
135+
136+
return clientcmd.WriteToFile(*config, rules.Precedence[0])
137+
}

Diff for: cmd/clusterctl/cmd/get_kubeconfig_test.go

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
Copyright 2020 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 cmd
18+
19+
import (
20+
_ "embed"
21+
"os"
22+
"path/filepath"
23+
"testing"
24+
25+
. "github.com/onsi/gomega"
26+
)
27+
28+
//go:embed testdata/existsing-kubeconfig.yaml
29+
var existingKubeconfig string
30+
31+
//go:embed testdata/workload-kubeconfig.yaml
32+
var workloadKubeconfig string
33+
34+
//go:embed testdata/joined-kubeconfig.yaml
35+
var joinedKubeconfig string
36+
37+
func Test_intoKubeconfig(t *testing.T) {
38+
tmpDir := t.TempDir()
39+
40+
tt := []struct {
41+
name string
42+
path string
43+
data string
44+
expected string
45+
}{
46+
{
47+
name: "inserting into empty kubeconfig",
48+
path: filepath.Join(tmpDir, "empty-kubeconfig"),
49+
data: "",
50+
expected: workloadKubeconfig,
51+
},
52+
{
53+
name: "inserting into existing kubeconfig",
54+
path: filepath.Join(tmpDir, "existing-kubeconfig"),
55+
data: existingKubeconfig,
56+
expected: joinedKubeconfig,
57+
},
58+
}
59+
60+
for _, tc := range tt {
61+
t.Run(tc.name, func(t *testing.T) {
62+
g := NewWithT(t)
63+
if tc.data != "" {
64+
err := os.WriteFile(tc.path, []byte(tc.data), 0600)
65+
g.Expect(err).ToNot(HaveOccurred())
66+
}
67+
68+
err := intoKubeconfig(tc.path, workloadKubeconfig)
69+
g.Expect(err).ToNot(HaveOccurred())
70+
71+
expected, err := os.ReadFile(tc.path)
72+
g.Expect(err).ToNot(HaveOccurred())
73+
g.Expect(string(expected)).To(Equal(tc.expected))
74+
})
75+
}
76+
}
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
apiVersion: v1
2+
clusters:
3+
- cluster:
4+
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURCVENDQWUyZ0F3SUJBZ0lJTzYyZzVmMHlsR2t3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TlRBek1UQXdOakF6TkRaYUZ3MHpOVEF6TURnd05qQTRORFphTUJVeApFekFSQmdOVkJBTVRDbXQxWW1WeWJtVjBaWE13Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLCkFvSUJBUURESzh6a1U1QWkxRTJIazNiM01PQko3Tko4YmVIRzlVMXQ0cWdkclpyMWZadHUrUDBHVlArejAzWWMKWWlTd0QzNitJWk04ZGkvQVV3VjduVXhFT0ZOUjh2Y1RRdkNqdktUUVlFWTI3RlZxUWxNNklnbmM2Y3JOUHRQUApKZUxlNG1zeHBHMVVOa3R6YzZucGVEb1diT0ttVnVST2lUT0l3TG9TVld4YkkrR1NScS9STkROT2VVam1hM01TClJqV1VneTEyM3IxQlFCbEYyMFI4b29MZVBldzhCeWk5U2dhRUlhbXdoODhMTmhVR3VsVlJiOTNHUDZQSEQ2bkMKdzBSQ1p3MlIvTS9EdlZtakxvZEpSRnBIKytBaTkwZVNZcGtFekk2WlM5WW00TjhOZFROU3FySy9OcTBBcjNzNwpjc2NqTjc0aThCYlZsRDJIR1UxY2sxdmgyNHh0QWdNQkFBR2pXVEJYTUE0R0ExVWREd0VCL3dRRUF3SUNwREFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJRVFhzWkkzNFFJbGtGUDk0TFNCcFU3aUZFYW9qQVYKQmdOVkhSRUVEakFNZ2dwcmRXSmxjbTVsZEdWek1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQUxDeTQ5QUlWcwpuSkJ4T3ZuM2lkQUdtZ2VEbTFMbElLQ254eUtEb2FTcXlldWpVT2ozYXY3U1dzUDZaTERiNzQ1dXpuT2JEK0RiCi9STkJMSDZKTEs2dGNTRTcxTVpaeWV6N1YxWlF1MExqdXlmT2xiM2VzcEVka25uMEZyMkhibXYzbW5aU3NROTIKK3JvVUNiQXhOSElqQUY2UlV5Uzk5ZWp3cTdQRWtTU2o1U1RFNW1YYUF0OEdoQnVwODNkbTlQMklBWmZlcytucQpLNVFwUFk3bnh4bEI1TGdpcFdrdHc2djZpNGdqWCsvNUVoVDNnMUk2dW1ncXdTTkhMNHlLcFBtV2NVTXcvVkU3Ckg5c0VSa1JwbDlMNWx1SlB3TmdKcmpWRnBwQTN6bnFuNTRHa0RWeDhvN1UxLzFFK2pudUVCU1JCZ1I1alRjdTgKcDR4WkVWMXZRdG1iCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
5+
server: https://127.0.0.1:42341
6+
name: kind-caaph-e2e
7+
contexts:
8+
- context:
9+
cluster: kind-caaph-e2e
10+
user: kind-caaph-e2e
11+
name: kind-caaph-e2e
12+
current-context: kind-caaph-e2e
13+
kind: Config
14+
preferences: {}
15+
users:
16+
- name: kind-caaph-e2e
17+
user:
18+
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURLVENDQWhHZ0F3SUJBZ0lJYklySmRLM1ZSRWN3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TlRBek1UQXdOakF6TkRaYUZ3MHlOakF6TVRBd05qQTRORFphTUR3eApIekFkQmdOVkJBb1RGbXQxWW1WaFpHMDZZMngxYzNSbGNpMWhaRzFwYm5NeEdUQVhCZ05WQkFNVEVHdDFZbVZ5CmJtVjBaWE10WVdSdGFXNHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDbnEyYkMKQnhxZGIwa3ZWRGRkMUpidUI0amdkL2RzMFhaQlBlOFBPRW8yRUs3SWF2ZkJiL3orYUxxWXdVKzFZZFZFR1EyZwphcXdUM0hTY0VYdlVkNHRvVFA0dUJMbGt6Q1lvUTh4UngzcGtpNlVIdnplbzFscXRJYm0vV0JjTmt0M2VZakhJCkxqSjBZV2JRNm5CaEtvRU4vRC9ySkNHbFMvbSs0eU9WN0tJU29uZXlsVkd6eldUVlRYelBkZVI1eVBTUk1ucmQKdUpOYXlSSldjaE1BQ1Y5aFppZTVoYlgyRnZDMkpzSXlROFZMMEMzY2h6ZStoMEFQc0syNWVYaU9QU1M0a1A2WQo4L0dHU2pySUJ3R2dZNHMyanpaQTJjUC9jN3RoYWxSTzhkVUtQNWl5cVV0ekluR25FbkQ3RUdveitaL3RXeGM3CmRGK1ByN3hBQkt1dE9KNExBZ01CQUFHalZqQlVNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUsKQmdnckJnRUZCUWNEQWpBTUJnTlZIUk1CQWY4RUFqQUFNQjhHQTFVZEl3UVlNQmFBRkJOZXhramZoQWlXUVUvMwpndElHbFR1SVVScWlNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUJMc0VXblBLRU13MWxlNmFzWXkrdGc3OTZ4Cm92blpnRzlsQ0NpMlFuem1QSERIRjRwaFhCb2ZzNDJiQXFIY05ER1A2dEhwUUFOM25qWk1HNGJvLzR3U1NHb0gKTk1yOTNDclhSNXArUWFJa2VKNnpaUWRnK0lCb3l3NENWell4SS9ia3hUTldZR2VodERaSzU1MGVveUc2ZW1nQwp0WHpFZ3RpVllNRXNQeFpMSHF2TTdRNzIzNDljTGhXV3ViRWFCcFRMdCtDZ3YzR1lob1grRW5QS0l5UXVFaHRSCktBMEZIRk9wd3R2aEdFaGk4OTFsRGE4RmNYRXZvQnJWWVpyLzVtWkN3VFprOGRiSmUrV0xkb1U4V2x0Y083K1AKYkRYNUZVYUNMVU5NZnVpSHVUZUdvSjlic0VzVnB2V29PZDdEQkZ1NmY2WnhZSFRGZE92NHpIRTgyVmJSCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
19+
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBcDZ0bXdnY2FuVzlKTDFRM1hkU1c3Z2VJNEhmM2JORjJRVDN2RHpoS05oQ3V5R3IzCndXLzgvbWk2bU1GUHRXSFZSQmtOb0dxc0U5eDBuQkY3MUhlTGFFeitMZ1M1Wk13bUtFUE1VY2Q2Wkl1bEI3ODMKcU5aYXJTRzV2MWdYRFpMZDNtSXh5QzR5ZEdGbTBPcHdZU3FCRGZ3LzZ5UWhwVXY1dnVNamxleWlFcUozc3BWUgpzODFrMVUxOHozWGtlY2owa1RKNjNiaVRXc2tTVm5JVEFBbGZZV1ludVlXMTloYnd0aWJDTWtQRlM5QXQzSWMzCnZvZEFEN0N0dVhsNGpqMGt1SkQrbVBQeGhrbzZ5QWNCb0dPTE5vODJRTm5ELzNPN1lXcFVUdkhWQ2orWXNxbEwKY3lKeHB4SncreEJxTS9tZjdWc1hPM1JmajYrOFFBU3JyVGllQ3dJREFRQUJBb0lCQUM1RUdhTEltd3QrUGMrVgppWUdXdTk4S3JUWmpaQjdlM2FhN0ZGQTYxQjkvRXZ1eXFKTHo5L2VycG5Hb1JmcGkrSnBGMzRDNGJxczhWZUVLCjRnU0t5Q2tMdGNlMC9nTldZZUVTZERyTXRIVXlJMk96M0FQMndpYXIrN2xVOFdlSmN6ZCtHUWFIMk54TVZEYXAKdWtEVm5hN2lRREQrVTFwdFVJamYzRjN1WWZxSjc5T0VjU3ZXdGwrTDJWb2VJdHVrRlJDNFdweUc4dVRwM0lsNwpYU2x0MDVCVHZhMTlzbHhkNnRQOFlXemNhSHFvY29Vci9YRFR2RnRBRkF5QXdzdFZJcUNnbm90LysycVRWZzZECkdRRmp4VzZmeGhDQmF2VEdQTTBST2VYb0lZVkZKTzdNL3ZxZENKYlcvVGQ5L2xqZ01vNEczbmM0ejFOQ0FGWGYKSytselR1RUNnWUVBeG1YSWFnb0VFcGxXWkxkZnR5eHhUUU9DRkcyY3hxWFdsM0F6bmc3dWFZNlNOY0x1K3hGKwoxa0ZhZXFaWEIzbUdOREZqd013Y2RZQW5iZnRha3dUUWJKM3hSVzJNQWZ2aWRpb1Y0bkoza093Y3dVVkh5NzEwClFsN1p6cERsa3VxalNKeHNYOTVjL1pKem55ai8wN2NPblJMU1lKRVE2ZUFiczdWeTZXWmtaQzhDZ1lFQTJGbTAKLytVRWRuRXBNYmViQVNxWEFlcEFFUHFYSXVRMzB5RS9qVWQ0NHUvaTNaNFg1SmlabGtSenBGWTMrZ2JTMGdJaQpsQXBhUy9aY1Bna2pDa2p1SXFXSTZsRnNCeXh0RlBEdjhJT0crZHhMaTh4b3dUQUg4YS9kSm5TVHRta2cxd1l2CmxwZlVMK2dCa25xbHhJb3dVK2lkQ09sZGlPRnlIdmRvcmtlRkFPVUNnWUVBeGxaSWhlR2ZjcXlDVnN1UFQ0S0oKZmhGTXdIbFlLcXJ0blpWWXRPRitiRjVRUXBpdTZxWHg0K1dQdTErQlQ0S29ySkxYZzQxSndobXMwV0ZmaWV4ZApKTWk4dzhpaEYvYWt6YmFEREUrY3dtK1l5NG9rSFVVMzdZWC9QbWRTYW81UlVDeTRseHFvWWdNL3ZWUnlkRWZqCnZ4bHE0WWtFbkpvamQ0T2s3am9qa0pjQ2dZQlpobTEyZ2w4aFg5d3EvWTMvb284UU5NM0QrTjMzaXpacWdGU2EKOHB0Y1ZPV3N6UkdYLzJsMm0rYTZkdzc4d0R3V3VObDBKd0NNZEdwaEpVakJwY0ljeGlEc3pUcUZrV1dtQjdsUgo3aXM1VTJaVGVBZFZBYm9YMGEwU2o2VGNFNDZLUzczL2M5NVM0STlkL0xDQnJxa20zNWV6cTFXUUliSW1qRHhkCitheFkzUUtCZ0VybjlxSnllU2FoREdtU2dkRFRJR09hSUtwODRYWE1OcU5wbTZ5dzdQbnF5dWxLSXJCM0Q2SFoKOEtMaTVIRDhTR2xVWkRkaU9RaUpYREJhSGdHbmt5MFJGNnNCbm9TaFNuSFZqaG9tNVc1YkZ1aHRjZnFDVmQ2eQpWckUyemxzMW5ZaUZ1Ylc1VW1iTjdrSldPQlVxbmsvNjR2cmUwZEhHUXd1TXh3OHUrT0U1Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==

0 commit comments

Comments
 (0)