Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stop triggering pod restarts for CR labels/annotations updates #449

Merged
merged 3 commits into from
Nov 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 0 additions & 29 deletions controllers/rabbitmqcluster_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -533,35 +533,6 @@ var _ = Describe("RabbitmqClusterController", func() {
return roleBinding.Annotations
}, 3).Should(HaveKeyWithValue(annotationKey, annotationValue))
})

It("updates annotations for config maps", func() {
Eventually(func() map[string]string {
roleBinding, err := clientSet.CoreV1().ConfigMaps(cluster.Namespace).Get(ctx, cluster.ChildResourceName("server-conf"), metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred())
return roleBinding.Annotations
}, 3).Should(HaveKeyWithValue(annotationKey, annotationValue))

Eventually(func() map[string]string {
roleBinding, err := clientSet.CoreV1().ConfigMaps(cluster.Namespace).Get(ctx, cluster.ChildResourceName("plugins-conf"), metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred())
return roleBinding.Annotations
}, 3).Should(HaveKeyWithValue(annotationKey, annotationValue))
})

It("updates annotations for config maps", func() {
Eventually(func() map[string]string {
roleBinding, err := clientSet.CoreV1().ConfigMaps(cluster.Namespace).Get(ctx, cluster.ChildResourceName("server-conf"), metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred())
return roleBinding.Annotations
}, 3).Should(HaveKeyWithValue(annotationKey, annotationValue))

Eventually(func() map[string]string {
roleBinding, err := clientSet.CoreV1().ConfigMaps(cluster.Namespace).Get(ctx, cluster.ChildResourceName("plugins-conf"), metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred())
return roleBinding.Annotations
}, 3).Should(HaveKeyWithValue(annotationKey, annotationValue))
})

})

It("service type is updated", func() {
Expand Down
8 changes: 4 additions & 4 deletions internal/resource/configmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,16 @@ func (builder *RabbitmqResourceBuilder) ServerConfigMap() *ServerConfigMapBuilde
func (builder *ServerConfigMapBuilder) Build() (runtime.Object, error) {
return &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: builder.Instance.ChildResourceName(ServerConfigMapName),
Namespace: builder.Instance.Namespace,
Name: builder.Instance.ChildResourceName(ServerConfigMapName),
Namespace: builder.Instance.Namespace,
Labels: metadata.GetLabels(builder.Instance.Name, builder.Instance.Labels),
Annotations: metadata.ReconcileAndFilterAnnotations(nil, builder.Instance.Annotations),
},
}, nil
}

