Skip to content

Commit 6aeff8c

Browse files
committed
Support defining a KubeConfig Secret data key
Signed-off-by: Nick Stogner <[email protected]>
1 parent e0ba73f commit 6aeff8c

20 files changed

+171
-50
lines changed

api/go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ go 1.17
44

55
require (
66
github.com/fluxcd/pkg/apis/kustomize v0.3.3
7-
github.com/fluxcd/pkg/apis/meta v0.12.2
7+
github.com/fluxcd/pkg/apis/meta v0.13.0
88
k8s.io/apiextensions-apiserver v0.23.5
99
k8s.io/apimachinery v0.23.5
1010
sigs.k8s.io/controller-runtime v0.11.2

api/go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,8 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
122122
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
123123
github.com/fluxcd/pkg/apis/kustomize v0.3.3 h1:bPN29SdVzWl0yhgivuf/83IAe2R6vUuDVcB3LzyVU8E=
124124
github.com/fluxcd/pkg/apis/kustomize v0.3.3/go.mod h1:5HTOFZfQFVMMqR2rvuxpbZhpb+sQpcTT6RCQZOhjFzA=
125-
github.com/fluxcd/pkg/apis/meta v0.12.2 h1:AiKAZxLyPtV150y63WC+mL1Qm4x5qWQmW6r4mLy1i8c=
126-
github.com/fluxcd/pkg/apis/meta v0.12.2/go.mod h1:Z26X5uTU5LxAyWETGueRQY7TvdPaGfKU7Wye9bdUlho=
125+
github.com/fluxcd/pkg/apis/meta v0.13.0 h1:0QuNKEExSjk+Rv0I6a85p2H3xOlWhdxZRsh10waEL/c=
126+
github.com/fluxcd/pkg/apis/meta v0.13.0/go.mod h1:Z26X5uTU5LxAyWETGueRQY7TvdPaGfKU7Wye9bdUlho=
127127
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
128128
github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
129129
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=

api/v1beta2/kustomization_types.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -164,16 +164,17 @@ type Decryption struct {
164164

165165
// KubeConfig references a Kubernetes secret that contains a kubeconfig file.
166166
type KubeConfig struct {
167-
// SecretRef holds the name to a secret that contains a 'value' key with
168-
// the kubeconfig file as the value. It must be in the same namespace as
167+
// SecretRef holds the name of a secret that contains a key with
168+
// the kubeconfig file as the value. If no key is set, the key will default
169+
// to 'value'. The secret must be in the same namespace as
169170
// the Kustomization.
170171
// It is recommended that the kubeconfig is self-contained, and the secret
171172
// is regularly updated if credentials such as a cloud-access-token expire.
172173
// Cloud specific `cmd-path` auth helpers will not function without adding
173174
// binaries and credentials to the Pod that is responsible for reconciling
174175
// the Kustomization.
175176
// +required
176-
SecretRef meta.LocalObjectReference `json:"secretRef,omitempty"`
177+
SecretRef meta.SecretKeyReference `json:"secretRef,omitempty"`
177178
}
178179

179180
// PostBuild describes which actions to perform on the YAML manifest

config/crd/bases/kustomize.toolkit.fluxcd.io_kustomizations.yaml

+13-9
Original file line numberDiff line numberDiff line change
@@ -698,17 +698,21 @@ spec:
698698
is empty.
699699
properties:
700700
secretRef:
701-
description: SecretRef holds the name to a secret that contains
702-
a 'value' key with the kubeconfig file as the value. It must
703-
be in the same namespace as the Kustomization. It is recommended
704-
that the kubeconfig is self-contained, and the secret is regularly
705-
updated if credentials such as a cloud-access-token expire.
706-
Cloud specific `cmd-path` auth helpers will not function without
707-
adding binaries and credentials to the Pod that is responsible
708-
for reconciling the Kustomization.
701+
description: SecretRef holds the name of a secret that contains
702+
a key with the kubeconfig file as the value. If no key is set,
703+
the key will default to 'value'. The secret must be in the same
704+
namespace as the Kustomization. It is recommended that the kubeconfig
705+
is self-contained, and the secret is regularly updated if credentials
706+
such as a cloud-access-token expire. Cloud specific `cmd-path`
707+
auth helpers will not function without adding binaries and credentials
708+
to the Pod that is responsible for reconciling the Kustomization.
709709
properties:
710+
key:
711+
description: Key in the Secret, when not specified an implementation-specific
712+
default key is used.
713+
type: string
710714
name:
711-
description: Name of the referent.
715+
description: Name of the Secret.
712716
type: string
713717
required:
714718
- name

controllers/kustomization_acl_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ stringData:
8989
Interval: metav1.Duration{Duration: reconciliationInterval},
9090
Path: "./",
9191
KubeConfig: &kustomizev1.KubeConfig{
92-
SecretRef: meta.LocalObjectReference{
92+
SecretRef: meta.SecretKeyReference{
9393
Name: "kubeconfig",
9494
},
9595
},

controllers/kustomization_decryptor_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ func TestKustomizationReconciler_Decryptor(t *testing.T) {
146146
Interval: metav1.Duration{Duration: 2 * time.Minute},
147147
Path: "./",
148148
KubeConfig: &kustomizev1.KubeConfig{
149-
SecretRef: meta.LocalObjectReference{
149+
SecretRef: meta.SecretKeyReference{
150150
Name: "kubeconfig",
151151
},
152152
},

controllers/kustomization_dependson_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ spec:
143143
Interval: metav1.Duration{Duration: reconciliationInterval},
144144
Path: "./",
145145
KubeConfig: &kustomizev1.KubeConfig{
146-
SecretRef: meta.LocalObjectReference{
146+
SecretRef: meta.SecretKeyReference{
147147
Name: "kubeconfig",
148148
},
149149
},

controllers/kustomization_force_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ stringData:
8686
Interval: metav1.Duration{Duration: reconciliationInterval},
8787
Path: "./",
8888
KubeConfig: &kustomizev1.KubeConfig{
89-
SecretRef: meta.LocalObjectReference{
89+
SecretRef: meta.SecretKeyReference{
9090
Name: "kubeconfig",
9191
},
9292
},

controllers/kustomization_impersonation.go

+13-8
Original file line numberDiff line numberDiff line change
@@ -177,15 +177,20 @@ func (ki *KustomizeImpersonation) getKubeConfig(ctx context.Context) ([]byte, er
177177
}
178178

179179
var kubeConfig []byte
180-
for k := range secret.Data {
181-
if k == "value" || k == "value.yaml" {
182-
kubeConfig = secret.Data[k]
183-
break
180+
switch {
181+
case ki.kustomization.Spec.KubeConfig.SecretRef.Key != "":
182+
key := ki.kustomization.Spec.KubeConfig.SecretRef.Key
183+
kubeConfig = secret.Data[key]
184+
if kubeConfig == nil {
185+
return nil, fmt.Errorf("KubeConfig secret '%s' does not contain a '%s' key with a kubeconfig", secretName, key)
184186
}
185-
}
186-
187-
if len(kubeConfig) == 0 {
188-
return nil, fmt.Errorf("KubeConfig secret '%s' doesn't contain a 'value' key ", secretName.String())
187+
case secret.Data["value"] != nil:
188+
kubeConfig = secret.Data["value"]
189+
case secret.Data["value.yaml"] != nil:
190+
kubeConfig = secret.Data["value.yaml"]
191+
default:
192+
// User did not specify a key, and the 'value' key was not defined.
193+
return nil, fmt.Errorf("KubeConfig secret '%s' does not contain a 'value' key with a kubeconfig", secretName)
189194
}
190195

191196
return kubeConfig, nil

controllers/kustomization_impersonation_test.go

+111-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ data:
9292
Interval: metav1.Duration{Duration: time.Minute},
9393
Path: "./",
9494
KubeConfig: &kustomizev1.KubeConfig{
95-
SecretRef: meta.LocalObjectReference{
95+
SecretRef: meta.SecretKeyReference{
9696
Name: "kubeconfig",
9797
},
9898
},
@@ -208,3 +208,113 @@ data:
208208
g.Expect(apierrors.IsNotFound(err)).To(BeTrue())
209209
})
210210
}
211+
212+
func TestKustomizationReconciler_KubeConfig(t *testing.T) {
213+
g := NewWithT(t)
214+
id := "kc-" + randStringRunes(5)
215+
revision := "v1.0.0"
216+
217+
err := createNamespace(id)
218+
g.Expect(err).NotTo(HaveOccurred(), "failed to create test namespace")
219+
220+
manifests := func(name string, data string) []testserver.File {
221+
return []testserver.File{
222+
{
223+
Name: "config.yaml",
224+
Body: fmt.Sprintf(`---
225+
apiVersion: v1
226+
kind: ConfigMap
227+
metadata:
228+
name: %[1]s
229+
data:
230+
key: "%[2]s"
231+
`, name, data),
232+
},
233+
}
234+
}
235+
236+
artifact, err := testServer.ArtifactFromFiles(manifests(id, randStringRunes(5)))
237+
g.Expect(err).NotTo(HaveOccurred(), "failed to create artifact from files")
238+
239+
repositoryName := types.NamespacedName{
240+
Name: randStringRunes(5),
241+
Namespace: id,
242+
}
243+
244+
err = applyGitRepository(repositoryName, artifact, revision)
245+
g.Expect(err).NotTo(HaveOccurred())
246+
247+
const (
248+
secretName = "user-defined-name"
249+
secretKey = "user-defined-key"
250+
)
251+
kustomizationKey := types.NamespacedName{
252+
Name: randStringRunes(5),
253+
Namespace: id,
254+
}
255+
kustomization := &kustomizev1.Kustomization{
256+
ObjectMeta: metav1.ObjectMeta{
257+
Name: kustomizationKey.Name,
258+
Namespace: kustomizationKey.Namespace,
259+
},
260+
Spec: kustomizev1.KustomizationSpec{
261+
Interval: metav1.Duration{Duration: time.Minute},
262+
Path: "./",
263+
KubeConfig: &kustomizev1.KubeConfig{
264+
SecretRef: meta.SecretKeyReference{
265+
Name: secretName,
266+
Key: secretKey,
267+
},
268+
},
269+
SourceRef: kustomizev1.CrossNamespaceSourceReference{
270+
Name: repositoryName.Name,
271+
Namespace: repositoryName.Namespace,
272+
Kind: sourcev1.GitRepositoryKind,
273+
},
274+
TargetNamespace: id,
275+
Prune: true,
276+
},
277+
}
278+
279+
g.Expect(k8sClient.Create(context.Background(), kustomization)).To(Succeed())
280+
281+
resultK := &kustomizev1.Kustomization{}
282+
readyCondition := &metav1.Condition{}
283+
284+
t.Run("fails to reconcile with missing kubeconfig secret", func(t *testing.T) {
285+
g.Eventually(func() bool {
286+
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK)
287+
readyCondition = apimeta.FindStatusCondition(resultK.Status.Conditions, meta.ReadyCondition)
288+
return apimeta.IsStatusConditionFalse(resultK.Status.Conditions, meta.ReadyCondition)
289+
}, timeout, time.Second).Should(BeTrue())
290+
291+
g.Expect(readyCondition.Reason).To(Equal(kustomizev1.ReconciliationFailedReason))
292+
g.Expect(readyCondition.Message).To(ContainSubstring(`Secret "%s" not found`, secretName))
293+
})
294+
295+
secret := &corev1.Secret{
296+
ObjectMeta: metav1.ObjectMeta{
297+
Name: secretName,
298+
Namespace: id,
299+
},
300+
Data: map[string][]byte{
301+
secretKey: kubeConfig,
302+
},
303+
}
304+
g.Expect(k8sClient.Create(context.Background(), secret)).NotTo(HaveOccurred(), "failed to create kubeconfig secret")
305+
306+
t.Run("reconciles successfully after secret is created", func(t *testing.T) {
307+
revision = "v2.0.0"
308+
err = applyGitRepository(repositoryName, artifact, revision)
309+
g.Expect(err).NotTo(HaveOccurred())
310+
311+
g.Eventually(func() bool {
312+
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK)
313+
readyCondition = apimeta.FindStatusCondition(resultK.Status.Conditions, meta.ReadyCondition)
314+
return resultK.Status.LastAppliedRevision == revision
315+
}, timeout, time.Second).Should(BeTrue())
316+
317+
g.Expect(readyCondition.Reason).To(Equal(kustomizev1.ReconciliationSucceededReason))
318+
})
319+
320+
}

controllers/kustomization_inventory_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ stringData:
9797
Interval: metav1.Duration{Duration: 2 * time.Minute},
9898
Path: "./",
9999
KubeConfig: &kustomizev1.KubeConfig{
100-
SecretRef: meta.LocalObjectReference{
100+
SecretRef: meta.SecretKeyReference{
101101
Name: "kubeconfig",
102102
},
103103
},

controllers/kustomization_prune_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ data:
9898
Interval: metav1.Duration{Duration: reconciliationInterval},
9999
Path: "./",
100100
KubeConfig: &kustomizev1.KubeConfig{
101-
SecretRef: meta.LocalObjectReference{
101+
SecretRef: meta.SecretKeyReference{
102102
Name: "kubeconfig",
103103
},
104104
},
@@ -226,7 +226,7 @@ data:
226226
Interval: metav1.Duration{Duration: reconciliationInterval},
227227
Path: "./",
228228
KubeConfig: &kustomizev1.KubeConfig{
229-
SecretRef: meta.LocalObjectReference{
229+
SecretRef: meta.SecretKeyReference{
230230
Name: "kubeconfig",
231231
},
232232
},
@@ -370,7 +370,7 @@ data:
370370
Interval: metav1.Duration{Duration: reconciliationInterval},
371371
Path: "./",
372372
KubeConfig: &kustomizev1.KubeConfig{
373-
SecretRef: meta.LocalObjectReference{
373+
SecretRef: meta.SecretKeyReference{
374374
Name: "kubeconfig",
375375
},
376376
},

controllers/kustomization_transformer_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func TestKustomizationReconciler_KustomizeTransformer(t *testing.T) {
7474
Interval: metav1.Duration{Duration: reconciliationInterval},
7575
Path: "./",
7676
KubeConfig: &kustomizev1.KubeConfig{
77-
SecretRef: meta.LocalObjectReference{
77+
SecretRef: meta.SecretKeyReference{
7878
Name: "kubeconfig",
7979
},
8080
},
@@ -197,7 +197,7 @@ func TestKustomizationReconciler_KustomizeTransformerFiles(t *testing.T) {
197197
Interval: metav1.Duration{Duration: reconciliationInterval},
198198
Path: "./",
199199
KubeConfig: &kustomizev1.KubeConfig{
200-
SecretRef: meta.LocalObjectReference{
200+
SecretRef: meta.SecretKeyReference{
201201
Name: "kubeconfig",
202202
},
203203
},
@@ -316,7 +316,7 @@ func TestKustomizationReconciler_FluxTransformers(t *testing.T) {
316316
Interval: metav1.Duration{Duration: reconciliationInterval},
317317
Path: "./",
318318
KubeConfig: &kustomizev1.KubeConfig{
319-
SecretRef: meta.LocalObjectReference{
319+
SecretRef: meta.SecretKeyReference{
320320
Name: "kubeconfig",
321321
},
322322
},

controllers/kustomization_validation_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func TestKustomizationReconciler_Validation(t *testing.T) {
7979
Interval: metav1.Duration{Duration: 2 * time.Minute},
8080
Path: "./",
8181
KubeConfig: &kustomizev1.KubeConfig{
82-
SecretRef: meta.LocalObjectReference{
82+
SecretRef: meta.SecretKeyReference{
8383
Name: "kubeconfig",
8484
},
8585
},

controllers/kustomization_varsub_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ stringData:
120120
},
121121
Spec: kustomizev1.KustomizationSpec{
122122
KubeConfig: &kustomizev1.KubeConfig{
123-
SecretRef: meta.LocalObjectReference{
123+
SecretRef: meta.SecretKeyReference{
124124
Name: "kubeconfig",
125125
},
126126
},
@@ -269,7 +269,7 @@ metadata:
269269
},
270270
Spec: kustomizev1.KustomizationSpec{
271271
KubeConfig: &kustomizev1.KubeConfig{
272-
SecretRef: meta.LocalObjectReference{
272+
SecretRef: meta.SecretKeyReference{
273273
Name: "kubeconfig",
274274
},
275275
},

controllers/kustomization_wait_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ data:
8585
Interval: metav1.Duration{Duration: 2 * time.Minute},
8686
Path: "./",
8787
KubeConfig: &kustomizev1.KubeConfig{
88-
SecretRef: meta.LocalObjectReference{
88+
SecretRef: meta.SecretKeyReference{
8989
Name: "kubeconfig",
9090
},
9191
},

docs/api/kustomize.md

+5-4
Original file line numberDiff line numberDiff line change
@@ -521,14 +521,15 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference
521521
<td>
522522
<code>secretRef</code><br>
523523
<em>
524-
<a href="https://godoc.org/github.com/fluxcd/pkg/apis/meta#LocalObjectReference">
525-
github.com/fluxcd/pkg/apis/meta.LocalObjectReference
524+
<a href="https://godoc.org/github.com/fluxcd/pkg/apis/meta#SecretKeyReference">
525+
github.com/fluxcd/pkg/apis/meta.SecretKeyReference
526526
</a>
527527
</em>
528528
</td>
529529
<td>
530-
<p>SecretRef holds the name to a secret that contains a &lsquo;value&rsquo; key with
531-
the kubeconfig file as the value. It must be in the same namespace as
530+
<p>SecretRef holds the name of a secret that contains a key with
531+
the kubeconfig file as the value. If no key is set, the key will default
532+
to &lsquo;value&rsquo;. The secret must be in the same namespace as
532533
the Kustomization.
533534
It is recommended that the kubeconfig is self-contained, and the secret
534535
is regularly updated if credentials such as a cloud-access-token expire.

go.mod

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ require (
1515
github.com/fluxcd/kustomize-controller/api v0.24.4
1616
github.com/fluxcd/pkg/apis/acl v0.0.3
1717
github.com/fluxcd/pkg/apis/kustomize v0.3.3
18-
github.com/fluxcd/pkg/apis/meta v0.12.2
18+
github.com/fluxcd/pkg/apis/meta v0.13.0
1919
github.com/fluxcd/pkg/kustomize v0.3.0
20-
github.com/fluxcd/pkg/runtime v0.14.1
20+
github.com/fluxcd/pkg/runtime v0.14.2
2121
github.com/fluxcd/pkg/ssa v0.15.2
2222
github.com/fluxcd/pkg/testserver v0.2.0
2323
github.com/fluxcd/pkg/untar v0.1.0

0 commit comments

Comments
 (0)