Skip to content

Commit a4b78a3

Browse files
Jonathan S. Katzjkatz
authored andcommitted
Allow custom labels to be edited on existing clusters
This allows for custom labels to be edited on all of the managed objects within a cluster. This works both from editing "userlabels" within the pgclusters custom resource, as well as via API calls. The changes are applied to Postgres instances using a rolling update. Issue: [ch11329]
1 parent 99399e2 commit a4b78a3

File tree

4 files changed

+316
-50
lines changed

4 files changed

+316
-50
lines changed

docs/content/custom-resources/_index.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,34 @@ spec:
854854
Save your edits, and in a short period of time, you should see these annotations
855855
applied to the managed Deployments.
856856

857+
### Manage Custom Labels
858+
859+
Several Kubernetes [Labels](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/)
860+
are automatically applied by PGO to its managed objects. However, it is possible
861+
to apply your own custom labels to the objects that PGO manages for a Postgres
862+
cluster. These objects include:
863+
864+
- ConfigMaps
865+
- Deployments
866+
- Jobs
867+
- Pods
868+
- PVCs
869+
- Secrets
870+
- Services
871+
872+
The custom labels can be managed through the `userlabels` attribute on the
873+
`pgclusters.crunchydata.com` custom resource spec.
874+
875+
For example, if I want to add a custom label to all of the objects within my
876+
Postgres cluster with a key of `favorite` and a value of `hippo`, you would
877+
apply the following to the spec:
878+
879+
```
880+
spec:
881+
userlabels:
882+
favorite: hippo
883+
```
884+
857885
### Delete a PostgreSQL Cluster
858886

859887
A PostgreSQL cluster can be deleted by simply deleting the `pgclusters.crunchydata.com` resource.
@@ -954,7 +982,7 @@ make changes, as described below.
954982
| tlsOnly | `create`,`update` | If set to true, requires client connections to use only TLS to connect to the PostgreSQL database. |
955983
| tolerations | `create`,`update` | Any array of Kubernetes [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/). Please refer to the [Kubernetes documentation](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) for how to set this field. |
956984
| user | `create` | The name of the PostgreSQL user that is created when the PostgreSQL cluster is first created. |
957-
| userlabels | `create` | A set of key-value string pairs that are used as a sort of "catch-all" as well as a way to add custom labels to clusters. This will disappear at some point. |
985+
| userlabels | `create`,`update` | A set of key-value string pairs that are used as a sort of "catch-all" as well as a way to add custom labels to clusters. |
958986

959987
##### Storage Specification
960988

internal/apiserver/labelservice/labelimpl.go