func (builder *ServerConfigMapBuilder) Update(object runtime.Object) error {
configMap := object.(*corev1.ConfigMap)
configMap.Labels = metadata.GetLabels(builder.Instance.Name, builder.Instance.Labels)
configMap.Annotations = metadata.ReconcileAndFilterAnnotations(configMap.GetAnnotations(), builder.Instance.Annotations)

ini.PrettySection = false // Remove trailing new line because rabbitmq.conf has only a default section.
cfg, err := ini.Load([]byte(defaultRabbitmqConf))
Expand Down
153 changes: 67 additions & 86 deletions internal/resource/configmap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,22 @@ var _ = Describe("GenerateServerConfigMap", func() {
var configMap *corev1.ConfigMap

BeforeEach(func() {
instance.Labels = map[string]string{
"app.kubernetes.io/foo": "bar",
"foo": "bar",
"rabbitmq": "is-great",
"foo/app.kubernetes.io": "edgecase",
}
instance.Annotations = map[string]string{
"my-annotation": "i-like-this",
"kubernetes.io/name": "i-do-not-like-this",
"kubectl.kubernetes.io/name": "i-do-not-like-this",
"k8s.io/name": "i-do-not-like-this",
"kubernetes.io/other": "i-do-not-like-this",
"kubectl.kubernetes.io/other": "i-do-not-like-this",
"k8s.io/other": "i-do-not-like-this",
}

obj, err := configMapBuilder.Build()
configMap = obj.(*corev1.ConfigMap)
Expect(err).NotTo(HaveOccurred())
Expand All @@ -75,6 +91,32 @@ var _ = Describe("GenerateServerConfigMap", func() {
Expect(configMap.Name).To(Equal(builder.Instance.ChildResourceName("server-conf")))
Expect(configMap.Namespace).To(Equal(builder.Instance.Namespace))
})

It("adds labels from the instance and default labels", func() {
Expect(configMap.Labels).To(SatisfyAll(
HaveLen(6),
HaveKeyWithValue("foo", "bar"),
HaveKeyWithValue("rabbitmq", "is-great"),
HaveKeyWithValue("foo/app.kubernetes.io", "edgecase"),
HaveKeyWithValue("app.kubernetes.io/name", instance.Name),
HaveKeyWithValue("app.kubernetes.io/component", "rabbitmq"),
HaveKeyWithValue("app.kubernetes.io/part-of", "rabbitmq"),
Not(HaveKey("app.kubernetes.io/foo")),
))
})

It("adds annotations from the instance", func() {
Expect(configMap.Annotations).To(SatisfyAll(
HaveLen(1),
HaveKeyWithValue("my-annotation", "i-like-this"),
Not(HaveKey("kubernetes.io/name")),
Not(HaveKey("kubectl.kubernetes.io/name")),
Not(HaveKey("k8s.io/name")),
Not(HaveKey("kubernetes.io/other")),
Not(HaveKey("kubectl.kubernetes.io/other")),
Not(HaveKey("k8s.io/other")),
))
})
})

Context("Update", func() {
Expand Down Expand Up @@ -284,94 +326,33 @@ ssl_options.verify = verify_peer
})
})

Context("labels", func() {
BeforeEach(func() {
instance = rabbitmqv1beta1.RabbitmqCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "rabbit-labelled",
},
}
instance.Labels = map[string]string{
"app.kubernetes.io/foo": "bar",
"foo": "bar",
"rabbitmq": "is-great",
"foo/app.kubernetes.io": "edgecase",
}

configMap = &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"app.kubernetes.io/name": instance.Name,
"app.kubernetes.io/part-of": "rabbitmq",
"this-was-the-previous-label": "should-be-deleted",
},
},
}
err := configMapBuilder.Update(configMap)
Expect(err).NotTo(HaveOccurred())
})

It("adds labels from the CR", func() {
testLabels(configMap.Labels)
})

It("restores the default labels", func() {
labels := configMap.Labels
Expect(labels["app.kubernetes.io/name"]).To(Equal(instance.Name))
Expect(labels["app.kubernetes.io/component"]).To(Equal("rabbitmq"))
Expect(labels["app.kubernetes.io/part-of"]).To(Equal("rabbitmq"))
})

It("deletes the labels that are removed from the CR", func() {
Expect(configMap.Labels).NotTo(HaveKey("this-was-the-previous-label"))
})
// this is to ensure that pods are not restarted when instance labels are updated
It("does not update labels on the config map", func() {
configMap.Labels = map[string]string{
"app.kubernetes.io/name": instance.Name,
"app.kubernetes.io/component": "rabbitmq",
"app.kubernetes.io/part-of": "rabbitmq",
}
instance.Labels = map[string]string{
"new-label": "test",
}
Expect(configMapBuilder.Update(configMap)).To(Succeed())
Expect(configMap.Labels).To(SatisfyAll(
HaveLen(3),
HaveKeyWithValue("app.kubernetes.io/name", instance.Name),
HaveKeyWithValue("app.kubernetes.io/component", "rabbitmq"),
HaveKeyWithValue("app.kubernetes.io/part-of", "rabbitmq"),
Not(HaveKey("new-label")),
))
})

Context("instance annotations", func() {
BeforeEach(func() {
instance = rabbitmqv1beta1.RabbitmqCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "rabbit-labelled",
},
}
instance.Annotations = map[string]string{
"my-annotation": "i-like-this",
"kubernetes.io/name": "i-do-not-like-this",
"kubectl.kubernetes.io/name": "i-do-not-like-this",
"k8s.io/name": "i-do-not-like-this",
"kubernetes.io/other": "i-do-not-like-this",
"kubectl.kubernetes.io/other": "i-do-not-like-this",
"k8s.io/other": "i-do-not-like-this",
}

configMap = &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"my-annotation": "i-will-not-stay",
"old-annotation": "old-value",
"im-here-to-stay.kubernetes.io": "for-a-while",
"kubernetes.io/name": "should-stay",
"kubectl.kubernetes.io/name": "should-stay",
"k8s.io/name": "should-stay",
},
},
}
err := configMapBuilder.Update(configMap)
Expect(err).NotTo(HaveOccurred())
})

