Skip to content

Commit c40d35f

Browse files
Allow to use image overrides from mounted custerctl-config.yaml file
Signed-off-by: Danil-Grigorev <[email protected]>
1 parent 05d29b5 commit c40d35f

File tree

2 files changed

+133
-6
lines changed

2 files changed

+133
-6
lines changed
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
Copyright 2024 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 controller
18+
19+
import (
20+
"fmt"
21+
22+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
23+
"k8s.io/client-go/kubernetes/scheme"
24+
25+
appsv1 "k8s.io/api/apps/v1"
26+
corev1 "k8s.io/api/core/v1"
27+
28+
configclient "sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
29+
)
30+
31+
const (
32+
daemonSetKind = "DaemonSet"
33+
)
34+
35+
func imageOverrides(component string, overrides configclient.Client) func(objs []unstructured.Unstructured) ([]unstructured.Unstructured, error) {
36+
imageOverridesWrapper := func(objs []unstructured.Unstructured) ([]unstructured.Unstructured, error) {
37+
if overrides == nil {
38+
return objs, nil
39+
}
40+
41+
return fixImages(objs, func(image string) (string, error) {
42+
return overrides.ImageMeta().AlterImage(component, image)
43+
})
44+
}
45+
46+
return imageOverridesWrapper
47+
}
48+
49+
// fixImages alters images using the give alter func
50+
// NB. The implemented approach is specific for the provider components YAML & for the cert-manager manifest; it is not
51+
// intended to cover all the possible objects used to deploy containers existing in Kubernetes.
52+
func fixImages(objs []unstructured.Unstructured, alterImageFunc func(image string) (string, error)) ([]unstructured.Unstructured, error) {
53+
for i := range objs {
54+
if err := fixDeploymentImages(&objs[i], alterImageFunc); err != nil {
55+
return nil, err
56+
}
57+
58+
if err := fixDaemonSetImages(&objs[i], alterImageFunc); err != nil {
59+
return nil, err
60+
}
61+
}
62+
63+
return objs, nil
64+
}
65+
66+
func fixDeploymentImages(o *unstructured.Unstructured, alterImageFunc func(image string) (string, error)) error {
67+
if o.GetKind() != deploymentKind {
68+
return nil
69+
}
70+
71+
// Convert Unstructured into a typed object
72+
d := &appsv1.Deployment{}
73+
if err := scheme.Scheme.Convert(o, d, nil); err != nil {
74+
return err
75+
}
76+
77+
if err := fixPodSpecImages(&d.Spec.Template.Spec, alterImageFunc); err != nil {
78+
return fmt.Errorf("%w: failed to fix containers in deployment %s", err, d.Name)
79+
}
80+
81+
// Convert typed object back to Unstructured
82+
return scheme.Scheme.Convert(d, o, nil)
83+
}
84+
85+
func fixDaemonSetImages(o *unstructured.Unstructured, alterImageFunc func(image string) (string, error)) error {
86+
if o.GetKind() != daemonSetKind {
87+
return nil
88+
}
89+
90+
// Convert Unstructured into a typed object
91+
d := &appsv1.DaemonSet{}
92+
if err := scheme.Scheme.Convert(o, d, nil); err != nil {
93+
return err
94+
}
95+
96+
if err := fixPodSpecImages(&d.Spec.Template.Spec, alterImageFunc); err != nil {
97+
return fmt.Errorf("%w: failed to fix containers in deamonSet %s", err, d.Name)
98+
}
99+
// Convert typed object back to Unstructured
100+
return scheme.Scheme.Convert(d, o, nil)
101+
}
102+
103+
func fixPodSpecImages(podSpec *corev1.PodSpec, alterImageFunc func(image string) (string, error)) error {
104+
if err := fixContainersImage(podSpec.Containers, alterImageFunc); err != nil {
105+
return fmt.Errorf("%w: failed to fix containers", err)
106+
}
107+
108+
if err := fixContainersImage(podSpec.InitContainers, alterImageFunc); err != nil {
109+
return fmt.Errorf("%w: failed to fix init containers", err)
110+
}
111+
112+
return nil
113+
}
114+
115+
func fixContainersImage(containers []corev1.Container, alterImageFunc func(image string) (string, error)) error {
116+
for j := range containers {
117+
container := &containers[j]
118+
119+
image, err := alterImageFunc(container.Image)
120+
if err != nil {
121+
return fmt.Errorf("%w: failed to fix image for container %s", err, container.Name)
122+
}
123+
124+
container.Image = image
125+
}
126+
127+
return nil
128+
}

internal/controller/phases.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -148,12 +148,6 @@ func (p *phaseReconciler) initializePhaseReconciler(ctx context.Context) (reconc
148148
return reconcile.Result{}, err
149149
}
150150

151-
if p.overridesClient != nil {
152-
if imageOverrides, err := p.overridesClient.Variables().Get("images"); err == nil {
153-
reader.Set("images", imageOverrides)
154-
}
155-
}
156-
157151
// Load provider's secret and config url.
158152
p.configClient, err = configclient.New(ctx, "", configclient.InjectReader(reader))
159153
if err != nil {
@@ -468,6 +462,11 @@ func (p *phaseReconciler) fetch(ctx context.Context) (reconcile.Result, error) {
468462
return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason, operatorv1.ProviderInstalledCondition)
469463
}
470464

465+
// Apply image overrides to the provider manifests.
466+
if err := repository.AlterComponents(p.components, imageOverrides(p.components.ManifestLabel(), p.overridesClient)); err != nil {
467+
return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason, operatorv1.ProviderInstalledCondition)
468+
}
469+
471470
conditions.Set(p.provider, conditions.TrueCondition(operatorv1.ProviderInstalledCondition))
472471

473472
return reconcile.Result{}, nil

0 commit comments

Comments
 (0)