Skip to content

Commit 853d3a8

Browse files
dkoshkinjimmidyson
andauthored
feat: add imageRegistryCredentials handler (#174)
Co-authored-by: Jimmi Dyson <jimmidyson@gmail.com>
1 parent e447ee6 commit 853d3a8

35 files changed

+2140
-7
lines changed

api/v1alpha1/clusterconfig_types.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package v1alpha1
55

66
import (
7+
corev1 "k8s.io/api/core/v1"
78
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
89

910
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/variables"
@@ -28,6 +29,9 @@ type GenericClusterConfig struct {
2829
// +optional
2930
ExtraAPIServerCertSANs ExtraAPIServerCertSANs `json:"extraAPIServerCertSANs,omitempty"`
3031

32+
// +optional
33+
ImageRegistries ImageRegistries `json:"imageRegistries,omitempty"`
34+
3135
// +optional
3236
Addons *Addons `json:"addons,omitempty"`
3337
}
@@ -46,6 +50,7 @@ func (GenericClusterConfig) VariableSchema() clusterv1.VariableSchema {
4650
"",
4751
).VariableSchema().
4852
OpenAPIV3Schema,
53+
"imageRegistries": ImageRegistries{}.VariableSchema().OpenAPIV3Schema,
4954
},
5055
},
5156
}
@@ -175,6 +180,84 @@ func (ExtraAPIServerCertSANs) VariableSchema() clusterv1.VariableSchema {
175180
}
176181
}
177182

