mirror of
https://github.com/cloudnative-pg/plugin-barman-cloud.git
synced 2026-07-06 17:52:21 +02:00
feat: configure k8s recommended labels on subresources
Signed-off-by: Gabriele Fedi <gabriele.fedi@enterprisedb.com>
This commit is contained in:
parent
a81f6e3d73
commit
3b6c25c2dc
@ -31,7 +31,6 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/specs"
|
||||
)
|
||||
|
||||
@ -62,9 +61,7 @@ func EnsureRole(
|
||||
return err
|
||||
}
|
||||
|
||||
return patchRole(ctx, c, roleKey, newRole.Rules, map[string]string{
|
||||
metadata.ClusterLabelName: cluster.Name,
|
||||
})
|
||||
return patchRole(ctx, c, roleKey, newRole.Rules, specs.GetRequiredLabels(cluster))
|
||||
}
|
||||
|
||||
// EnsureRoleRules updates the rules of an existing Role to match
|
||||
@ -90,6 +87,48 @@ func EnsureRoleRules(
|
||||
return err
|
||||
}
|
||||
|
||||
// EnsureRoleBinding ensures the RoleBinding for the given Cluster matches
|
||||
// the desired state.
|
||||
//
|
||||
// This function is called from the Pre hook (gRPC). It creates the RoleBinding
|
||||
// if it does not exist, otherwise it patches RoleRef, Subjects, and labels to match
|
||||
// the desired state.
|
||||
func EnsureRoleBinding(ctx context.Context, c client.Client, cluster *cnpgv1.Cluster) error {
|
||||
contextLogger := log.FromContext(ctx)
|
||||
|
||||
desiredRoleBinding := specs.BuildRoleBinding(cluster)
|
||||
if err := specs.SetControllerReference(cluster, desiredRoleBinding); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
roleBinding := &rbacv1.RoleBinding{}
|
||||
|
||||
if err := c.Get(ctx, client.ObjectKey{
|
||||
Namespace: cluster.Namespace,
|
||||
Name: specs.GetRBACName(cluster.Name),
|
||||
}, roleBinding); err != nil {
|
||||
if apierrs.IsNotFound(err) {
|
||||
contextLogger.Info("Creating RoleBinding", "name", desiredRoleBinding.Name,
|
||||
"namespace", desiredRoleBinding.Namespace)
|
||||
return c.Create(ctx, desiredRoleBinding)
|
||||
}
|
||||
}
|
||||
|
||||
if !roleBindingNeedsUpdate(roleBinding, desiredRoleBinding) {
|
||||
return nil
|
||||
}
|
||||
|
||||
contextLogger.Info("Patching role binding",
|
||||
"name", roleBinding.Name, "namespace", roleBinding.Namespace)
|
||||
|
||||
oldRoleBinding := roleBinding.DeepCopy()
|
||||
roleBinding.Labels = desiredRoleBinding.Labels
|
||||
roleBinding.RoleRef = desiredRoleBinding.RoleRef
|
||||
roleBinding.Subjects = desiredRoleBinding.Subjects
|
||||
|
||||
return c.Patch(ctx, roleBinding, client.MergeFrom(oldRoleBinding))
|
||||
}
|
||||
|
||||
// ensureRoleExists creates the Role if it does not exist. Returns
|
||||
// nil on success and nil on AlreadyExists (another writer created
|
||||
// it concurrently). The caller always follows up with patchRole.
|
||||
@ -179,3 +218,25 @@ func labelsNeedUpdate(existing, desired map[string]string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// roleBindingNeedsUpdate returns true if the existing RoleBinding's
|
||||
// RoleRef or Subjects differ from the desired, or if labels need update.
|
||||
func roleBindingNeedsUpdate(existing, desired *rbacv1.RoleBinding) bool {
|
||||
if existing == nil || desired == nil {
|
||||
return existing != desired
|
||||
}
|
||||
|
||||
if !equality.Semantic.DeepEqual(existing.RoleRef, desired.RoleRef) {
|
||||
return true
|
||||
}
|
||||
|
||||
if !equality.Semantic.DeepEqual(existing.Subjects, desired.Subjects) {
|
||||
return true
|
||||
}
|
||||
|
||||
if labelsNeedUpdate(existing.Labels, desired.Labels) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@ -27,14 +27,12 @@ import (
|
||||
"github.com/cloudnative-pg/cnpg-i-machinery/pkg/pluginhelper/object"
|
||||
"github.com/cloudnative-pg/cnpg-i/pkg/reconciler"
|
||||
"github.com/cloudnative-pg/machinery/pkg/log"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config"
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/rbac"
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/specs"
|
||||
)
|
||||
|
||||
// ReconcilerImplementation implements the Reconciler capability
|
||||
@ -117,7 +115,7 @@ func (r ReconcilerImplementation) Pre(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.ensureRoleBinding(ctx, &cluster); err != nil {
|
||||
if err := rbac.EnsureRoleBinding(ctx, r.Client, &cluster); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -136,34 +134,3 @@ func (r ReconcilerImplementation) Post(
|
||||
Behavior: reconciler.ReconcilerHooksResult_BEHAVIOR_CONTINUE,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r ReconcilerImplementation) ensureRoleBinding(
|
||||
ctx context.Context,
|
||||
cluster *cnpgv1.Cluster,
|
||||
) error {
|
||||
var roleBinding rbacv1.RoleBinding
|
||||
if err := r.Client.Get(ctx, client.ObjectKey{
|
||||
Namespace: cluster.Namespace,
|
||||
Name: specs.GetRBACName(cluster.Name),
|
||||
}, &roleBinding); err != nil {
|
||||
if apierrs.IsNotFound(err) {
|
||||
return r.createRoleBinding(ctx, cluster)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: this assumes role bindings never change.
|
||||
// Is that true? Should we relax this assumption?
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r ReconcilerImplementation) createRoleBinding(
|
||||
ctx context.Context,
|
||||
cluster *cnpgv1.Cluster,
|
||||
) error {
|
||||
roleBinding := specs.BuildRoleBinding(cluster)
|
||||
if err := specs.SetControllerReference(cluster, roleBinding); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.Client.Create(ctx, roleBinding)
|
||||
}
|
||||
|
||||
46
internal/cnpgi/operator/specs/metadata.go
Normal file
46
internal/cnpgi/operator/specs/metadata.go
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright © contributors to CloudNativePG, established as
|
||||
CloudNativePG a Series of LF Projects, LLC.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package specs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
|
||||
"github.com/cloudnative-pg/cloudnative-pg/pkg/utils"
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
|
||||
)
|
||||
|
||||
// GetRequiredLabels returns the labels that must be present on all plugin-managed objects for a given Cluster.
|
||||
func GetRequiredLabels(cluster *cnpgv1.Cluster) map[string]string {
|
||||
requiredLabels := map[string]string{
|
||||
metadata.ClusterLabelName: cluster.Name,
|
||||
// Kubernetes recommended labels
|
||||
utils.KubernetesAppLabelName: utils.AppName,
|
||||
utils.KubernetesAppInstanceLabelName: cluster.Name,
|
||||
utils.KubernetesAppManagedByLabelName: "plugin-barman-cloud",
|
||||
utils.KubernetesAppComponentLabelName: utils.DatabaseComponentName,
|
||||
}
|
||||
|
||||
if version, err := cluster.GetPostgresqlMajorVersion(); err == nil && version != 0 {
|
||||
requiredLabels[utils.KubernetesAppVersionLabelName] = fmt.Sprint(version)
|
||||
}
|
||||
|
||||
return requiredLabels
|
||||
}
|
||||
67
internal/cnpgi/operator/specs/metadata_test.go
Normal file
67
internal/cnpgi/operator/specs/metadata_test.go
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
Copyright © contributors to CloudNativePG, established as
|
||||
CloudNativePG a Series of LF Projects, LLC.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package specs
|
||||
|
||||
import (
|
||||
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
|
||||
"github.com/cloudnative-pg/cloudnative-pg/pkg/utils"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
|
||||
)
|
||||
|
||||
var _ = Describe("GetRequiredLabels", func() {
|
||||
It("should return all expected labels for a cluster without an image", func() {
|
||||
cluster := &cnpgv1.Cluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-cluster",
|
||||
Namespace: "default",
|
||||
},
|
||||
}
|
||||
labels := GetRequiredLabels(cluster)
|
||||
|
||||
Expect(labels).To(HaveKeyWithValue(metadata.ClusterLabelName, "my-cluster"))
|
||||
Expect(labels).To(HaveKeyWithValue(utils.KubernetesAppLabelName, utils.AppName))
|
||||
Expect(labels).To(HaveKeyWithValue(utils.KubernetesAppInstanceLabelName, "my-cluster"))
|
||||
Expect(labels).To(HaveKeyWithValue(utils.KubernetesAppManagedByLabelName, "plugin-barman-cloud"))
|
||||
Expect(labels).To(HaveKeyWithValue(utils.KubernetesAppComponentLabelName, utils.DatabaseComponentName))
|
||||
Expect(labels).To(HaveLen(6))
|
||||
})
|
||||
|
||||
It("should use the major version from the image catalog ref", func() {
|
||||
cluster := &cnpgv1.Cluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pg16-cluster",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: cnpgv1.ClusterSpec{
|
||||
ImageCatalogRef: &cnpgv1.ImageCatalogRef{
|
||||
Major: 16,
|
||||
},
|
||||
},
|
||||
}
|
||||
labels := GetRequiredLabels(cluster)
|
||||
|
||||
Expect(labels).To(HaveKeyWithValue(utils.KubernetesAppVersionLabelName, "16"))
|
||||
Expect(labels).To(HaveKeyWithValue(utils.KubernetesAppInstanceLabelName, "pg16-cluster"))
|
||||
})
|
||||
})
|
||||
@ -29,7 +29,6 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
|
||||
)
|
||||
|
||||
// BuildRole builds the Role object for this cluster
|
||||
@ -41,9 +40,7 @@ func BuildRole(
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: cluster.Namespace,
|
||||
Name: GetRBACName(cluster.Name),
|
||||
Labels: map[string]string{
|
||||
metadata.ClusterLabelName: cluster.Name,
|
||||
},
|
||||
Labels: GetRequiredLabels(cluster),
|
||||
},
|
||||
Rules: BuildRoleRules(barmanObjects),
|
||||
}
|
||||
@ -131,6 +128,7 @@ func BuildRoleBinding(
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: cluster.Namespace,
|
||||
Name: GetRBACName(cluster.Name),
|
||||
Labels: GetRequiredLabels(cluster),
|
||||
},
|
||||
Subjects: []rbacv1.Subject{
|
||||
{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user