diff --git a/api/k0smotron.io/v1beta1/k0smotroncluster_types.go b/api/k0smotron.io/v1beta1/k0smotroncluster_types.go index dee636b96..ebd223037 100644 --- a/api/k0smotron.io/v1beta1/k0smotroncluster_types.go +++ b/api/k0smotron.io/v1beta1/k0smotroncluster_types.go @@ -351,6 +351,10 @@ func (kmc *Cluster) GetConfigMapName() string { return kmc.getObjectName("kmc-%s-config") } +func (kmc *Cluster) GetConfigMapChecksumAnnotationName() string { + return "checksum" +} + func (kmc *Cluster) GetServiceName() string { switch kmc.Spec.Service.Type { case v1.ServiceTypeNodePort: diff --git a/internal/controller/controlplane/k0smotron_controlplane_controller.go b/internal/controller/controlplane/k0smotron_controlplane_controller.go index 9678ab762..3a8acd196 100644 --- a/internal/controller/controlplane/k0smotron_controlplane_controller.go +++ b/internal/controller/controlplane/k0smotron_controlplane_controller.go @@ -233,6 +233,7 @@ func (c *K0smotronController) reconcile(ctx context.Context, cluster *clusterv1. Labels: map[string]string{ clusterv1.ClusterNameLabel: cluster.Name, }, + Annotations: kcp.Annotations, OwnerReferences: []metav1.OwnerReference{ { APIVersion: cpv1beta1.GroupVersion.String(), diff --git a/internal/controller/k0smotron.io/k0smotroncluster_configmap.go b/internal/controller/k0smotron.io/k0smotroncluster_configmap.go index 27bb5398e..e46d23723 100644 --- a/internal/controller/k0smotron.io/k0smotroncluster_configmap.go +++ b/internal/controller/k0smotron.io/k0smotroncluster_configmap.go @@ -22,6 +22,7 @@ import ( "net" "strings" + "github.com/cloudflare/cfssl/scan/crypto/sha256" "github.com/imdario/mergo" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -91,6 +92,17 @@ func (r *ClusterReconciler) generateConfig(kmc *km.Cluster, sans []string) (v1.C return v1.ConfigMap{}, nil, err } + annotations := annotationsForCluster(kmc) + if annotations == nil { + annotations = make(map[string]string) + } + + hash := sha256.New() + _, err = hash.Write(b) + if err != nil { + return v1.ConfigMap{}, nil, fmt.Errorf("failed to hash configmap: %w", err) + } + annotations[kmc.GetConfigMapChecksumAnnotationName()] = fmt.Sprintf("%x", hash.Sum(nil)) cm := v1.ConfigMap{ TypeMeta: metav1.TypeMeta{ Kind: "ConfigMap", @@ -100,7 +112,7 @@ func (r *ClusterReconciler) generateConfig(kmc *km.Cluster, sans []string) (v1.C Name: kmc.GetConfigMapName(), Namespace: kmc.Namespace, Labels: labelsForCluster(kmc), - Annotations: annotationsForCluster(kmc), + Annotations: annotations, }, Data: map[string]string{ "K0SMOTRON_K0S_YAML": string(b), diff --git a/internal/controller/k0smotron.io/k0smotroncluster_etcd.go b/internal/controller/k0smotron.io/k0smotroncluster_etcd.go index e8e587a42..ac13ff898 100644 --- a/internal/controller/k0smotron.io/k0smotroncluster_etcd.go +++ b/internal/controller/k0smotron.io/k0smotroncluster_etcd.go @@ -20,10 +20,11 @@ import ( "bytes" "context" "fmt" - batchv1 "k8s.io/api/batch/v1" "strings" "text/template" + batchv1 "k8s.io/api/batch/v1" + km "github.com/k0sproject/k0smotron/api/k0smotron.io/v1beta1" apps "k8s.io/api/apps/v1" @@ -265,7 +266,8 @@ func (r *ClusterReconciler) generateEtcdStatefulSet(kmc *km.Cluster, replicas in PodManagementPolicy: apps.ParallelPodManagement, Template: v1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: labels, + Labels: labels, + Annotations: annotationsForCluster(kmc), }, Spec: v1.PodSpec{ AutomountServiceAccountToken: ptr.To(false), diff --git a/internal/controller/k0smotron.io/k0smotroncluster_statefulset.go b/internal/controller/k0smotron.io/k0smotroncluster_statefulset.go index 2ff1e6d7a..0a4ea8cb2 100644 --- a/internal/controller/k0smotron.io/k0smotroncluster_statefulset.go +++ b/internal/controller/k0smotron.io/k0smotroncluster_statefulset.go @@ -40,8 +40,8 @@ import ( var entrypointDefaultMode = int32(0744) const ( - clusterLabel = "k0smotron.io/cluster" - statefulSetAnnotation = "k0smotron.io/statefulset-hash" + clusterLabel = "k0smotron.io/cluster" + statefulSetHashAnnotation = "k0smotron.io/statefulset-hash" ) // findStatefulSetPod returns a first running pod from a StatefulSet @@ -50,9 +50,26 @@ func (r *ClusterReconciler) findStatefulSetPod(ctx context.Context, statefulSet } func (r *ClusterReconciler) generateStatefulSet(kmc *km.Cluster) (apps.StatefulSet, error) { - labels := labelsForCluster(kmc) + + configMap := &v1.ConfigMap{} + err := r.Client.Get( + context.Background(), + client.ObjectKey{Namespace: kmc.Namespace, Name: kmc.GetConfigMapName()}, + configMap, + ) + if err != nil { + if apierrors.IsNotFound(err) { + return apps.StatefulSet{}, fmt.Errorf("configmap %s missing", kmc.GetConfigMapName()) + } + + return apps.StatefulSet{}, fmt.Errorf("failed to get configmap %s: %w", kmc.GetConfigMapName(), err) + } annotations := annotationsForCluster(kmc) + if annotations == nil { + annotations = make(map[string]string, 1) + } + annotations["checksum/configmap"] = configMap.Annotations[kmc.GetConfigMapChecksumAnnotationName()] statefulSet := apps.StatefulSet{ TypeMeta: metav1.TypeMeta{ @@ -306,10 +323,10 @@ data: ReadOnly: true, }) - err := ctrl.SetControllerReference(kmc, &statefulSet, r.Scheme) + err = ctrl.SetControllerReference(kmc, &statefulSet, r.Scheme) statefulSet.Annotations = map[string]string{ - statefulSetAnnotation: controller.ComputeHash(&statefulSet.Spec.Template, statefulSet.Status.CollisionCount), + statefulSetHashAnnotation: controller.ComputeHash(&statefulSet.Spec.Template, statefulSet.Status.CollisionCount), } for k, v := range annotations { statefulSet.Annotations[k] = v @@ -547,7 +564,7 @@ func (r *ClusterReconciler) reconcileStatefulSet(ctx context.Context, kmc km.Clu func isStatefulSetsEqual(new, old *apps.StatefulSet) bool { return *new.Spec.Replicas == *old.Spec.Replicas && - new.Annotations[statefulSetAnnotation] == old.Annotations[statefulSetAnnotation] && + new.Annotations[statefulSetHashAnnotation] == old.Annotations[statefulSetHashAnnotation] && reflect.DeepEqual(new.Spec.Selector, old.Spec.Selector) && equality.Semantic.DeepDerivative(new.Spec.VolumeClaimTemplates, old.Spec.VolumeClaimTemplates)