183+
type ImageRegistries struct {
184+
// +optional
185+
ImageRegistryCredentials ImageRegistryCredentials `json:"credentials,omitempty"`
186+
}
187+
188+
func (ImageRegistries) VariableSchema() clusterv1.VariableSchema {
189+
return clusterv1.VariableSchema{
190+
OpenAPIV3Schema: clusterv1.JSONSchemaProps{
191+
Description: "Configuration for image registries.",
192+
Type: "object",
193+
Properties: map[string]clusterv1.JSONSchemaProps{
194+
"credentials": ImageRegistryCredentials{}.VariableSchema().OpenAPIV3Schema,
195+
},
196+
},
197+
}
198+
}
199+
200+
type ImageRegistryCredentials []ImageRegistryCredentialsResource
201+
202+
func (ImageRegistryCredentials) VariableSchema() clusterv1.VariableSchema {
203+
resourceSchema := ImageRegistryCredentialsResource{}.VariableSchema().OpenAPIV3Schema
204+
205+
return clusterv1.VariableSchema{
206+
OpenAPIV3Schema: clusterv1.JSONSchemaProps{
207+
Description: "Image registry credentials to set up on all Nodes in the cluster. " +
208+
"Enabling this will configure the Kubelets with " +
209+
"https://kubernetes.io/docs/tasks/administer-cluster/kubelet-credential-provider/.",
210+
Type: "array",
211+
Items: &resourceSchema,
212+
},
213+
}
214+
}
215+
216+
// ImageRegistryCredentialsResource required for providing credentials for an image registry URL.
217+
type ImageRegistryCredentialsResource struct {
218+
// Registry URL.
219+
URL string `json:"url"`
220+
221+
// The Secret containing the registry credentials.
222+
// The Secret should have keys 'username' and 'password'.
223+
// This credentials Secret is not required for some registries, e.g. ECR.
224+
// +optional
225+
Secret *corev1.ObjectReference `json:"secretRef,omitempty"`
226+
}
227+
228+
func (ImageRegistryCredentialsResource) VariableSchema() clusterv1.VariableSchema {
229+
return clusterv1.VariableSchema{
230+
OpenAPIV3Schema: clusterv1.JSONSchemaProps{
231+
Type: "object",
232+
Properties: map[string]clusterv1.JSONSchemaProps{
233+
"url": {
234+
Description: "Registry URL.",
235+
Type: "string",
236+
},
237+
"secretRef": {
238+
Description: "The Secret containing the registry credentials. " +
239+
"The Secret should have keys 'username' and 'password'. " +
240+
"This credentials Secret is not required for some registries, e.g. ECR.",
241+
Type: "object",
242+
Properties: map[string]clusterv1.JSONSchemaProps{
243+
"name": {
244+
Description: "The name of the Secret containing the registry credentials.",
245+
Type: "string",
246+
},
247+
"namespace": {
248+
Description: "The namespace of the Secret containing the registry credentials. " +
249+
"Defaults to the namespace of the KubeadmControlPlaneTemplate and KubeadmConfigTemplate" +
250+
" that reference this variable.",
251+
Type: "string",
252+
},
253+
},
254+
},
255+
},
256+
Required: []string{"url"},
257+
},
258+
}
259+
}
260+
178261
type Addons struct {
179262
// +optional
180263
CNI *CNI `json:"cni,omitempty"`

api/v1alpha1/doc.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,4 @@
44
// Package v1alpha1 contains API Schema definitions for the CAPI extensions v1alpha1 API group
55
// +kubebuilder:object:generate=true
66
// +groupName=capiext.labs.d2iq.io
7-
//
8-
//go:generate -command CTRLGEN controller-gen paths="./..."
9-
//go:generate CTRLGEN rbac:headerFile="../../hack/license-header.yaml.txt",roleName=capi-runtime-extensions-manager-role output:rbac:artifacts:config=../../charts/capi-runtime-extensions/templates
10-
//go:generate CTRLGEN object:headerFile="../../hack/license-header.go.txt" output:object:artifacts:config=/dev/null
117
package v1alpha1

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 65 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

charts/capi-runtime-extensions/templates/role.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
apiVersion: rbac.authorization.k8s.io/v1
55
kind: ClusterRole
66
metadata:
7-
name: capi-runtime-extensions-manager-role
7+
name: {{ include "chart.name" . }}-manager-role
88
rules:
99
- apiGroups:
1010
- ""
@@ -23,8 +23,11 @@ rules:
2323
resources:
2424
- secrets
2525
verbs:
26+
- create
2627
- get
2728
- list
29+
- patch
30+
- update
2831
- watch
2932
- apiGroups:
3033
- addons.cluster.x-k8s.io

common/pkg/testutils/capitest/patches.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package capitest
66
import (
77
"context"
88
"encoding/json"
9+
"fmt"
910
"testing"
1011

1112
"github.com/onsi/gomega"
@@ -24,6 +25,7 @@ type PatchTestDef struct {
2425
Vars []runtimehooksv1.Variable
2526
RequestItem runtimehooksv1.GeneratePatchesRequestItem
2627
ExpectedPatchMatchers []JSONPatchMatcher
28+
ExpectedFailure bool
2729
}
2830

2931
type JSONPatchMatcher struct {
@@ -53,7 +55,12 @@ func ValidateGeneratePatches[T mutation.GeneratePatches](
5355
}
5456
resp := &runtimehooksv1.GeneratePatchesResponse{}
5557
h.GeneratePatches(context.Background(), req, resp)
56-
g.Expect(resp.Status).To(gomega.Equal(runtimehooksv1.ResponseStatusSuccess))
58+
expectedStatus := runtimehooksv1.ResponseStatusSuccess
59+
if tt.ExpectedFailure {
60+
expectedStatus = runtimehooksv1.ResponseStatusFailure
61+
}
62+
g.Expect(resp.Status).
63+
To(gomega.Equal(expectedStatus), fmt.Sprintf("Message: %s", resp.Message))
5764

5865
if len(tt.ExpectedPatchMatchers) == 0 {
5966
g.Expect(resp.Items).To(gomega.BeEmpty())
@@ -101,6 +108,7 @@ func VariableWithValue(
101108

102109
for _, p := range variablePath[:len(variablePath)-1] {
103110
nestedValue[p] = make(map[string]any, 1)
111+
nestedValue = nestedValue[p].(map[string]any)
104112
}
105113

106114
nestedValue[variablePath[len(variablePath)-1]] = value

common/pkg/testutils/capitest/request/items.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package request
55

66
import (
7+
corev1 "k8s.io/api/core/v1"
78
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
89
"k8s.io/apimachinery/pkg/runtime"
910
"k8s.io/apimachinery/pkg/types"
@@ -16,6 +17,13 @@ import (
1617
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/testutils/capitest/serializer"
1718
)
1819

20+
const (
21+
ClusterName = "test-cluster"
22+
KubeadmConfigTemplateRequestObjectName = "test-kubeadmconfigtemplate"
23+
KubeadmControlPlaneTemplateRequestObjectName = "test-kubeadmcontrolplanetemplate"
24+
Namespace = corev1.NamespaceDefault
25+
)
26+
1927
// NewRequestItem returns a GeneratePatchesRequestItem with the given variables and object.
2028
func NewRequestItem(
2129
object runtime.Object,
@@ -42,10 +50,17 @@ func NewKubeadmConfigTemplateRequestItem(uid types.UID) runtimehooksv1.GenerateP
4250
APIVersion: bootstrapv1.GroupVersion.String(),
4351
Kind: "KubeadmConfigTemplate",
4452
},
53+
ObjectMeta: metav1.ObjectMeta{
54+
Name: KubeadmConfigTemplateRequestObjectName,
55+
Namespace: Namespace,
56+
},
4557
Spec: bootstrapv1.KubeadmConfigTemplateSpec{
4658
Template: bootstrapv1.KubeadmConfigTemplateResource{
4759
Spec: bootstrapv1.KubeadmConfigSpec{
4860
PostKubeadmCommands: []string{"initial-post-kubeadm"},
61+
JoinConfiguration: &bootstrapv1.JoinConfiguration{
62+
NodeRegistration: bootstrapv1.NodeRegistrationOptions{},
63+
},
4964
},
5065
},
5166
},
@@ -67,10 +82,30 @@ func NewKubeadmControlPlaneTemplateRequestItem(
6782
APIVersion: controlplanev1.GroupVersion.String(),
6883
Kind: "KubeadmControlPlaneTemplate",
6984
},
85+
ObjectMeta: metav1.ObjectMeta{
86+
Name: KubeadmControlPlaneTemplateRequestObjectName,
87+
Namespace: Namespace,
88+
},
89+
Spec: controlplanev1.KubeadmControlPlaneTemplateSpec{
90+
Template: controlplanev1.KubeadmControlPlaneTemplateResource{
91+
Spec: controlplanev1.KubeadmControlPlaneTemplateResourceSpec{
92+
KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
93+
InitConfiguration: &bootstrapv1.InitConfiguration{
94+
NodeRegistration: bootstrapv1.NodeRegistrationOptions{},
95+
},
96+
JoinConfiguration: &bootstrapv1.JoinConfiguration{
97+
NodeRegistration: bootstrapv1.NodeRegistrationOptions{},
98+
},
99+
},
100+
},
101+
},
102+
},
70103
},
71104
&runtimehooksv1.HolderReference{
72105
Kind: "Cluster",
73106
FieldPath: "spec.controlPlaneRef",
107+
Name: ClusterName,
108+
Namespace: Namespace,
74109
},
75110
uid,
76111
)

docs/content/addons/calico-cni.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,6 @@ configure defaults specific for their environment rather than compiling the defa
3838
The Helm chart comes with default configurations for the Calico Installation CRS per supported provider, but overriding
3939
is possible. To do so, specify:
4040
41-
```bash
41+
```shell
4242
--set-file handlers.CalicoCNI.defaultInstallationConfigMaps.DockerCluster.configMap.content=<file>
4343
```

docs/content/customization/_index.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ spec:
3939
additionalNo:
4040
- no-proxy-1.example.com
4141
- no-proxy-2.example.com
42+
imageRegistries:
43+
credentials:
44+
- url: https://my-registry.io
45+
secretRef:
46+
name: my-registry-credentials
4247
cni:
4348
provider: calico
4449
```
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
+++
2+
title = "Image registries"
3+
+++
4+
5+
Add image registry configuration to all Nodes in the cluster.
6+
7+
When the `credentials` variable is set, `files` and `preKubeadmnCommands` with configurations for
8+
[Kubelet image credential provider](https://kubernetes.io/docs/tasks/administer-cluster/kubelet-credential-provider/)
9+
and [dynamic credential provider](https://github.com/mesosphere/dynamic-credential-provider) will be added.
10+
11+
This customization will be available when the
12+
[provider-specific cluster configuration patch]({{< ref "..">}}) is included in the `ClusterClass`.
13+
14+
## Example
15+
16+
If your registry requires static credentials, create a Kubernetes Secret with keys for `username` and `password`:
17+
18+
```shell
19+
kubectl create secret generic my-registry-credentials \
20+
--from-literal username=${REGISTRY_USERNAME} --from-literal password=${REGISTRY_PASSWORD}
21+
```
22+
23+
To add image registry credentials, specify the following configuration:
24+
25+
```yaml
26+
apiVersion: cluster.x-k8s.io/v1beta1
27+
kind: Cluster
28+
metadata:
29+
name: <NAME>
30+
spec:
31+
topology:
32+
variables:
33+
- name: clusterConfig
34+
value:
35+
imageRegistries:
36+
credentials:
37+
- url: https://my-registry.io
38+
secretRef:
39+
name: my-registry-credentials
40+
```
41+
42+
Applying this configuration will result in new files and preKubeadmCommands
43+
on the `KubeadmControlPlaneTemplate` and `KubeadmConfigTemplate`.

0 commit comments

Comments
 (0)