Skip to content

Commit f207d9a

Browse files
committed
feat: read kube-vip template from KCPTemplate
To correctly support kube-vip version upgrades without an unplanned control-plane rollout, read the kube-vip template directly from the KCPTemplate that will be released along with the ClusterClasses.
1 parent 26f7794 commit f207d9a

File tree

10 files changed

+474
-284
lines changed

10 files changed

+474
-284
lines changed

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

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,18 +83,26 @@ func NewKubeadmConfigTemplateRequest(
8383
)
8484
}
8585

86-
func NewKubeadmControlPlaneTemplateRequest(
87-
uid types.UID,
88-
name string,
89-
) runtimehooksv1.GeneratePatchesRequestItem {
86+
type KubeadmControlPlaneTemplateRequestItemBuilder struct {
87+
files []bootstrapv1.File
88+
}
89+
90+
func (b *KubeadmControlPlaneTemplateRequestItemBuilder) WithFiles(
91+
files ...bootstrapv1.File,
92+
) *KubeadmControlPlaneTemplateRequestItemBuilder {
93+
b.files = files
94+
return b
95+
}
96+
97+
func (b *KubeadmControlPlaneTemplateRequestItemBuilder) NewRequest(uid types.UID) runtimehooksv1.GeneratePatchesRequestItem {
9098
return NewRequestItem(
9199
&controlplanev1.KubeadmControlPlaneTemplate{
92100
TypeMeta: metav1.TypeMeta{
93101
APIVersion: controlplanev1.GroupVersion.String(),
94102
Kind: "KubeadmControlPlaneTemplate",
95103
},
96104
ObjectMeta: metav1.ObjectMeta{
97-
Name: name,
105+
Name: kubeadmControlPlaneTemplateRequestObjectName,
98106
Namespace: Namespace,
99107
},
100108
Spec: controlplanev1.KubeadmControlPlaneTemplateSpec{
@@ -107,6 +115,7 @@ func NewKubeadmControlPlaneTemplateRequest(
107115
JoinConfiguration: &bootstrapv1.JoinConfiguration{
108116
NodeRegistration: bootstrapv1.NodeRegistrationOptions{},
109117
},
118+
Files: b.files,
110119
},
111120
},
112121
},
@@ -126,7 +135,8 @@ func NewKubeadmControlPlaneTemplateRequest(
126135
func NewKubeadmControlPlaneTemplateRequestItem(
127136
uid types.UID,
128137
) runtimehooksv1.GeneratePatchesRequestItem {
129-
return NewKubeadmControlPlaneTemplateRequest(uid, kubeadmControlPlaneTemplateRequestObjectName)
138+
builder := &KubeadmControlPlaneTemplateRequestItemBuilder{}
139+
return builder.NewRequest(uid)
130140
}
131141

132142
func NewCPDockerMachineTemplateRequestItem(

pkg/handlers/generic/mutation/controlplanevirtualip/inject.go

Lines changed: 50 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ package controlplanevirtualip
66
import (
77
"context"
88
"fmt"
9+
"slices"
910

10-
"github.com/spf13/pflag"
1111
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
1212
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
13+
bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1"
1314
controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1"
1415
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
1516
ctrl "sigs.k8s.io/controller-runtime"
@@ -31,23 +32,9 @@ const (
3132

3233
type Config struct {
3334
*options.GlobalOptions
34-
35-
defaultKubeVIPConfigMapName string
36-
}
37-
38-
func (c *Config) AddFlags(prefix string, flags *pflag.FlagSet) {
39-
flags.StringVar(
40-
&c.defaultKubeVIPConfigMapName,
41-
prefix+".default-kube-vip-template-configmap-name",
42-
"default-kube-vip-template",
43-
"default ConfigMap name that holds the kube-vip template used for the control-plane virtual IP",
44-
)
4535
}
4636

4737
type ControlPlaneVirtualIP struct {
48-
client client.Reader
49-
config *Config
50-
5138
variableName string
5239
variableFieldPath []string
5340
}
@@ -56,14 +43,10 @@ type ControlPlaneVirtualIP struct {
5643
// It requires variableName and variableFieldPath to be passed from another provider specific handler.
5744
// The code is here to be shared across different providers.
5845
func NewControlPlaneVirtualIP(
59-
cl client.Reader,
60-
config *Config,
6146
variableName string,
6247
variableFieldPath ...string,
6348
) *ControlPlaneVirtualIP {
6449
return &ControlPlaneVirtualIP{
65-
client: cl,
66-
config: config,
6750
variableName: variableName,
6851
variableFieldPath: variableFieldPath,
6952
}
@@ -103,11 +86,6 @@ func (h *ControlPlaneVirtualIP) Mutate(
10386
controlPlaneEndpointVar,
10487
)
10588

106-
if controlPlaneEndpointVar.VirtualIPSpec == nil {
107-
log.V(5).Info("ControlPlane VirtualIP not set")
108-
return nil
109-
}
110-
11189
cluster, err := clusterGetter(ctx)
11290
if err != nil {
11391
log.Error(
@@ -117,23 +95,27 @@ func (h *ControlPlaneVirtualIP) Mutate(
11795
return err
11896
}
11997

120-
var virtualIPProvider providers.Provider
121-
// only kube-vip is supported, but more providers can be added in the future
122-
if controlPlaneEndpointVar.VirtualIPSpec.Provider == v1alpha1.VirtualIPProviderKubeVIP {
123-
virtualIPProvider = providers.NewKubeVIPFromConfigMapProvider(
124-
h.client,
125-
h.config.defaultKubeVIPConfigMapName,
126-
h.config.DefaultsNamespace(),
127-
)
128-
}
129-
13098
return patches.MutateIfApplicable(
13199
obj,
132100
vars,
133101
&holderRef,
134102
selectors.ControlPlane(),
135103
log,
136104
func(obj *controlplanev1.KubeadmControlPlaneTemplate) error {
105+
if controlPlaneEndpointVar.VirtualIPSpec == nil {
106+
log.V(5).Info("ControlPlane VirtualIP not set")
107+
// if VirtualIPSpec is not set, delete all template files
108+
// as we do not want them to end up in the generated KCP
109+
deleteFiles(obj, providers.TemplateFileNames...)
110+
return nil
111+
}
112+
113+
var virtualIPProvider providers.Provider
114+
// only kube-vip is supported, but more providers can be added in the future
115+
if controlPlaneEndpointVar.VirtualIPSpec.Provider == v1alpha1.VirtualIPProviderKubeVIP {
116+
virtualIPProvider = providers.NewKubeVIPFromKCPTemplateProvider(obj)
117+
}
118+
137119
files, preKubeadmCommands, postKubeadmCommands, generateErr := virtualIPProvider.GenerateFilesAndCommands(
138120
ctx,
139121
controlPlaneEndpointVar,
@@ -150,10 +132,8 @@ func (h *ControlPlaneVirtualIP) Mutate(
150132
"adding %s static Pod file to control plane kubeadm config spec",
151133
virtualIPProvider.Name(),
152134
))
153-
obj.Spec.Template.Spec.KubeadmConfigSpec.Files = append(
154-
obj.Spec.Template.Spec.KubeadmConfigSpec.Files,
155-
files...,
156-
)
135+
136+
mergeFiles(obj, files...)
157137

158138
if len(preKubeadmCommands) > 0 {
159139
log.WithValues(
@@ -187,3 +167,35 @@ func (h *ControlPlaneVirtualIP) Mutate(
187167
},
188168
)
189169
}
170+
171+
func deleteFiles(obj *controlplanev1.KubeadmControlPlaneTemplate, filePathsToDelete ...string) {
172+
for i := len(obj.Spec.Template.Spec.KubeadmConfigSpec.Files) - 1; i >= 0; i-- {
173+
for _, path := range filePathsToDelete {
174+
if obj.Spec.Template.Spec.KubeadmConfigSpec.Files[i].Path == path {
175+
obj.Spec.Template.Spec.KubeadmConfigSpec.Files =
176+
slices.Delete(obj.Spec.Template.Spec.KubeadmConfigSpec.Files, i, i+1)
177+
break
178+
}
179+
}
180+
}
181+
}
182+
183+
// mergeFiles will merge the files into the KubeadmControlPlaneTemplate,
184+
// overriding any file with the same path and appending the rest.
185+
func mergeFiles(obj *controlplanev1.KubeadmControlPlaneTemplate, filesToMerge ...bootstrapv1.File) {
186+
// replace any existing files with the same path
187+
for i := len(filesToMerge) - 1; i >= 0; i-- {
188+
for j := range obj.Spec.Template.Spec.KubeadmConfigSpec.Files {
189+
if obj.Spec.Template.Spec.KubeadmConfigSpec.Files[j].Path == filesToMerge[i].Path {
190+
obj.Spec.Template.Spec.KubeadmConfigSpec.Files[j] = filesToMerge[i]
191+
filesToMerge = slices.Delete(filesToMerge, i, i+1)
192+
break
193+
}
194+
}
195+
}
196+
// append the remaining files
197+
obj.Spec.Template.Spec.KubeadmConfigSpec.Files = append(
198+
obj.Spec.Template.Spec.KubeadmConfigSpec.Files,
199+
filesToMerge...,
200+
)
201+
}

0 commit comments

Comments
 (0)