Skip to content

Commit 2777c81

Browse files
committed
feat: new imageRepository patch
1 parent db1bde1 commit 2777c81

File tree

9 files changed

+316
-0
lines changed

9 files changed

+316
-0
lines changed

api/v1alpha1/clusterconfig_types.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ type ClusterConfigSpec struct {
2525
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
2626
// Important: Run "make" to regenerate code after modifying this file
2727

28+
// +optional
29+
ImageRepository *ImageRepository `json:"imageRepository,omitempty"`
30+
2831
// +optional
2932
Proxy *HTTPProxy `json:"proxy,omitempty"`
3033

@@ -38,13 +41,30 @@ func (ClusterConfigSpec) VariableSchema() clusterv1.VariableSchema {
3841
Description: "Cluster configuration",
3942
Type: "object",
4043
Properties: map[string]clusterv1.JSONSchemaProps{
44+
"imageRepository": ImageRepository("").VariableSchema().OpenAPIV3Schema,
4145
"proxy": HTTPProxy{}.VariableSchema().OpenAPIV3Schema,
4246
"extraAPIServerCertSANs": ExtraAPIServerCertSANs{}.VariableSchema().OpenAPIV3Schema,
4347
},
4448
},
4549
}
4650
}
4751

52+
// ImageRepository required for overriding Kubernetes image repository.
53+
type ImageRepository string
54+
55+
func (ImageRepository) VariableSchema() clusterv1.VariableSchema {
56+
return clusterv1.VariableSchema{
57+
OpenAPIV3Schema: clusterv1.JSONSchemaProps{
58+
Description: "Sets the imageRepository used for the KubeadmControlPlane.",
59+
Type: "string",
60+
},
61+
}
62+
}
63+
64+
func (v ImageRepository) String() string {
65+
return string(v)
66+
}
67+
4868
// HTTPProxy required for providing proxy configuration.
4969
type HTTPProxy struct {
5070
// HTTP proxy.

api/v1alpha1/zz_generated.deepcopy.go

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

cmd/main.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/cni/calico"
3232
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/extraapiservercertsans"
3333
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/httpproxy"
34+
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/imagerepository"
3435
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/servicelbgc"
3536
)
3637

