Compare commits

..

2 Commits

Author SHA1 Message Date
Armando Ruocco
fca31a984c
Merge 064864ba72 into 376e178ab5 2026-03-27 10:22:44 +00:00
Armando Ruocco
064864ba72 fix(rbac): reconcile Role when ObjectStore spec changes
When an ObjectStore's credentials change (e.g., secret rename), the
RBAC Role granting the Cluster's ServiceAccount access to those secrets
was not updated because nothing triggered a Cluster reconciliation.

Implement the ObjectStore controller's Reconcile to detect referencing
Clusters and update their Roles directly. Extract ensureRole into a
shared rbac.EnsureRole function used by both the Pre hook and the
ObjectStore controller.

The shared EnsureRole accepts optional beforeCreate callbacks so the
Pre hook can set owner references on Role creation, while the
ObjectStore controller path skips ownership (it only updates existing
Roles).

Handle deleted ObjectStores gracefully by skipping NotFound errors
during Role reconciliation, and filter status-only changes with
GenerationChangedPredicate.

Add RBAC permission for the plugin to list/watch Clusters so the
ObjectStore controller can find affected Clusters. Regenerate
config/rbac/role.yaml.

Signed-off-by: Armando Ruocco <armando.ruocco@enterprisedb.com>
2026-03-27 11:21:34 +01:00
13 changed files with 243 additions and 206 deletions

View File

@ -102,8 +102,6 @@ func NewCmd() *cobra.Command {
_ = viper.BindPFlag("server-address", cmd.Flags().Lookup("server-address"))
_ = viper.BindEnv("sidecar-image", "SIDECAR_IMAGE")
_ = viper.BindEnv("custom-cnpg-group", "CUSTOM_CNPG_GROUP")
_ = viper.BindEnv("custom-cnpg-version", "CUSTOM_CNPG_VERSION")
return cmd
}

View File

@ -57,8 +57,6 @@ func NewCmd() *cobra.Command {
_ = viper.BindEnv("pod-name", "POD_NAME")
_ = viper.BindEnv("pgdata", "PGDATA")
_ = viper.BindEnv("spool-directory", "SPOOL_DIRECTORY")
_ = viper.BindEnv("custom-cnpg-group", "CUSTOM_CNPG_GROUP")
_ = viper.BindEnv("custom-cnpg-version", "CUSTOM_CNPG_VERSION")
return cmd
}

View File

@ -28,15 +28,16 @@ import (
"github.com/spf13/viper"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/scheme"
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
extendedclient "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/instance/internal/client"
pluginscheme "github.com/cloudnative-pg/plugin-barman-cloud/internal/scheme"
)
// Start starts the sidecar informers and CNPG-i server
@ -126,7 +127,26 @@ func generateScheme(ctx context.Context) *runtime.Scheme {
utilruntime.Must(barmancloudv1.AddToScheme(result))
utilruntime.Must(clientgoscheme.AddToScheme(result))
pluginscheme.AddCNPGToScheme(ctx, result)
cnpgGroup := viper.GetString("custom-cnpg-group")
cnpgVersion := viper.GetString("custom-cnpg-version")
if len(cnpgGroup) == 0 {
cnpgGroup = cnpgv1.SchemeGroupVersion.Group
}
if len(cnpgVersion) == 0 {
cnpgVersion = cnpgv1.SchemeGroupVersion.Version
}
// Proceed with custom registration of the CNPG scheme
schemeGroupVersion := schema.GroupVersion{Group: cnpgGroup, Version: cnpgVersion}
schemeBuilder := &scheme.Builder{GroupVersion: schemeGroupVersion}
schemeBuilder.Register(&cnpgv1.Cluster{}, &cnpgv1.ClusterList{})
schemeBuilder.Register(&cnpgv1.Backup{}, &cnpgv1.BackupList{})
schemeBuilder.Register(&cnpgv1.ScheduledBackup{}, &cnpgv1.ScheduledBackupList{})
utilruntime.Must(schemeBuilder.AddToScheme(result))
schemeLog := log.FromContext(ctx)
schemeLog.Info("CNPG types registration", "schemeGroupVersion", schemeGroupVersion)
return result
}

View File