Lines changed: 3 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import (
1919
"context"
2020

2121
"github.com/crunchydata/postgres-operator/internal/apiserver"
22-
"github.com/crunchydata/postgres-operator/internal/config"
2322
"github.com/crunchydata/postgres-operator/internal/kubeapi"
2423
"github.com/crunchydata/postgres-operator/internal/util"
2524
crv1 "github.com/crunchydata/postgres-operator/pkg/apis/crunchydata.com/v1"
@@ -111,7 +110,7 @@ func Label(request *msgs.LabelRequest, ns, pgouser string) msgs.LabelResponse {
111110

112111
func addLabels(items []crv1.Pgcluster, DryRun bool, newLabels map[string]string, ns string) {
113112
ctx := context.TODO()
114-
patchBytes, err := kubeapi.NewMergePatch().Add("metadata", "labels")(newLabels).Bytes()
113+
patchBytes, err := kubeapi.NewMergePatch().Add("spec", "userlabels")(newLabels).Bytes()
115114
if err != nil {
116115
log.Error(err.Error())
117116
return
@@ -129,30 +128,6 @@ func addLabels(items []crv1.Pgcluster, DryRun bool, newLabels map[string]string,
129128
}
130129
}
131130
}
132-
133-
for i := 0; i < len(items); i++ {
134-
// get deployments for this CRD
135-
selector := config.LABEL_PG_CLUSTER + "=" + items[i].Spec.Name
136-
deployments, err := apiserver.Clientset.
137-
AppsV1().Deployments(ns).
138-
List(ctx, metav1.ListOptions{LabelSelector: selector})
139-
if err != nil {
140-
return
141-
}
142-
143-
for _, d := range deployments.Items {
144-
// update Deployment with the label
145-
if !DryRun {
146-
log.Debugf("patching deployment %s: %s", d.Name, patchBytes)
147-
_, err := apiserver.Clientset.AppsV1().Deployments(ns).
148-
Patch(ctx, d.Name, types.MergePatchType, patchBytes, metav1.PatchOptions{})
149-
if err != nil {
150-
log.Error(err.Error())
151-
}
152-
}
153-
}
154-
155-
}
156131
}
157132

158133
// DeleteLabel ...
@@ -241,7 +216,7 @@ func deleteLabels(items []crv1.Pgcluster, labelsMap map[string]string, ns string
241216
ctx := context.TODO()
242217
patch := kubeapi.NewMergePatch()
243218
for key := range labelsMap {
244-
patch.Remove("metadata", "labels", key)
219+
patch.Remove("spec", "userlabels", key)
245220
}
246221
patchBytes, err := patch.Bytes()
247222
if err != nil {
@@ -259,26 +234,5 @@ func deleteLabels(items []crv1.Pgcluster, labelsMap map[string]string, ns string
259234
}
260235
}
261236

262-
for i := 0; i < len(items); i++ {
263-
// get deployments for this CRD
264-
selector := config.LABEL_PG_CLUSTER + "=" + items[i].Spec.Name
265-
deployments, err := apiserver.Clientset.
266-
AppsV1().Deployments(ns).
267-
List(ctx, metav1.ListOptions{LabelSelector: selector})
268-
if err != nil {
269-
return err
270-
}
271-
272-
for _, d := range deployments.Items {
273-
log.Debugf("patching deployment %s: %s", d.Name, patchBytes)
274-
_, err = apiserver.Clientset.AppsV1().Deployments(ns).
275-
Patch(ctx, d.Name, types.MergePatchType, patchBytes, metav1.PatchOptions{})
276-
if err != nil {
277-
log.Error(err.Error())
278-
return err
279-
}
280-
}
281-
282-
}
283-
return err
237+
return nil
284238
}

internal/controller/pgcluster/pgclustercontroller.go

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,19 @@ func (c *Controller) onUpdate(oldObj, newObj interface{}) {
354354
}
355355
}
356356