@@ -125,6 +126,9 @@ func main() {
125126

126127
auditpolicy.NewPatch(),
127128

129+
imagerepository.NewVariable(),
130+
imagerepository.NewPatch(imagerepository.VariableName),
131+
128132
clusterconfig.NewVariable(),
129133
mutation.NewMetaGeneratePatchesHandler(
130134
"clusterConfigPatch",
@@ -134,6 +138,7 @@ func main() {
134138
extraapiservercertsans.VariableName,
135139
),
136140
auditpolicy.NewPatch(),
141+
imagerepository.NewPatch(clusterconfig.VariableName, imagerepository.VariableName),
137142
),
138143
)
139144
if err := mgr.Add(runtimeWebhookServer); err != nil {

docs/content/cluster-config.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ spec:
3535
variables:
3636
- name: clusterConfig
3737
value:
38+
imageRepository: "my-registry.io/my-org/my-repo"
3839
extraAPIServerCertSANs:
3940
- a.b.c.example.com
4041
- d.e.f.example.com

docs/content/image-repository.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
title: "Image Repository"
3+
---
4+
5+
Override the container image repository used when pulling Kubernetes images.
6+
7+
To enable the API server certificate SANs enable the `extraapiservercertsansvars` and `extraapiservercertsanspatch`
8+
external patches on `ClusterClass`.
9+
10+
```yaml
11+
apiVersion: cluster.x-k8s.io/v1beta1
12+
kind: ClusterClass
13+
metadata:
14+
name: <NAME>
15+
spec:
16+
patches:
17+
- name: apiserver-cert-sans
18+
external:
19+
generateExtension: "imagerepositorypatch.capi-runtime-extensions"
20+
discoverVariablesExtension: "imagerepositoryvars.capi-runtime-extensions"
21+
```
22+
23+
On the cluster resource then specify desired certificate SANs values:
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: imageRepository
34+
value: "my-registry.io/my-org/my-repo"
35+
```
36+
37+
Applying this configuration will result in the following value being set:
38+
39+
- KubeadmControlPlaneTemplate:
40+
- `/spec/template/spec/kubeadmConfigSpec/clusterConfiguration/imageRepository: my-registry.io/my-org/my-repo`
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// Copyright 2023 D2iQ, Inc. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package imagerepository
5+
6+
import (
7+
"context"
8+
_ "embed"
9+
10+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
11+
"k8s.io/apimachinery/pkg/runtime"
12+
"k8s.io/apimachinery/pkg/runtime/serializer"
13+
bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1"
14+
controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1"
15+
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
16+
"sigs.k8s.io/cluster-api/exp/runtime/topologymutation"
17+
ctrl "sigs.k8s.io/controller-runtime"
18+
"sigs.k8s.io/controller-runtime/pkg/client"
19+
20+
"github.com/d2iq-labs/capi-runtime-extensions/api/v1alpha1"
21+
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/handlers"
22+
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/handlers/mutation"
23+
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/patches"
24+
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/patches/selectors"
25+
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/variables"
26+
)
27+
28+
const (
29+
// HandlerNamePatch is the name of the inject handler.
30+
HandlerNamePatch = "ImageRepositoryPatch"
31+
)
32+
33+
type imageRepositoryPatchHandler struct {
34+
decoder runtime.Decoder
35+
variableName string
36+
variableFieldPath []string
37+
}
38+
39+
var (
40+
_ handlers.Named = &imageRepositoryPatchHandler{}
41+
_ mutation.GeneratePatches = &imageRepositoryPatchHandler{}
42+
)
43+
44+
func NewPatch(
45+
variableName string,
46+
variableFieldPath ...string,
47+
) *imageRepositoryPatchHandler {
48+
scheme := runtime.NewScheme()
49+
_ = bootstrapv1.AddToScheme(scheme)
50+
_ = controlplanev1.AddToScheme(scheme)
51+
return &imageRepositoryPatchHandler{
52+
decoder: serializer.NewCodecFactory(scheme).UniversalDecoder(
53+
controlplanev1.GroupVersion,
54+
bootstrapv1.GroupVersion,
55+
),
56+
variableName: variableName,
57+
variableFieldPath: variableFieldPath,
58+
}
59+
}
60+
61+
func (h *imageRepositoryPatchHandler) Name() string {
62+
return HandlerNamePatch
63+
}
64+
65+
func (h *imageRepositoryPatchHandler) GeneratePatches(
66+
ctx context.Context,
67+
req *runtimehooksv1.GeneratePatchesRequest,
68+
resp *runtimehooksv1.GeneratePatchesResponse,
69+
) {
70+
topologymutation.WalkTemplates(
71+
ctx,
72+
h.decoder,
73+
req,
74+
resp,
75+
func(
76+
ctx context.Context,
77+
obj runtime.Object,
78+
vars map[string]apiextensionsv1.JSON,
79+
holderRef runtimehooksv1.HolderReference,
80+
) error {
81+
log := ctrl.LoggerFrom(ctx).WithValues(
82+
"holderRef", holderRef,
83+
)
84+
85+
imageRepositoryVar, found, err := variables.Get[v1alpha1.ImageRepository](
86+
vars,
87+
h.variableName,
88+
h.variableFieldPath...,
89+
)
90+
if err != nil {
91+
return err
92+
}
93+
if !found {
94+
log.V(5).Info("imageRepository variable not defined")
95+
return nil
96+
}
97+
98+
log = log.WithValues(
99+
"variableName",
100+
h.variableName,
101+
"variableFieldPath",
102+
h.variableFieldPath,
103+
"variableValue",
104+
imageRepositoryVar,
105+
)
106+
107+
return patches.Generate(
108+
obj, vars, &holderRef, selectors.ControlPlane(), log,
109+
func(obj *controlplanev1.KubeadmControlPlaneTemplate) error {
110+
log.WithValues(
111+
"patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(),
112+
"patchedObjectName", client.ObjectKeyFromObject(obj),
113+
).Info("setting imageRepository in kubeadm config spec")
114+
115+
if obj.Spec.Template.Spec.KubeadmConfigSpec.ClusterConfiguration == nil {
116+
obj.Spec.Template.Spec.KubeadmConfigSpec.ClusterConfiguration = &bootstrapv1.ClusterConfiguration{}
117+
}
118+
obj.Spec.Template.Spec.KubeadmConfigSpec.ClusterConfiguration.ImageRepository = imageRepositoryVar.String()
119+
120+
return nil
121+
},
122+
)
123+
},
124+
)
125+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright 2023 D2iQ, Inc. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package imagerepository
5+
6+
import (
7+
"testing"
8+
9+
. "github.com/onsi/gomega"
10+
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
11+
12+
"github.com/d2iq-labs/capi-runtime-extensions/api/v1alpha1"
13+
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/handlers/mutation"
14+
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/testutils/capitest"
15+
)
16+
17+
func TestGeneratePatches(t *testing.T) {
18+
capitest.ValidateGeneratePatches(
19+
t,
20+
func() mutation.GeneratePatches { return NewPatch(VariableName) },
21+
capitest.PatchTestDef{
22+
Name: "unset variable",
23+
},
24+
capitest.PatchTestDef{
25+
Name: "imageRepository set",
26+
Vars: []runtimehooksv1.Variable{
27+
capitest.VariableWithValue(
28+
VariableName,
29+
v1alpha1.ImageRepository("my-registry.io/my-org/my-repo"),
30+
),
31+
},
32+
RequestItem: capitest.NewKubeadmControlPlaneTemplateRequestItem(),
33+
ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{
34+
Operation: "add",
35+
Path: "/spec/template/spec/kubeadmConfigSpec/clusterConfiguration",
36+
ValueMatcher: HaveKeyWithValue(
37+
"imageRepository",
38+
"my-registry.io/my-org/my-repo",
39+
),
40+
}},
41+
},
42+
)
43+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2023 D2iQ, Inc. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package imagerepository
5+
6+
import (
7+
"context"
8+
9+
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
10+
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
11+
12+
"github.com/d2iq-labs/capi-runtime-extensions/api/v1alpha1"
13+
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/handlers"
14+
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/handlers/mutation"
15+
)
16+
17+
var (
18+
_ handlers.Named = &imageRepositoryVariableHandler{}
19+
_ mutation.DiscoverVariables = &imageRepositoryVariableHandler{}
20+
)
21+
22+
const (
23+
// VariableName is http proxy external patch variable name.
24+
VariableName = "imageRepository"
25+
26+
// HandlerNameVariable is the name of the variable handler.
27+
HandlerNameVariable = "ImageRepositoryVars"
28+
)
29+
30+
func NewVariable() *imageRepositoryVariableHandler {
31+
return &imageRepositoryVariableHandler{}
32+
}
33+
34+
type imageRepositoryVariableHandler struct{}
35+
36+
func (h *imageRepositoryVariableHandler) Name() string {
37+
return HandlerNameVariable
38+
}
39+
40+
func (h *imageRepositoryVariableHandler) DiscoverVariables(
41+
ctx context.Context,
42+
_ *runtimehooksv1.DiscoverVariablesRequest,
43+
resp *runtimehooksv1.DiscoverVariablesResponse,
44+
) {
45+
resp.Variables = append(resp.Variables, clusterv1.ClusterClassVariable{
46+
Name: VariableName,
47+
Required: false,
48+
Schema: v1alpha1.ImageRepository("").VariableSchema(),
49+
})
50+
resp.SetStatus(runtimehooksv1.ResponseStatusSuccess)
51+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2023 D2iQ, Inc. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package imagerepository
5+
6+
import (
7+
"testing"
8+
9+
"k8s.io/utils/ptr"
10+
11+
"github.com/d2iq-labs/capi-runtime-extensions/api/v1alpha1"
12+
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/testutils/capitest"
13+
)
14+
15+
func TestVariableValidation(t *testing.T) {
16+
capitest.ValidateDiscoverVariables(
17+
t,
18+
VariableName,
19+
ptr.To(v1alpha1.ImageRepository("").VariableSchema()),
20+
NewVariable,
21+
capitest.VariableTestDef{
22+
Name: "set",
23+
Vals: "my-registry.io/my-org/my-repo",
24+
},
25+
)
26+
}

0 commit comments

Comments
 (0)