@ -24,6 +24,7 @@ import (
"crypto/tls"
// +kubebuilder:scaffold:imports
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
"github.com/cloudnative-pg/machinery/pkg/log"
"github.com/spf13/viper"
"k8s.io/apimachinery/pkg/runtime"
@ -37,33 +38,25 @@ import (
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
"github.com/cloudnative-pg/plugin-barman-cloud/internal/controller"
pluginscheme "github.com/cloudnative-pg/plugin-barman-cloud/internal/scheme"
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that exec-entrypoint and run can make use of them.
_ "k8s.io/client-go/plugin/pkg/client/auth"
)
// generateScheme creates a runtime.Scheme with all type definitions
// needed by the operator. CNPG types are registered under a
// configurable API group to support custom CNPG-based operators.
func generateScheme(ctx context.Context) *runtime.Scheme {
result := runtime.NewScheme()
var scheme = runtime.NewScheme()
utilruntime.Must(clientgoscheme.AddToScheme(result))
utilruntime.Must(barmancloudv1.AddToScheme(result))
pluginscheme.AddCNPGToScheme(ctx, result)
func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
utilruntime.Must(barmancloudv1.AddToScheme(scheme))
utilruntime.Must(cnpgv1.AddToScheme(scheme))
// +kubebuilder:scaffold:scheme
return result
}
// Start starts the manager
func Start(ctx context.Context) error {
setupLog := log.FromContext(ctx)
scheme := generateScheme(ctx)
var tlsOpts []func(*tls.Config)
// if the enable-http2 flag is false (the default), http/2 should be disabled

View File

@ -0,0 +1,58 @@
/*
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 operator
import (
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/utils/ptr"
)
// setOwnerReference explicitly set the owner reference between an
// owner object and a controller one.
//
// Important: this function won't use any registered scheme and will
// fail unless the metadata has been correctly set into the owner
// object.
func setOwnerReference(owner, controlled metav1.Object) error {
ro, ok := owner.(runtime.Object)
if !ok {
return fmt.Errorf("%T is not a runtime.Object, cannot call setOwnerReference", owner)
}
if len(ro.DeepCopyObject().GetObjectKind().GroupVersionKind().Group) == 0 {
return fmt.Errorf("%T metadata have not been set, cannot call setOwnerReference", owner)
}
controlled.SetOwnerReferences([]metav1.OwnerReference{
{
APIVersion: ro.GetObjectKind().GroupVersionKind().GroupVersion().String(),
Kind: ro.GetObjectKind().GroupVersionKind().Kind,
Name: owner.GetName(),
UID: owner.GetUID(),
BlockOwnerDeletion: ptr.To(true),
Controller: ptr.To(true),
},
})
return nil
}

View File

@ -30,129 +30,62 @@ import (
"k8s.io/apimachinery/pkg/api/equality"
apierrs "k8s.io/apimachinery/pkg/api/errors"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/specs"
)
// EnsureRole ensures the RBAC Role for the given Cluster matches
// the desired state derived from the given ObjectStores. On creation,
// the Cluster is set as the owner of the Role for garbage collection.
//
// This function is called from both the Pre hook (gRPC) and the
// ObjectStore controller. To handle concurrent modifications
// gracefully, AlreadyExists on Create and Conflict on Patch are
// retried once rather than returned as errors.
// the desired state derived from the given ObjectStores.
// Optional beforeCreate callbacks are applied to the Role before creation
// (e.g. to set owner references). They are not called on updates.
func EnsureRole(
ctx context.Context,
c client.Client,
cluster *cnpgv1.Cluster,
barmanObjects []barmancloudv1.ObjectStore,
beforeCreate ...func(*rbacv1.Role) error,
) error {
contextLogger := log.FromContext(ctx)
newRole := specs.BuildRole(cluster, barmanObjects)
roleKey := client.ObjectKey{
var role rbacv1.Role
if err := c.Get(ctx, client.ObjectKey{
Namespace: newRole.Namespace,
Name: newRole.Name,
}
var role rbacv1.Role
err := c.Get(ctx, roleKey, &role)
switch {
case apierrs.IsNotFound(err):
role, err := createRole(ctx, c, cluster, newRole)
if err != nil {
}, &role); err != nil {
if !apierrs.IsNotFound(err) {
return err
}
if role == nil {
// Created successfully, nothing else to do.
return nil
}
// AlreadyExists: fall through to patch with the re-read role.
return patchRoleRules(ctx, c, newRole.Rules, role)
case err != nil:
contextLogger.Info(
"Creating role",
"name", newRole.Name,
"namespace", newRole.Namespace,
)
for _, fn := range beforeCreate {
if err := fn(newRole); err != nil {
return err
default:
return patchRoleRules(ctx, c, newRole.Rules, &role)
}
}
// createRole attempts to create the Role. If another writer created
// it concurrently (AlreadyExists), it re-reads and returns the
// existing Role for the caller to patch. On success it returns nil.
func createRole(
ctx context.Context,
c client.Client,
cluster *cnpgv1.Cluster,
newRole *rbacv1.Role,
) (*rbacv1.Role, error) {
contextLogger := log.FromContext(ctx)
if err := controllerutil.SetControllerReference(cluster, newRole, c.Scheme()); err != nil {
return nil, err
return c.Create(ctx, newRole)
}
contextLogger.Info("Creating role",
"name", newRole.Name, "namespace", newRole.Namespace)
createErr := c.Create(ctx, newRole)
if createErr == nil {
return nil, nil
}
if !apierrs.IsAlreadyExists(createErr) {
return nil, createErr
}
contextLogger.Info("Role was created concurrently, checking rules")
var role rbacv1.Role
if err := c.Get(ctx, client.ObjectKeyFromObject(newRole), &role); err != nil {
return nil, err
}
return &role, nil
}
// patchRoleRules patches the Role's rules if they differ from the
// desired state. On Conflict (concurrent modification), it retries
// once with a fresh read.
func patchRoleRules(
ctx context.Context,
c client.Client,
desiredRules []rbacv1.PolicyRule,
role *rbacv1.Role,
) error {
if equality.Semantic.DeepEqual(desiredRules, role.Rules) {
if equality.Semantic.DeepEqual(newRole.Rules, role.Rules) {
return nil
}
contextLogger := log.FromContext(ctx)
contextLogger.Info("Patching role",
"name", role.Name, "namespace", role.Namespace, "rules", desiredRules)
contextLogger.Info(
"Patching role",
"name", newRole.Name,
"namespace", newRole.Namespace,
"rules", newRole.Rules,
)
oldRole := role.DeepCopy()
role.Rules = desiredRules
role.Rules = newRole.Rules
patchErr := c.Patch(ctx, role, client.MergeFrom(oldRole))
if patchErr == nil || !apierrs.IsConflict(patchErr) {
return patchErr
}
// Conflict: re-read and retry once.
contextLogger.Info("Role was modified concurrently, retrying patch")
if err := c.Get(ctx, client.ObjectKeyFromObject(role), role); err != nil {
return err
}
if equality.Semantic.DeepEqual(desiredRules, role.Rules) {
return nil
}
oldRole = role.DeepCopy()
role.Rules = desiredRules
return c.Patch(ctx, role, client.MergeFrom(oldRole))
return c.Patch(ctx, &role, client.MergeFrom(oldRole))
}

View File

@ -21,12 +21,13 @@ package rbac_test
import (
"context"
"fmt"
barmanapi "github.com/cloudnative-pg/barman-cloud/pkg/api"
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
machineryapi "github.com/cloudnative-pg/machinery/pkg/api"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
barmanapi "github.com/cloudnative-pg/barman-cloud/pkg/api"
machineryapi "github.com/cloudnative-pg/machinery/pkg/api"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
@ -99,7 +100,7 @@ var _ = Describe("EnsureRole", func() {
fakeClient = fake.NewClientBuilder().WithScheme(newScheme()).Build()
})
It("should create the Role with owner reference", func() {
It("should create the Role", func() {
err := rbac.EnsureRole(ctx, fakeClient, cluster, objects)
Expect(err).NotTo(HaveOccurred())
@ -110,11 +111,32 @@ var _ = Describe("EnsureRole", func() {
}, &role)
Expect(err).NotTo(HaveOccurred())
Expect(role.Rules).To(HaveLen(3))
})
// Verify owner reference is set to the Cluster
Expect(role.OwnerReferences).To(HaveLen(1))
Expect(role.OwnerReferences[0].Name).To(Equal("test-cluster"))
Expect(role.OwnerReferences[0].Kind).To(Equal("Cluster"))
It("should apply beforeCreate callbacks", func() {
called := false
err := rbac.EnsureRole(ctx, fakeClient, cluster, objects, func(role *rbacv1.Role) error {
called = true
role.Labels = map[string]string{"owner": "test-cluster"}
return nil
})
Expect(err).NotTo(HaveOccurred())
Expect(called).To(BeTrue())
var role rbacv1.Role
err = fakeClient.Get(ctx, client.ObjectKey{
Namespace: "default",
Name: "test-cluster-barman-cloud",
}, &role)
Expect(err).NotTo(HaveOccurred())
Expect(role.Labels).To(HaveKeyWithValue("owner", "test-cluster"))
})
It("should return error if beforeCreate callback fails", func() {
err := rbac.EnsureRole(ctx, fakeClient, cluster, objects, func(_ *rbacv1.Role) error {
return fmt.Errorf("callback error")
})
Expect(err).To(MatchError("callback error"))
})
})
@ -147,13 +169,15 @@ var _ = Describe("EnsureRole", func() {
Context("when the Role exists with different rules", func() {
BeforeEach(func() {
fakeClient = fake.NewClientBuilder().WithScheme(newScheme()).Build()
// Create with old secret
oldObjects := []barmancloudv1.ObjectStore{
newObjectStore("my-store", "default", "old-secret"),
}
Expect(rbac.EnsureRole(ctx, fakeClient, cluster, oldObjects)).To(Succeed())
})
It("should patch the Role with new rules and preserve owner reference", func() {
It("should patch the Role with new rules", func() {
// Now call with new secret name
err := rbac.EnsureRole(ctx, fakeClient, cluster, objects)
Expect(err).NotTo(HaveOccurred())
@ -163,13 +187,20 @@ var _ = Describe("EnsureRole", func() {
Name: "test-cluster-barman-cloud",
}, &role)).To(Succeed())
// The secrets rule should reference the new secret
secretsRule := role.Rules[2]
Expect(secretsRule.ResourceNames).To(ContainElement("aws-creds"))
Expect(secretsRule.ResourceNames).NotTo(ContainElement("old-secret"))
})
// Owner reference must survive the patch
Expect(role.OwnerReferences).To(HaveLen(1))
Expect(role.OwnerReferences[0].Name).To(Equal("test-cluster"))
It("should not call beforeCreate callbacks on update", func() {
called := false
err := rbac.EnsureRole(ctx, fakeClient, cluster, objects, func(_ *rbacv1.Role) error {
called = true
return nil
})
Expect(err).NotTo(HaveOccurred())
Expect(called).To(BeFalse())
})
})
})

View File

@ -30,7 +30,6 @@ import (
rbacv1 "k8s.io/api/rbac/v1"
apierrs "k8s.io/apimachinery/pkg/api/errors"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config"
@ -114,7 +113,9 @@ func (r ReconcilerImplementation) Pre(
barmanObjects = append(barmanObjects, barmanObject)
}
if err := rbac.EnsureRole(ctx, r.Client, &cluster, barmanObjects); err != nil {
if err := rbac.EnsureRole(ctx, r.Client, &cluster, barmanObjects, func(role *rbacv1.Role) error {
return setOwnerReference(&cluster, role)
}); err != nil {
return nil, err
}
@ -163,7 +164,7 @@ func (r ReconcilerImplementation) createRoleBinding(
cluster *cnpgv1.Cluster,
) error {
roleBinding := specs.BuildRoleBinding(cluster)
if err := controllerutil.SetControllerReference(cluster, roleBinding, r.Client.Scheme()); err != nil {
if err := setOwnerReference(cluster, roleBinding); err != nil {
return err
}
return r.Client.Create(ctx, roleBinding)

View File

@ -22,6 +22,7 @@ package restore
import (
"context"
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
"github.com/cloudnative-pg/machinery/pkg/log"
"github.com/spf13/viper"
corev1 "k8s.io/api/core/v1"
@ -32,20 +33,14 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
pluginscheme "github.com/cloudnative-pg/plugin-barman-cloud/internal/scheme"
)
// generateScheme creates a runtime.Scheme with all type definitions
// needed by the restore sidecar. CNPG types are registered under a
// configurable API group to support custom CNPG-based operators.
func generateScheme(ctx context.Context) *runtime.Scheme {
result := runtime.NewScheme()
var scheme = runtime.NewScheme()
utilruntime.Must(barmancloudv1.AddToScheme(result))
utilruntime.Must(clientgoscheme.AddToScheme(result))
pluginscheme.AddCNPGToScheme(ctx, result)
return result
func init() {
utilruntime.Must(barmancloudv1.AddToScheme(scheme))
utilruntime.Must(cnpgv1.AddToScheme(scheme))
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
}
// Start starts the sidecar informers and CNPG-i server
@ -53,8 +48,6 @@ func Start(ctx context.Context) error {
setupLog := log.FromContext(ctx)
setupLog.Info("Starting barman cloud instance plugin")
scheme := generateScheme(ctx)
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
Client: client.Options{

View File

@ -25,11 +25,13 @@ import (
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
"github.com/cloudnative-pg/machinery/pkg/log"
rbacv1 "k8s.io/api/rbac/v1"
apierrs "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/predicate"
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
@ -115,7 +117,9 @@ func (r *ObjectStoreReconciler) reconcileRBACForCluster(
barmanObjects = append(barmanObjects, barmanObject)
}
return rbac.EnsureRole(ctx, r.Client, cluster, barmanObjects)
return rbac.EnsureRole(ctx, r.Client, cluster, barmanObjects, func(role *rbacv1.Role) error {
return controllerutil.SetControllerReference(cluster, role, r.Scheme)
})
}
// referencesObjectStore checks if the given ObjectStore is in the list

View File

@ -20,14 +20,81 @@ SPDX-License-Identifier: Apache-2.0
package controller
import (
"context"
"fmt"
"path/filepath"
"runtime"
"testing"
// +kubebuilder:scaffold:imports
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
var (
cfg *rest.Config
k8sClient client.Client
testEnv *envtest.Environment
ctx context.Context
cancel context.CancelFunc
)
func TestControllers(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Controller Suite")
}
var _ = BeforeSuite(func() {
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
ctx, cancel = context.WithCancel(context.TODO())
By("bootstrapping test environment")
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
ErrorIfCRDPathMissing: true,
// The BinaryAssetsDirectory is only required if you want to run the tests directly
// without call the makefile target test. If not informed it will look for the
// default path defined in controller-runtime which is /usr/local/kubebuilder/.
// Note that you must have the required binaries setup under the bin directory to perform
// the tests directly. When we run make test it will be setup and used automatically.
BinaryAssetsDirectory: filepath.Join("..", "..", "bin", "k8s",
fmt.Sprintf("1.31.0-%s-%s", runtime.GOOS, runtime.GOARCH)),
}
var err error
// cfg is defined in this file globally.
cfg, err = testEnv.Start()
Expect(err).NotTo(HaveOccurred())
Expect(cfg).NotTo(BeNil())
err = barmancloudv1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
// +kubebuilder:scaffold:scheme
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
Expect(err).NotTo(HaveOccurred())
Expect(k8sClient).NotTo(BeNil())
})
var _ = AfterSuite(func() {
By("tearing down the test environment")
cancel()
err := testEnv.Stop()
Expect(err).NotTo(HaveOccurred())
})

View File

@ -1,58 +0,0 @@
/*
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 scheme provides utilities for building runtime schemes
// with support for custom CNPG API groups.
package scheme
import (
"context"
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
"github.com/cloudnative-pg/machinery/pkg/log"
"github.com/spf13/viper"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
crscheme "sigs.k8s.io/controller-runtime/pkg/scheme"
)
// AddCNPGToScheme registers CNPG types into the given scheme using
// the API group configured via CUSTOM_CNPG_GROUP/CUSTOM_CNPG_VERSION
// environment variables, defaulting to postgresql.cnpg.io/v1.
// This allows the plugin to work with any CNPG-based operator.
func AddCNPGToScheme(ctx context.Context, s *runtime.Scheme) {
cnpgGroup := viper.GetString("custom-cnpg-group")
cnpgVersion := viper.GetString("custom-cnpg-version")
if len(cnpgGroup) == 0 {
cnpgGroup = cnpgv1.SchemeGroupVersion.Group
}
if len(cnpgVersion) == 0 {
cnpgVersion = cnpgv1.SchemeGroupVersion.Version
}
schemeGroupVersion := schema.GroupVersion{Group: cnpgGroup, Version: cnpgVersion}
schemeBuilder := &crscheme.Builder{GroupVersion: schemeGroupVersion}
schemeBuilder.Register(&cnpgv1.Cluster{}, &cnpgv1.ClusterList{})
schemeBuilder.Register(&cnpgv1.Backup{}, &cnpgv1.BackupList{})
schemeBuilder.Register(&cnpgv1.ScheduledBackup{}, &cnpgv1.ScheduledBackupList{})
utilruntime.Must(schemeBuilder.AddToScheme(s))
log.FromContext(ctx).Info("CNPG types registration", "schemeGroupVersion", schemeGroupVersion)
}

View File

@ -870,7 +870,6 @@ rules:
- postgresql.cnpg.io
resources:
- backups
- clusters
verbs:
- get
- list