357+
// check to see if any of the custom labels have been modified
358+
if !reflect.DeepEqual(util.GetCustomLabels(oldcluster), util.GetCustomLabels(newcluster)) {
359+
// update the custom labels on all of the managed objects at are not the
360+
// Postgres cluster deployments
361+
if err := updateLabels(c, oldcluster, newcluster); err != nil {
362+
log.Error(err)
363+
return
364+
}
365+
366+
// append the PostgreSQL specific functions as part of a rolling update
367+
rollingUpdateFuncs = append(rollingUpdateFuncs, clusteroperator.UpdateLabels)
368+
}
369+
357370
// check to see if any tolerations have been modified
358371
if !reflect.DeepEqual(oldcluster.Spec.Tolerations, newcluster.Spec.Tolerations) {
359372
rollingUpdateFuncs = append(rollingUpdateFuncs, clusteroperator.UpdateTolerations)
@@ -639,6 +652,237 @@ func updateBackrestS3(c *Controller, cluster *crv1.Pgcluster) error {
639652
return nil
640653
}
641654

655+
// updateLabels updates the custom labels on all of the managed objects *except*
656+
// the Postgres instances themselves, i.e. the deployment templates
657+
func updateLabels(c *Controller, oldCluster *crv1.Pgcluster, newCluster *crv1.Pgcluster) error {
658+
// we need to figure out which labels need to be removed from the list
659+
labelsToRemove := make([]string, 0)
660+
labels := util.GetCustomLabels(newCluster)
661+
662+
for old := range util.GetCustomLabels(oldCluster) {
663+
if _, ok := labels[old]; !ok {
664+
labelsToRemove = append(labelsToRemove, old)
665+
}
666+
}
667+
668+
// go through each object group and update the labels.
669+
if err := updateLabelsForDeployments(c, newCluster, labels, labelsToRemove); err != nil {
670+
return err
671+
}
672+
673+
if err := updateLabelsForPVCs(c, newCluster, labels, labelsToRemove); err != nil {
674+
return err
675+
}
676+
677+
if err := updateLabelsForConfigMaps(c, newCluster, labels, labelsToRemove); err != nil {
678+
return err
679+
}
680+
681+
if err := updateLabelsForSecrets(c, newCluster, labels, labelsToRemove); err != nil {
682+
return err
683+
}
684+
685+
return updateLabelsForServices(c, newCluster, labels, labelsToRemove)
686+
}
687+
688+
// updateLabelsForConfigMaps updates the custom labels for ConfigMaps
689+
func updateLabelsForConfigMaps(c *Controller, cluster *crv1.Pgcluster, labels map[string]string, labelsToRemove []string) error {
690+
ctx := context.TODO()
691+
692+
options := metav1.ListOptions{
693+
LabelSelector: fields.AndSelectors(
694+
fields.OneTermEqualSelector(config.LABEL_PG_CLUSTER, cluster.Name),
695+
fields.OneTermEqualSelector(config.LABEL_VENDOR, config.LABEL_CRUNCHY),
696+
).String(),
697+
}
698+
699+
items, err := c.Client.CoreV1().ConfigMaps(cluster.Namespace).List(ctx, options)
700+
701+
if err != nil {
702+
return err
703+
}
704+
705+
for i := range items.Items {
706+
item := &items.Items[i]
707+
708+
for j := range labelsToRemove {
709+
delete(item.ObjectMeta.Labels, labelsToRemove[j])
710+
}
711+
712+
for k, v := range labels {
713+
item.ObjectMeta.Labels[k] = v
714+
}
715+
716+
if _, err := c.Client.CoreV1().ConfigMaps(cluster.Namespace).Update(ctx,
717+
item, metav1.UpdateOptions{}); err != nil {
718+
return err
719+
}
720+
}
721+
722+
return nil
723+
}
724+
725+
// updateLabelsForDeployments updates the custom labels for Deployments, except
726+
// for the **templates** on the Postgres instances
727+
func updateLabelsForDeployments(c *Controller, cluster *crv1.Pgcluster, labels map[string]string, labelsToRemove []string) error {
728+
ctx := context.TODO()
729+
730+
options := metav1.ListOptions{
731+
LabelSelector: fields.AndSelectors(
732+
fields.OneTermEqualSelector(config.LABEL_PG_CLUSTER, cluster.Name),
733+
fields.OneTermEqualSelector(config.LABEL_VENDOR, config.LABEL_CRUNCHY),
734+
).String(),
735+
}
736+
737+
items, err := c.Client.AppsV1().Deployments(cluster.Namespace).List(ctx, options)
738+
739+
if err != nil {
740+
return err
741+
}
742+
743+
for i := range items.Items {
744+
item := &items.Items[i]
745+
746+
for j := range labelsToRemove {
747+
delete(item.ObjectMeta.Labels, labelsToRemove[j])
748+
749+
// only remove the labels on the template if this is not a Postgres
750+
// instance
751+
if _, ok := item.ObjectMeta.Labels[config.LABEL_PG_DATABASE]; !ok {
752+
delete(item.Spec.Template.ObjectMeta.Labels, labelsToRemove[j])
753+
}
754+
}
755+
756+
for k, v := range labels {
757+
item.ObjectMeta.Labels[k] = v
758+
759+
// only update the labels on the template if this is not a Postgres
760+
// instance
761+
if _, ok := item.ObjectMeta.Labels[config.LABEL_PG_DATABASE]; !ok {
762+
item.Spec.Template.ObjectMeta.Labels[k] = v
763+
}
764+
}
765+
766+
if _, err := c.Client.AppsV1().Deployments(cluster.Namespace).Update(ctx,
767+
item, metav1.UpdateOptions{}); err != nil {
768+
return err
769+
}
770+
}
771+
772+
return nil
773+
}
774+
775+
// updateLabelsForPVCs updates the custom labels for PVCs
776+
func updateLabelsForPVCs(c *Controller, cluster *crv1.Pgcluster, labels map[string]string, labelsToRemove []string) error {
777+
ctx := context.TODO()
778+
779+
options := metav1.ListOptions{
780+
LabelSelector: fields.AndSelectors(
781+
fields.OneTermEqualSelector(config.LABEL_PG_CLUSTER, cluster.Name),
782+
fields.OneTermEqualSelector(config.LABEL_VENDOR, config.LABEL_CRUNCHY),
783+
).String(),
784+
}
785+
786+
items, err := c.Client.CoreV1().PersistentVolumeClaims(cluster.Namespace).List(ctx, options)
787+
788+
if err != nil {
789+
return err
790+
}
791+
792+
for i := range items.Items {
793+
item := &items.Items[i]
794+
795+
for j := range labelsToRemove {
796+
delete(item.ObjectMeta.Labels, labelsToRemove[j])
797+
}
798+
799+
for k, v := range labels {
800+
item.ObjectMeta.Labels[k] = v
801+
}
802+
803+
if _, err := c.Client.CoreV1().PersistentVolumeClaims(cluster.Namespace).Update(ctx,
804+
item, metav1.UpdateOptions{}); err != nil {
805+
return err
806+
}
807+
}
808+
809+
return nil
810+
}
811+
812+
// updateLabelsForSecrets updates the custom labels for Secrets
813+
func updateLabelsForSecrets(c *Controller, cluster *crv1.Pgcluster, labels map[string]string, labelsToRemove []string) error {
814+
ctx := context.TODO()
815+
816+
options := metav1.ListOptions{
817+
LabelSelector: fields.AndSelectors(
818+
fields.OneTermEqualSelector(config.LABEL_PG_CLUSTER, cluster.Name),
819+
fields.OneTermEqualSelector(config.LABEL_VENDOR, config.LABEL_CRUNCHY),
820+
).String(),
821+
}
822+
823+
items, err := c.Client.CoreV1().Secrets(cluster.Namespace).List(ctx, options)
824+
825+
if err != nil {
826+
return err
827+
}
828+
829+
for i := range items.Items {
830+
item := &items.Items[i]
831+
832+
for j := range labelsToRemove {
833+
delete(item.ObjectMeta.Labels, labelsToRemove[j])
834+
}
835+
836+
for k, v := range labels {
837+
item.ObjectMeta.Labels[k] = v
838+
}
839+
840+
if _, err := c.Client.CoreV1().Secrets(cluster.Namespace).Update(ctx,
841+
item, metav1.UpdateOptions{}); err != nil {
842+
return err
843+
}
844+
}
845+
846+
return nil
847+
}
848+
849+
// updateLabelsForServices updates the custom labels for Services
850+
func updateLabelsForServices(c *Controller, cluster *crv1.Pgcluster, labels map[string]string, labelsToRemove []string) error {
851+
ctx := context.TODO()
852+
853+
options := metav1.ListOptions{
854+
LabelSelector: fields.AndSelectors(
855+
fields.OneTermEqualSelector(config.LABEL_PG_CLUSTER, cluster.Name),
856+
fields.OneTermEqualSelector(config.LABEL_VENDOR, config.LABEL_CRUNCHY),
857+
).String(),
858+
}
859+
860+
items, err := c.Client.CoreV1().Services(cluster.Namespace).List(ctx, options)
861+
862+
if err != nil {
863+
return err
864+
}
865+
866+
for i := range items.Items {
867+
item := &items.Items[i]
868+
869+
for j := range labelsToRemove {
870+
delete(item.ObjectMeta.Labels, labelsToRemove[j])
871+
}
872+
873+
for k, v := range labels {
874+
item.ObjectMeta.Labels[k] = v
875+
}
876+
877+
if _, err := c.Client.CoreV1().Services(cluster.Namespace).Update(ctx,
878+
item, metav1.UpdateOptions{}); err != nil {
879+
return err
880+
}
881+
}
882+
883+
return nil
884+
}
885+
642886
// updatePgBouncer updates the pgBouncer Deployment to reflect any changes that
643887
// may be made, which include:
644888
// - enabling a pgBouncer Deployment :)

0 commit comments

Comments
 (0)