It("updates config map annotations", func() {
expectedAnnotations := map[string]string{
"my-annotation": "i-like-this",
"old-annotation": "old-value",
"im-here-to-stay.kubernetes.io": "for-a-while",
"kubernetes.io/name": "should-stay",
"kubectl.kubernetes.io/name": "should-stay",
"k8s.io/name": "should-stay",
}

Expect(configMap.Annotations).To(Equal(expectedAnnotations))
})
// this is to ensure that pods are not restarted when instance annotations are updated
It("does not update annotations on the config map", func() {
instance.Annotations = map[string]string{
"new-annotation": "test",
}
Expect(configMapBuilder.Update(configMap)).To(Succeed())
Expect(configMap.Annotations).To(BeEmpty())
})
})
})
Expand Down
8 changes: 4 additions & 4 deletions internal/resource/rabbitmq_plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ func (builder *RabbitmqResourceBuilder) RabbitmqPluginsConfigMap() *RabbitmqPlug
func (builder *RabbitmqPluginsConfigMapBuilder) Build() (runtime.Object, error) {
return &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: builder.Instance.ChildResourceName(PluginsConfigName),
Namespace: builder.Instance.Namespace,
Name: builder.Instance.ChildResourceName(PluginsConfigName),
Namespace: builder.Instance.Namespace,
Labels: metadata.GetLabels(builder.Instance.Name, builder.Instance.Labels),
Annotations: metadata.ReconcileAndFilterAnnotations(nil, builder.Instance.Annotations),
},
Data: map[string]string{
"enabled_plugins": desiredPluginsAsString([]rabbitmqv1beta1.Plugin{}),
Expand All @@ -47,8 +49,6 @@ func (builder *RabbitmqPluginsConfigMapBuilder) Build() (runtime.Object, error)

func (builder *RabbitmqPluginsConfigMapBuilder) Update(object runtime.Object) error {
configMap := object.(*corev1.ConfigMap)
configMap.Labels = metadata.GetLabels(builder.Instance.Name, builder.Instance.Labels)
configMap.Annotations = metadata.ReconcileAndFilterAnnotations(configMap.GetAnnotations(), builder.Instance.Annotations)

if configMap.Data == nil {
configMap.Data = make(map[string]string)
Expand Down
70 changes: 70 additions & 0 deletions internal/resource/rabbitmq_plugins_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,22 @@ var _ = Describe("RabbitMQPlugins", func() {
var configMap *corev1.ConfigMap

BeforeEach(func() {
instance.Labels = map[string]string{
"app.kubernetes.io/foo": "bar",
"foo": "bar",
"rabbitmq": "is-great",
"foo/app.kubernetes.io": "edgecase",
}
instance.Annotations = map[string]string{
"my-annotation": "i-like-this",
"kubernetes.io/name": "i-do-not-like-this",
"kubectl.kubernetes.io/name": "i-do-not-like-this",
"k8s.io/name": "i-do-not-like-this",
"kubernetes.io/other": "i-do-not-like-this",
"kubectl.kubernetes.io/other": "i-do-not-like-this",
"k8s.io/other": "i-do-not-like-this",
}

obj, err := configMapBuilder.Build()
configMap = obj.(*corev1.ConfigMap)
Expect(err).NotTo(HaveOccurred())
Expand All @@ -112,6 +128,32 @@ var _ = Describe("RabbitMQPlugins", func() {
configMap = obj.(*corev1.ConfigMap)
Expect(configMap.Data).To(HaveKeyWithValue("enabled_plugins", expectedEnabledPlugins))
})

It("adds labels from the instance and default labels", func() {
Expect(configMap.Labels).To(SatisfyAll(
HaveLen(6),
HaveKeyWithValue("foo", "bar"),
HaveKeyWithValue("rabbitmq", "is-great"),
HaveKeyWithValue("foo/app.kubernetes.io", "edgecase"),
HaveKeyWithValue("app.kubernetes.io/name", instance.Name),
HaveKeyWithValue("app.kubernetes.io/component", "rabbitmq"),
HaveKeyWithValue("app.kubernetes.io/part-of", "rabbitmq"),
Not(HaveKey("app.kubernetes.io/foo")),
))
})

It("adds annotations from the instance", func() {
Expect(configMap.Annotations).To(SatisfyAll(
HaveLen(1),
HaveKeyWithValue("my-annotation", "i-like-this"),
Not(HaveKey("kubernetes.io/name")),
Not(HaveKey("kubectl.kubernetes.io/name")),
Not(HaveKey("k8s.io/name")),
Not(HaveKey("kubernetes.io/other")),
Not(HaveKey("kubectl.kubernetes.io/other")),
Not(HaveKey("k8s.io/other")),
))
})
})

Context("Update", func() {
Expand Down Expand Up @@ -177,6 +219,34 @@ var _ = Describe("RabbitMQPlugins", func() {
})
})
})

// ensures that we are not unnecessarily running `rabbitmq-plugins set` when CR labels are updated
It("does not update labels on the config map", func() {
configMap.Labels = map[string]string{
"app.kubernetes.io/name": instance.Name,
"app.kubernetes.io/component": "rabbitmq",
"app.kubernetes.io/part-of": "rabbitmq",
}
instance.Labels = map[string]string{
"new-label": "test",
}
Expect(configMapBuilder.Update(configMap)).To(Succeed())
Expect(configMap.Labels).To(SatisfyAll(
HaveLen(3),
HaveKeyWithValue("app.kubernetes.io/name", instance.Name),
HaveKeyWithValue("app.kubernetes.io/component", "rabbitmq"),
HaveKeyWithValue("app.kubernetes.io/part-of", "rabbitmq"),
Not(HaveKey("new-label")),
))
})
// ensures that we are not unnecessarily running `rabbitmq-plugins set` when CR annotations are updated
It("does not update annotations on the config map", func() {
instance.Annotations = map[string]string{
"new-annotation": "test",
}
Expect(configMapBuilder.Update(configMap)).To(Succeed())
Expect(configMap.Annotations).To(BeEmpty())
})
})
})
})
23 changes: 12 additions & 11 deletions internal/resource/statefulset.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,12 @@ func (builder *StatefulSetBuilder) Update(object runtime.Object) error {

//Annotations
sts.Annotations = metadata.ReconcileAndFilterAnnotations(sts.Annotations, builder.Instance.Annotations)
defaultPodAnnotations := map[string]string{
"prometheus.io/scrape": "true",
"prometheus.io/port": "15692",
}
podAnnotations := metadata.ReconcileAnnotations(defaultPodAnnotations, metadata.ReconcileAndFilterAnnotations(sts.Spec.Template.Annotations, builder.Instance.Annotations))

//Labels
updatedLabels := metadata.GetLabels(builder.Instance.Name, builder.Instance.Labels)
sts.Labels = updatedLabels
sts.Labels = metadata.GetLabels(builder.Instance.Name, builder.Instance.Labels)

sts.Spec.Template = builder.podTemplateSpec(podAnnotations, updatedLabels)
// pod template
sts.Spec.Template = builder.podTemplateSpec(sts.Spec.Template.Annotations)

if !sts.Spec.Template.Spec.Containers[0].Resources.Limits.Memory().Equal(*sts.Spec.Template.Spec.Containers[0].Resources.Requests.Memory()) {
logger := ctrl.Log.WithName("statefulset").WithName("RabbitmqCluster")
Expand Down Expand Up @@ -261,7 +256,13 @@ func sortEnvVar(envVar []corev1.EnvVar) {
}
}

func (builder *StatefulSetBuilder) podTemplateSpec(annotations, labels map[string]string) corev1.PodTemplateSpec {
func (builder *StatefulSetBuilder) podTemplateSpec(previousPodAnnotations map[string]string) corev1.PodTemplateSpec {
// default pod annotations used for prometheus metrics
defaultPodAnnotations := map[string]string{
"prometheus.io/scrape": "true",
"prometheus.io/port": "15692",
}

//Init Container resources
cpuRequest := k8sresource.MustParse(initContainerCPU)
memoryRequest := k8sresource.MustParse(initContainerMemory)
Expand Down Expand Up @@ -510,8 +511,8 @@ func (builder *StatefulSetBuilder) podTemplateSpec(annotations, labels map[strin

return corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Annotations: annotations,
Labels: labels,
Annotations: metadata.ReconcileAnnotations(defaultPodAnnotations, previousPodAnnotations),
Labels: metadata.Label(builder.Instance.Name),
},
Spec: corev1.PodSpec{
TopologySpreadConstraints: []corev1.TopologySpreadConstraint{
Expand Down
Loading