Skip to content

Commit 60f227e

Browse files
committed
Unify k0s version format
Signed-off-by: apedriza <[email protected]>
1 parent f1c73fc commit 60f227e

File tree

8 files changed

+92
-48
lines changed

8 files changed

+92
-48
lines changed

api/k0smotron.io/v1beta1/k0smotroncluster_types.go

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ package v1beta1
1919
import (
2020
"crypto/md5"
2121
"fmt"
22+
"strings"
23+
2224
"github.com/k0sproject/version"
2325
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
24-
"strings"
2526

2627
v1 "k8s.io/api/core/v1"
2728
"k8s.io/apimachinery/pkg/api/resource"
@@ -174,27 +175,31 @@ type Mount struct {
174175
}
175176

176177
const (
177-
defaultK0SImage = "quay.io/k0sproject/k0s"
178-
DefaultK0SVersion = "v1.27.9-k0s.0"
179-
DefaultK0SSuffix = "k0s.0"
180-
DefaultEtcdImage = "quay.io/k0sproject/etcd:v3.5.13"
178+
defaultK0SRepository = "quay.io/k0sproject/k0s"
179+
DefaultK0SVersion = "v1.27.9+k0s.0"
180+
DefaultK0SSuffix = "k0s.0"
181+
DefaultEtcdImage = "quay.io/k0sproject/etcd:v3.5.13"
181182
)
182183

183-
func (c *ClusterSpec) GetImage() string {
184-
k0sVersion := c.Version
185-
if k0sVersion == "" {
186-
k0sVersion = DefaultK0SVersion
184+
// GetK0sImageRef returns the k0s image reference.
185+
func (c *ClusterSpec) GetK0sImageRef() string {
186+
k0sTag := c.Version
187+
if k0sTag == "" {
188+
k0sTag = DefaultK0SVersion
187189
}
188190

189-
if !strings.Contains(k0sVersion, "-k0s.") {
190-
k0sVersion = fmt.Sprintf("%s-%s", k0sVersion, DefaultK0SSuffix)
191+
if !strings.Contains(k0sTag, "+k0s.") {
192+
k0sTag = fmt.Sprintf("%s+%s", k0sTag, DefaultK0SSuffix)
191193
}
192194

195+
k0sImageRef := fmt.Sprintf("%s:%s", c.Image, k0sTag)
193196
if c.Image == "" {
194-
return fmt.Sprintf("%s:%s", defaultK0SImage, k0sVersion)
197+
k0sImageRef = fmt.Sprintf("%s:%s", defaultK0SRepository, k0sTag)
195198
}
196199

197-
return fmt.Sprintf("%s:%s", c.Image, k0sVersion)
200+
// Mutate image reference to avoid "+" character in the k0s version tag which is not allowed in some
201+
// registries like Docker Hub (https://github.com/distribution/reference/blob/main/reference.go)
202+
return strings.ReplaceAll(k0sImageRef, "+k0s.", "-k0s.")
198203
}
199204

200205
// ClusterStatus defines the observed state of K0smotronCluster

api/k0smotron.io/v1beta1/k0smotroncluster_types_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func TestClusterSpec_GetImage(t *testing.T) {
3737
{
3838
name: "Only version given with suffix",
3939
spec: &ClusterSpec{
40-
Version: "v1.29.4-k0s.0",
40+
Version: "v1.29.4+k0s.0",
4141
},
4242
want: "quay.io/k0sproject/k0s:v1.29.4-k0s.0",
4343
},
@@ -66,7 +66,7 @@ func TestClusterSpec_GetImage(t *testing.T) {
6666
}
6767
for _, tt := range tests {
6868
t.Run(tt.name, func(t *testing.T) {
69-
if got := tt.spec.GetImage(); got != tt.want {
69+
if got := tt.spec.GetK0sImageRef(); got != tt.want {
7070
require.Equal(t, tt.want, got)
7171
}
7272
})

cmd/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ func main() {
327327
os.Exit(1)
328328
}
329329

330-
if err = (&controlplane.K0smotronControlPlaneValidator{}).SetupK0smotronControlPlaneWebhookWithManager(mgr); err != nil {
330+
if err = controlplane.SetupK0smotronControlPlaneWebhookWithManager(mgr); err != nil {
331331
setupLog.Error(err, "unable to create validation webhook", "webhook", "K0smotronControlPlaneValidator")
332332
os.Exit(1)
333333
}

config/webhook/manifests.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,26 @@ webhooks:
2424
resources:
2525
- clusters
2626
sideEffects: None
27+
- admissionReviewVersions:
28+
- v1
29+
clientConfig:
30+
service:
31+
name: webhook-service
32+
namespace: system
33+
path: /mutate-controlplane-cluster-x-k8s-io-v1beta1-k0smotroncontrolplane
34+
failurePolicy: Fail
35+
name: mutate-k0smotroncontrolplane-v1beta1.k0smotron.io
36+
rules:
37+
- apiGroups:
38+
- controlplane.cluster.x-k8s.io
39+
apiVersions:
40+
- v1beta1
41+
operations:
42+
- CREATE
43+
- UPDATE
44+
resources:
45+
- k0smotroncontrolplanes
46+
sideEffects: None
2747
---
2848
apiVersion: admissionregistration.k8s.io/v1
2949
kind: ValidatingWebhookConfiguration

internal/controller/controlplane/k0smotron_controlplane_webhook.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package controlplane
1919
import (
2020
"context"
2121
"fmt"
22+
2223
k0smotronio "github.com/k0sproject/k0smotron/internal/controller/k0smotron.io"
2324
"github.com/k0sproject/version"
2425
"k8s.io/apimachinery/pkg/runtime"
@@ -30,6 +31,7 @@ import (
3031
)
3132

3233
// +kubebuilder:webhook:path=/validate-controlplane-cluster-x-k8s-io-v1beta1-k0smotroncontrolplane,mutating=false,failurePolicy=fail,sideEffects=None,groups=controlplane.cluster.x-k8s.io,resources=k0smotroncontrolplanes,verbs=create;update,versions=v1beta1,name=validate-k0smotroncontrolplane-v1beta1.k0smotron.io,admissionReviewVersions=v1
34+
// +kubebuilder:webhook:path=/mutate-controlplane-cluster-x-k8s-io-v1beta1-k0smotroncontrolplane,mutating=true,failurePolicy=fail,sideEffects=None,groups=controlplane.cluster.x-k8s.io,resources=k0smotroncontrolplanes,verbs=create;update,versions=v1beta1,name=mutate-k0smotroncontrolplane-v1beta1.k0smotron.io,admissionReviewVersions=v1
3335

3436
// K0smotronControlPlaneValidator struct is responsible for validating the K0smotronControlPlane resource when it is created, updated, or deleted.
3537
//
@@ -39,7 +41,25 @@ type K0smotronControlPlaneValidator struct {
3941
cv k0smotronio.ClusterValidator
4042
}
4143

44+
// K0smotronControlPlaneDefaulter struct is responsible for defaulting the K0smotronControlPlane resource when it is created or updated.
45+
//
46+
// NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods,
47+
// as this struct is used only for temporary operations and does not need to be deeply copied.
48+
type K0smotronControlPlaneDefaulter struct {
49+
cv k0smotronio.ClusterDefaulter
50+
}
51+
4252
var _ webhook.CustomValidator = &K0smotronControlPlaneValidator{}
53+
var _ webhook.CustomDefaulter = &K0smotronControlPlaneDefaulter{}
54+
55+
// Default implements webhook.CustomDefaulter so a webhook will be registered for the type K0smotronControlPlane.
56+
func (d *K0smotronControlPlaneDefaulter) Default(_ context.Context, obj runtime.Object) error {
57+
kcp, ok := obj.(*v1beta1.K0smotronControlPlane)
58+
if !ok {
59+
return fmt.Errorf("expected a K0smotronControlPlane object but got %T", obj)
60+
}
61+
return d.cv.DefaultClusterSpec(&kcp.Spec)
62+
}
4363

4464
// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type K0smotronControlPlane.
4565
func (v *K0smotronControlPlaneValidator) ValidateCreate(_ context.Context, obj runtime.Object) (admission.Warnings, error) {
@@ -108,9 +128,10 @@ func (v *K0smotronControlPlaneValidator) ValidateDelete(_ context.Context, _ run
108128
}
109129

110130
// SetupK0smotronControlPlaneWebhookWithManager registers the webhook for K0smotronControlPlane in the manager.
111-
func (v *K0smotronControlPlaneValidator) SetupK0smotronControlPlaneWebhookWithManager(mgr ctrl.Manager) error {
131+
func SetupK0smotronControlPlaneWebhookWithManager(mgr ctrl.Manager) error {
112132
return ctrl.NewWebhookManagedBy(mgr).
113133
For(&v1beta1.K0smotronControlPlane{}).
114-
WithValidator(v).
134+
WithValidator(&K0smotronControlPlaneValidator{}).
135+
WithDefaulter(&K0smotronControlPlaneDefaulter{}).
115136
Complete()
116137
}

internal/controller/k0smotron.io/k0smotroncluster_etcd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ func initialCluster(kmc *km.Cluster, replicas int32) string {
406406
}
407407

408408
func generateEtcdInitContainers(kmc *km.Cluster, existingSts *apps.StatefulSet) []v1.Container {
409-
checkImage := kmc.Spec.GetImage()
409+
checkImage := kmc.Spec.GetK0sImageRef()
410410
if existingSts != nil {
411411
for _, c := range existingSts.Spec.Template.Spec.InitContainers {
412412
if c.Name == "dns-check" {

internal/controller/k0smotron.io/k0smotroncluster_statefulset.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ func (scope *kmcScope) generateStatefulSet(kmc *km.Cluster) (apps.StatefulSet, e
118118
}},
119119
Containers: []v1.Container{{
120120
Name: "controller",
121-
Image: kmc.Spec.GetImage(),
121+
Image: kmc.Spec.GetK0sImageRef(),
122122
ImagePullPolicy: v1.PullIfNotPresent,
123123
Args: []string{"/bin/sh", "-c", "/k0smotron-entrypoint.sh"},
124124
Ports: []v1.ContainerPort{
@@ -442,7 +442,7 @@ func mountSecrets(kmc *km.Cluster, sfs *apps.StatefulSet) {
442442
// Otherwise k0s will trip over the permissions and RO mounts
443443
sfs.Spec.Template.Spec.InitContainers = append(sfs.Spec.Template.Spec.InitContainers, v1.Container{
444444
Name: "certs-init",
445-
Image: kmc.Spec.GetImage(),
445+
Image: kmc.Spec.GetK0sImageRef(),
446446
Command: []string{
447447
"sh",
448448
"-c",

internal/controller/k0smotron.io/k0smotroncluster_webhook.go

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,6 @@ func (c ClusterValidator) ValidateDelete(_ context.Context, _ runtime.Object) (w
5656

5757
// ValidateClusterSpec validates the ClusterSpec and returns any warnings or errors.
5858
func (c ClusterValidator) ValidateClusterSpec(kcs *km.ClusterSpec) (warnings admission.Warnings, err error) {
59-
warnings = c.validateVersionSuffix(kcs.Version)
60-
6159
if kcs.Ingress != nil {
6260
warn, err := kcs.Ingress.Validate(kcs.Version)
6361
warnings = append(warnings, warn...)
@@ -73,43 +71,43 @@ func (c ClusterValidator) ValidateClusterSpec(kcs *km.ClusterSpec) (warnings adm
7371
return warnings, nil
7472
}
7573

76-
// validateVersionSuffix checks if the version has a k0s suffix and returns a warning if it doesn't
77-
func (c ClusterValidator) validateVersionSuffix(version string) admission.Warnings {
78-
warnings := admission.Warnings{}
79-
if version != "" && !strings.Contains(version, "-k0s.") {
80-
warnings = append(warnings, fmt.Sprintf("The specified version '%s' requires a k0s suffix (-k0s.<number>). Using '%s-k0s.0' instead.", version, version))
81-
}
82-
83-
return warnings
84-
}
85-
86-
func (c *ClusterDefaulter) Default(_ context.Context, obj runtime.Object) error {
74+
// Default implements webhook.CustomDefaulter so a webhook will be registered for the type Cluster.
75+
func (c ClusterDefaulter) Default(_ context.Context, obj runtime.Object) error {
8776
kmc, ok := obj.(*km.Cluster)
8877
if !ok {
8978
return fmt.Errorf("expected a Cluster object but got %T", obj)
9079
}
9180

92-
if kmc.Spec.Replicas == 0 {
93-
kmc.Spec.Replicas = 1
94-
}
81+
return c.DefaultClusterSpec(&kmc.Spec)
82+
}
9583

96-
if kmc.Spec.Version == "" {
97-
kmc.Spec.Version = km.DefaultK0SVersion
84+
// DefaultClusterSpec sets default values for the ClusterSpec.
85+
func (c *ClusterDefaulter) DefaultClusterSpec(kmcSpec *km.ClusterSpec) error {
86+
if kmcSpec.Replicas == 0 {
87+
kmcSpec.Replicas = 1
9888
}
9989

100-
if kmc.Spec.Service.Type == "" {
101-
kmc.Spec.Service.Type = corev1.ServiceTypeClusterIP
102-
kmc.Spec.Service.APIPort = 30443
103-
kmc.Spec.Service.KonnectivityPort = 30132
90+
if kmcSpec.Version == "" {
91+
kmcSpec.Version = km.DefaultK0SVersion
92+
}
93+
// Ensure we store the version with +k0s. That way we can standardize the k0s version format.
94+
// Needed sanitization will be done in runtime when building image references and not required
95+
// by the user.
96+
kmcSpec.Version = strings.ReplaceAll(kmcSpec.Version, "-k0s.", "+k0s.")
97+
98+
if kmcSpec.Service.Type == "" {
99+
kmcSpec.Service.Type = corev1.ServiceTypeClusterIP
100+
kmcSpec.Service.APIPort = 30443
101+
kmcSpec.Service.KonnectivityPort = 30132
104102
}
105103

106-
if kmc.Spec.Etcd.Image == "" {
107-
kmc.Spec.Etcd.Image = km.DefaultEtcdImage
104+
if kmcSpec.Etcd.Image == "" {
105+
kmcSpec.Etcd.Image = km.DefaultEtcdImage
108106
}
109107

110-
if kmc.Spec.Ingress != nil {
111-
if kmc.Spec.Ingress.Deploy == nil {
112-
kmc.Spec.Ingress.Deploy = ptr.To(true)
108+
if kmcSpec.Ingress != nil {
109+
if kmcSpec.Ingress.Deploy == nil {
110+
kmcSpec.Ingress.Deploy = ptr.To(true)
113111
}
114112
}
115113

0 commit comments

Comments
 (0)