Skip to content

Commit e20b632

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

File tree

2 files changed

+125
-6
lines changed

2 files changed

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

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)