Skip to content

Commit acd0ff3

Browse files
author
Yuvaraj Kakaraparthi
committed
KCP: add KubeadmControlPlaneTemplate type
1 parent 37c862b commit acd0ff3

13 files changed

+1751
-229
lines changed

controlplane/kubeadm/PROJECT

+3
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,6 @@ resources:
88
- group: controlplane
99
version: v1alpha4
1010
kind: KubeadmControlPlane
11+
- group: controlplane
12+
kind: KubeadmControlPlaneTemplate
13+
version: v1alpha4

controlplane/kubeadm/api/v1alpha4/kubeadm_control_plane_webhook.go

+6-224
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,14 @@ package v1alpha4
1919
import (
2020
"encoding/json"
2121
"fmt"
22-
"strings"
2322

2423
"github.com/blang/semver"
2524
"github.com/coredns/corefile-migration/migration"
2625
jsonpatch "github.com/evanphx/json-patch"
2726
"github.com/pkg/errors"
2827
apierrors "k8s.io/apimachinery/pkg/api/errors"
2928
"k8s.io/apimachinery/pkg/runtime"
30-
"k8s.io/apimachinery/pkg/util/intstr"
3129
"k8s.io/apimachinery/pkg/util/validation/field"
32-
"sigs.k8s.io/cluster-api/util/container"
3330
"sigs.k8s.io/cluster-api/util/version"
3431
ctrl "sigs.k8s.io/controller-runtime"
3532
"sigs.k8s.io/controller-runtime/pkg/webhook"
@@ -49,47 +46,17 @@ var _ webhook.Validator = &KubeadmControlPlane{}
4946

5047
// Default implements webhook.Defaulter so a webhook will be registered for the type.
5148
func (in *KubeadmControlPlane) Default() {
52-
if in.Spec.Replicas == nil {
53-
replicas := int32(1)
54-
in.Spec.Replicas = &replicas
55-
}
56-
57-
if in.Spec.MachineTemplate.InfrastructureRef.Namespace == "" {
58-
in.Spec.MachineTemplate.InfrastructureRef.Namespace = in.Namespace
59-
}
60-
61-
if !strings.HasPrefix(in.Spec.Version, "v") {
62-
in.Spec.Version = "v" + in.Spec.Version
63-
}
64-
65-
ios1 := intstr.FromInt(1)
66-
67-
if in.Spec.RolloutStrategy == nil {
68-
in.Spec.RolloutStrategy = &RolloutStrategy{}
69-
}
70-
71-
// Enforce RollingUpdate strategy and default MaxSurge if not set.
72-
if in.Spec.RolloutStrategy != nil {
73-
if len(in.Spec.RolloutStrategy.Type) == 0 {
74-
in.Spec.RolloutStrategy.Type = RollingUpdateStrategyType
75-
}
76-
if in.Spec.RolloutStrategy.Type == RollingUpdateStrategyType {
77-
if in.Spec.RolloutStrategy.RollingUpdate == nil {
78-
in.Spec.RolloutStrategy.RollingUpdate = &RollingUpdate{}
79-
}
80-
in.Spec.RolloutStrategy.RollingUpdate.MaxSurge = intstr.ValueOrDefault(in.Spec.RolloutStrategy.RollingUpdate.MaxSurge, ios1)
81-
}
82-
}
49+
in.Spec.setDefaults(in.Namespace)
8350
}
8451

8552
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type.
8653
func (in *KubeadmControlPlane) ValidateCreate() error {
87-
allErrs := in.validateCommon()
88-
allErrs = append(allErrs, in.validateEtcd(nil)...)
54+
spec := in.Spec
55+
allErrs := spec.validate(in.Namespace)
56+
allErrs = append(allErrs, spec.validateEtcd(nil)...)
8957
if len(allErrs) > 0 {
9058
return apierrors.NewInvalid(GroupVersion.WithKind("KubeadmControlPlane").GroupKind(), in.Name, allErrs)
9159
}
92-
9360
return nil
9461
}
9562

@@ -141,7 +108,7 @@ func (in *KubeadmControlPlane) ValidateUpdate(old runtime.Object) error {
141108
{spec, "rolloutStrategy", "*"},
142109
}
143110

144-
allErrs := in.validateCommon()
111+
allErrs := in.Spec.validate(in.Namespace)
145112

146113
prev := old.(*KubeadmControlPlane)
147114

@@ -180,7 +147,7 @@ func (in *KubeadmControlPlane) ValidateUpdate(old runtime.Object) error {
180147
}
181148

182149
allErrs = append(allErrs, in.validateVersion(prev.Spec.Version)...)
183-
allErrs = append(allErrs, in.validateEtcd(prev)...)
150+
allErrs = append(allErrs, in.Spec.validateEtcd(&prev.Spec)...)
184151
allErrs = append(allErrs, in.validateCoreDNSVersion(prev)...)
185152

186153
if len(allErrs) > 0 {
@@ -236,139 +203,6 @@ func paths(path []string, diff map[string]interface{}) [][]string {
236203
return allPaths
237204
}
238205

239-
func (in *KubeadmControlPlane) validateCommon() (allErrs field.ErrorList) {
240-
if in.Spec.Replicas == nil {
241-
allErrs = append(
242-
allErrs,
243-
field.Required(
244-
field.NewPath("spec", "replicas"),
245-
"is required",
246-
),
247-
)
248-
} else if *in.Spec.Replicas <= 0 {
249-
// The use of the scale subresource should provide a guarantee that negative values
250-
// should not be accepted for this field, but since we have to validate that Replicas != 0
251-
// it doesn't hurt to also additionally validate for negative numbers here as well.
252-
allErrs = append(
253-
allErrs,
254-
field.Forbidden(
255-
field.NewPath("spec", "replicas"),
256-
"cannot be less than or equal to 0",
257-
),
258-
)
259-
}
260-
261-
externalEtcd := false
262-
if in.Spec.KubeadmConfigSpec.ClusterConfiguration != nil {
263-
if in.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.External != nil {
264-
externalEtcd = true
265-
}
266-
}
267-
268-
if !externalEtcd {
269-
if in.Spec.Replicas != nil && *in.Spec.Replicas%2 == 0 {
270-
allErrs = append(
271-
allErrs,
272-
field.Forbidden(
273-
field.NewPath("spec", "replicas"),
274-
"cannot be an even number when using managed etcd",
275-
),
276-
)
277-
}
278-
}
279-
280-
if in.Spec.MachineTemplate.InfrastructureRef.APIVersion == "" {
281-
allErrs = append(
282-
allErrs,
283-
field.Invalid(
284-
field.NewPath("spec", "machineTemplate", "infrastructure", "apiVersion"),
285-
in.Spec.MachineTemplate.InfrastructureRef.APIVersion,
286-
"cannot be empty",
287-
),
288-
)
289-
}
290-
if in.Spec.MachineTemplate.InfrastructureRef.Kind == "" {
291-
allErrs = append(
292-
allErrs,
293-
field.Invalid(
294-
field.NewPath("spec", "machineTemplate", "infrastructure", "kind"),
295-
in.Spec.MachineTemplate.InfrastructureRef.Kind,
296-
"cannot be empty",
297-
),
298-
)
299-
}
300-
if in.Spec.MachineTemplate.InfrastructureRef.Namespace != in.Namespace {
301-
allErrs = append(
302-
allErrs,
303-
field.Invalid(
304-
field.NewPath("spec", "machineTemplate", "infrastructure", "namespace"),
305-
in.Spec.MachineTemplate.InfrastructureRef.Namespace,
306-
"must match metadata.namespace",
307-
),
308-
)
309-
}
310-
311-
if !version.KubeSemver.MatchString(in.Spec.Version) {
312-
allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "version"), in.Spec.Version, "must be a valid semantic version"))
313-
}
314-
315-
if in.Spec.RolloutStrategy != nil {
316-
if in.Spec.RolloutStrategy.Type != RollingUpdateStrategyType {
317-
allErrs = append(
318-
allErrs,
319-
field.Required(
320-
field.NewPath("spec", "rolloutStrategy", "type"),
321-
"only RollingUpdateStrategyType is supported",
322-
),
323-
)
324-
}
325-
326-
ios1 := intstr.FromInt(1)
327-
ios0 := intstr.FromInt(0)
328-
329-
if *in.Spec.RolloutStrategy.RollingUpdate.MaxSurge == ios0 && *in.Spec.Replicas < int32(3) {
330-
allErrs = append(
331-
allErrs,
332-
field.Required(
333-
field.NewPath("spec", "rolloutStrategy", "rollingUpdate"),
334-
"when KubeadmControlPlane is configured to scale-in, replica count needs to be at least 3",
335-
),
336-
)
337-
}
338-
339-
if *in.Spec.RolloutStrategy.RollingUpdate.MaxSurge != ios1 && *in.Spec.RolloutStrategy.RollingUpdate.MaxSurge != ios0 {
340-
allErrs = append(
341-
allErrs,
342-
field.Required(
343-
field.NewPath("spec", "rolloutStrategy", "rollingUpdate", "maxSurge"),
344-
"value must be 1 or 0",
345-
),
346-
)
347-
}
348-
}
349-
350-
allErrs = append(allErrs, in.validateCoreDNSImage()...)
351-
352-
return allErrs
353-
}
354-
355-
func (in *KubeadmControlPlane) validateCoreDNSImage() (allErrs field.ErrorList) {
356-
if in.Spec.KubeadmConfigSpec.ClusterConfiguration == nil {
357-
return allErrs
358-
}
359-
// TODO: Remove when kubeadm types include OpenAPI validation
360-
if !container.ImageTagIsValid(in.Spec.KubeadmConfigSpec.ClusterConfiguration.DNS.ImageTag) {
361-
allErrs = append(
362-
allErrs,
363-
field.Forbidden(
364-
field.NewPath("spec", "kubeadmConfigSpec", "clusterConfiguration", "dns", "imageTag"),
365-
fmt.Sprintf("tag %s is invalid", in.Spec.KubeadmConfigSpec.ClusterConfiguration.DNS.ImageTag),
366-
),
367-
)
368-
}
369-
return allErrs
370-
}
371-
372206
func (in *KubeadmControlPlane) validateCoreDNSVersion(prev *KubeadmControlPlane) (allErrs field.ErrorList) {
373207
if in.Spec.KubeadmConfigSpec.ClusterConfiguration == nil || prev.Spec.KubeadmConfigSpec.ClusterConfiguration == nil {
374208
return allErrs
@@ -415,58 +249,6 @@ func (in *KubeadmControlPlane) validateCoreDNSVersion(prev *KubeadmControlPlane)
415249
return allErrs
416250
}
417251

418-
func (in *KubeadmControlPlane) validateEtcd(prev *KubeadmControlPlane) (allErrs field.ErrorList) {
419-
if in.Spec.KubeadmConfigSpec.ClusterConfiguration == nil {
420-
return allErrs
421-
}
422-
423-
// TODO: Remove when kubeadm types include OpenAPI validation
424-
if in.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.Local != nil && !container.ImageTagIsValid(in.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.Local.ImageTag) {
425-
allErrs = append(
426-
allErrs,
427-
field.Forbidden(
428-
field.NewPath("spec", "kubeadmConfigSpec", "clusterConfiguration", "etcd", "local", "imageTag"),
429-
fmt.Sprintf("tag %s is invalid", in.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.Local.ImageTag),
430-
),
431-
)
432-
}
433-
434-
if in.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.Local != nil && in.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.External != nil {
435-
allErrs = append(
436-
allErrs,
437-
field.Forbidden(
438-
field.NewPath("spec", "kubeadmConfigSpec", "clusterConfiguration", "etcd", "local"),
439-
"cannot have both external and local etcd",
440-
),
441-
)
442-
}
443-
444-
// update validations
445-
if prev != nil && prev.Spec.KubeadmConfigSpec.ClusterConfiguration != nil {
446-
if in.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.External != nil && prev.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.Local != nil {
447-
allErrs = append(
448-
allErrs,
449-
field.Forbidden(
450-
field.NewPath("spec", "kubeadmConfigSpec", "clusterConfiguration", "etcd", "external"),
451-
"cannot change between external and local etcd",
452-
),
453-
)
454-
}
455-
456-
if in.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.Local != nil && prev.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.External != nil {
457-
allErrs = append(
458-
allErrs,
459-
field.Forbidden(
460-
field.NewPath("spec", "kubeadmConfigSpec", "clusterConfiguration", "etcd", "local"),
461-
"cannot change between external and local etcd",
462-
),
463-
)
464-
}
465-
}
466-
467-
return allErrs
468-
}
469-
470252
func (in *KubeadmControlPlane) validateVersion(previousVersion string) (allErrs field.ErrorList) {
471253
fromVersion, err := version.ParseMajorMinorPatch(previousVersion)
472254
if err != nil {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
Copyright 2020 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 v1alpha4
18+
19+
import (
20+
"strings"
21+
22+
"k8s.io/apimachinery/pkg/util/intstr"
23+
)
24+
25+
func (s *KubeadmControlPlaneSpec) setDefaults(namespace string) {
26+
if s.Replicas == nil {
27+
replicas := int32(1)
28+
s.Replicas = &replicas
29+
}
30+
31+
if s.MachineTemplate.InfrastructureRef.Namespace == "" {
32+
s.MachineTemplate.InfrastructureRef.Namespace = namespace
33+
}
34+
35+
if !strings.HasPrefix(s.Version, "v") {
36+
s.Version = "v" + s.Version
37+
}
38+
39+
ios1 := intstr.FromInt(1)
40+
41+
if s.RolloutStrategy == nil {
42+
s.RolloutStrategy = &RolloutStrategy{}
43+
}
44+
45+
// Enforce RollingUpdate strategy and default MaxSurge if not set.
46+
if s.RolloutStrategy != nil {
47+
if len(s.RolloutStrategy.Type) == 0 {
48+
s.RolloutStrategy.Type = RollingUpdateStrategyType
49+
}
50+
if s.RolloutStrategy.Type == RollingUpdateStrategyType {
51+
if s.RolloutStrategy.RollingUpdate == nil {
52+
s.RolloutStrategy.RollingUpdate = &RollingUpdate{}
53+
}
54+
s.RolloutStrategy.RollingUpdate.MaxSurge = intstr.ValueOrDefault(s.RolloutStrategy.RollingUpdate.MaxSurge, ios1)
55+
}
56+
}
57+
}

0 commit comments

Comments
 (0)