mirror of
https://github.com/cloudnative-pg/plugin-barman-cloud.git
synced 2026-07-09 19:22:21 +02:00
Compare commits
2 Commits
fca31a984c
...
9d938831ce
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d938831ce | ||
|
|
840fbc861b |
@ -102,6 +102,8 @@ func NewCmd() *cobra.Command {
|
|||||||
_ = viper.BindPFlag("server-address", cmd.Flags().Lookup("server-address"))
|
_ = viper.BindPFlag("server-address", cmd.Flags().Lookup("server-address"))
|
||||||
|
|
||||||
_ = viper.BindEnv("sidecar-image", "SIDECAR_IMAGE")
|
_ = viper.BindEnv("sidecar-image", "SIDECAR_IMAGE")
|
||||||
|
_ = viper.BindEnv("custom-cnpg-group", "CUSTOM_CNPG_GROUP")
|
||||||
|
_ = viper.BindEnv("custom-cnpg-version", "CUSTOM_CNPG_VERSION")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@ -57,6 +57,8 @@ func NewCmd() *cobra.Command {
|
|||||||
_ = viper.BindEnv("pod-name", "POD_NAME")
|
_ = viper.BindEnv("pod-name", "POD_NAME")
|
||||||
_ = viper.BindEnv("pgdata", "PGDATA")
|
_ = viper.BindEnv("pgdata", "PGDATA")
|
||||||
_ = viper.BindEnv("spool-directory", "SPOOL_DIRECTORY")
|
_ = viper.BindEnv("spool-directory", "SPOOL_DIRECTORY")
|
||||||
|
_ = viper.BindEnv("custom-cnpg-group", "CUSTOM_CNPG_GROUP")
|
||||||
|
_ = viper.BindEnv("custom-cnpg-version", "CUSTOM_CNPG_VERSION")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,16 +28,15 @@ import (
|
|||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||||
ctrl "sigs.k8s.io/controller-runtime"
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/scheme"
|
|
||||||
|
|
||||||
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
||||||
extendedclient "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/instance/internal/client"
|
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
|
// Start starts the sidecar informers and CNPG-i server
|
||||||
@ -127,26 +126,7 @@ func generateScheme(ctx context.Context) *runtime.Scheme {
|
|||||||
|
|
||||||
utilruntime.Must(barmancloudv1.AddToScheme(result))
|
utilruntime.Must(barmancloudv1.AddToScheme(result))
|
||||||
utilruntime.Must(clientgoscheme.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
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,7 +24,6 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
|
||||||
// +kubebuilder:scaffold:imports
|
// +kubebuilder:scaffold:imports
|
||||||
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
|
|
||||||
"github.com/cloudnative-pg/machinery/pkg/log"
|
"github.com/cloudnative-pg/machinery/pkg/log"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
@ -38,25 +37,33 @@ import (
|
|||||||
|
|
||||||
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
||||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/controller"
|
"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.)
|
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
|
||||||
// to ensure that exec-entrypoint and run can make use of them.
|
// to ensure that exec-entrypoint and run can make use of them.
|
||||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
var scheme = runtime.NewScheme()
|
// 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()
|
||||||
|
|
||||||
func init() {
|
utilruntime.Must(clientgoscheme.AddToScheme(result))
|
||||||
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
|
utilruntime.Must(barmancloudv1.AddToScheme(result))
|
||||||
utilruntime.Must(barmancloudv1.AddToScheme(scheme))
|
pluginscheme.AddCNPGToScheme(ctx, result)
|
||||||
utilruntime.Must(cnpgv1.AddToScheme(scheme))
|
|
||||||
// +kubebuilder:scaffold:scheme
|
// +kubebuilder:scaffold:scheme
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts the manager
|
// Start starts the manager
|
||||||
func Start(ctx context.Context) error {
|
func Start(ctx context.Context) error {
|
||||||
setupLog := log.FromContext(ctx)
|
setupLog := log.FromContext(ctx)
|
||||||
|
|
||||||
|
scheme := generateScheme(ctx)
|
||||||
|
|
||||||
var tlsOpts []func(*tls.Config)
|
var tlsOpts []func(*tls.Config)
|
||||||
|
|
||||||
// if the enable-http2 flag is false (the default), http/2 should be disabled
|
// if the enable-http2 flag is false (the default), http/2 should be disabled
|
||||||
|
|||||||
@ -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 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
|
|
||||||
}
|
|
||||||
@ -30,62 +30,129 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/api/equality"
|
"k8s.io/apimachinery/pkg/api/equality"
|
||||||
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"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"
|
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
||||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/specs"
|
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/specs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EnsureRole ensures the RBAC Role for the given Cluster matches
|
// EnsureRole ensures the RBAC Role for the given Cluster matches
|
||||||
// the desired state derived from the given ObjectStores.
|
// the desired state derived from the given ObjectStores. On creation,
|
||||||
// Optional beforeCreate callbacks are applied to the Role before creation
|
// the Cluster is set as the owner of the Role for garbage collection.
|
||||||
// (e.g. to set owner references). They are not called on updates.
|
//
|
||||||
|
// 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.
|
||||||
func EnsureRole(
|
func EnsureRole(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
c client.Client,
|
c client.Client,
|
||||||
cluster *cnpgv1.Cluster,
|
cluster *cnpgv1.Cluster,
|
||||||
barmanObjects []barmancloudv1.ObjectStore,
|
barmanObjects []barmancloudv1.ObjectStore,
|
||||||
beforeCreate ...func(*rbacv1.Role) error,
|
|
||||||
) error {
|
) error {
|
||||||
contextLogger := log.FromContext(ctx)
|
|
||||||
newRole := specs.BuildRole(cluster, barmanObjects)
|
newRole := specs.BuildRole(cluster, barmanObjects)
|
||||||
|
|
||||||
var role rbacv1.Role
|
roleKey := client.ObjectKey{
|
||||||
if err := c.Get(ctx, client.ObjectKey{
|
|
||||||
Namespace: newRole.Namespace,
|
Namespace: newRole.Namespace,
|
||||||
Name: newRole.Name,
|
Name: newRole.Name,
|
||||||
}, &role); err != nil {
|
|
||||||
if !apierrs.IsNotFound(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
contextLogger.Info(
|
|
||||||
"Creating role",
|
|
||||||
"name", newRole.Name,
|
|
||||||
"namespace", newRole.Namespace,
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, fn := range beforeCreate {
|
|
||||||
if err := fn(newRole); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Create(ctx, newRole)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if equality.Semantic.DeepEqual(newRole.Rules, role.Rules) {
|
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 {
|
||||||
|
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:
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
contextLogger.Info(
|
contextLogger := log.FromContext(ctx)
|
||||||
"Patching role",
|
contextLogger.Info("Patching role",
|
||||||
"name", newRole.Name,
|
"name", role.Name, "namespace", role.Namespace, "rules", desiredRules)
|
||||||
"namespace", newRole.Namespace,
|
|
||||||
"rules", newRole.Rules,
|
|
||||||
)
|
|
||||||
|
|
||||||
oldRole := role.DeepCopy()
|
oldRole := role.DeepCopy()
|
||||||
role.Rules = newRole.Rules
|
role.Rules = desiredRules
|
||||||
|
|
||||||
return c.Patch(ctx, &role, client.MergeFrom(oldRole))
|
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))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,13 +21,12 @@ package rbac_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
|
barmanapi "github.com/cloudnative-pg/barman-cloud/pkg/api"
|
||||||
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
|
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/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "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"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
@ -81,9 +80,9 @@ func newObjectStore(name, namespace, secretName string) barmancloudv1.ObjectStor
|
|||||||
|
|
||||||
var _ = Describe("EnsureRole", func() {
|
var _ = Describe("EnsureRole", func() {
|
||||||
var (
|
var (
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cluster *cnpgv1.Cluster
|
cluster *cnpgv1.Cluster
|
||||||
objects []barmancloudv1.ObjectStore
|
objects []barmancloudv1.ObjectStore
|
||||||
fakeClient client.Client
|
fakeClient client.Client
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -100,7 +99,7 @@ var _ = Describe("EnsureRole", func() {
|
|||||||
fakeClient = fake.NewClientBuilder().WithScheme(newScheme()).Build()
|
fakeClient = fake.NewClientBuilder().WithScheme(newScheme()).Build()
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should create the Role", func() {
|
It("should create the Role with owner reference", func() {
|
||||||
err := rbac.EnsureRole(ctx, fakeClient, cluster, objects)
|
err := rbac.EnsureRole(ctx, fakeClient, cluster, objects)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
@ -111,32 +110,11 @@ var _ = Describe("EnsureRole", func() {
|
|||||||
}, &role)
|
}, &role)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(role.Rules).To(HaveLen(3))
|
Expect(role.Rules).To(HaveLen(3))
|
||||||
})
|
|
||||||
|
|
||||||
It("should apply beforeCreate callbacks", func() {
|
// Verify owner reference is set to the Cluster
|
||||||
called := false
|
Expect(role.OwnerReferences).To(HaveLen(1))
|
||||||
err := rbac.EnsureRole(ctx, fakeClient, cluster, objects, func(role *rbacv1.Role) error {
|
Expect(role.OwnerReferences[0].Name).To(Equal("test-cluster"))
|
||||||
called = true
|
Expect(role.OwnerReferences[0].Kind).To(Equal("Cluster"))
|
||||||
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"))
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -169,15 +147,13 @@ var _ = Describe("EnsureRole", func() {
|
|||||||
Context("when the Role exists with different rules", func() {
|
Context("when the Role exists with different rules", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
fakeClient = fake.NewClientBuilder().WithScheme(newScheme()).Build()
|
fakeClient = fake.NewClientBuilder().WithScheme(newScheme()).Build()
|
||||||
// Create with old secret
|
|
||||||
oldObjects := []barmancloudv1.ObjectStore{
|
oldObjects := []barmancloudv1.ObjectStore{
|
||||||
newObjectStore("my-store", "default", "old-secret"),
|
newObjectStore("my-store", "default", "old-secret"),
|
||||||
}
|
}
|
||||||
Expect(rbac.EnsureRole(ctx, fakeClient, cluster, oldObjects)).To(Succeed())
|
Expect(rbac.EnsureRole(ctx, fakeClient, cluster, oldObjects)).To(Succeed())
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should patch the Role with new rules", func() {
|
It("should patch the Role with new rules and preserve owner reference", func() {
|
||||||
// Now call with new secret name
|
|
||||||
err := rbac.EnsureRole(ctx, fakeClient, cluster, objects)
|
err := rbac.EnsureRole(ctx, fakeClient, cluster, objects)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
@ -187,20 +163,13 @@ var _ = Describe("EnsureRole", func() {
|
|||||||
Name: "test-cluster-barman-cloud",
|
Name: "test-cluster-barman-cloud",
|
||||||
}, &role)).To(Succeed())
|
}, &role)).To(Succeed())
|
||||||
|
|
||||||
// The secrets rule should reference the new secret
|
|
||||||
secretsRule := role.Rules[2]
|
secretsRule := role.Rules[2]
|
||||||
Expect(secretsRule.ResourceNames).To(ContainElement("aws-creds"))
|
Expect(secretsRule.ResourceNames).To(ContainElement("aws-creds"))
|
||||||
Expect(secretsRule.ResourceNames).NotTo(ContainElement("old-secret"))
|
Expect(secretsRule.ResourceNames).NotTo(ContainElement("old-secret"))
|
||||||
})
|
|
||||||
|
|
||||||
It("should not call beforeCreate callbacks on update", func() {
|
// Owner reference must survive the patch
|
||||||
called := false
|
Expect(role.OwnerReferences).To(HaveLen(1))
|
||||||
err := rbac.EnsureRole(ctx, fakeClient, cluster, objects, func(_ *rbacv1.Role) error {
|
Expect(role.OwnerReferences[0].Name).To(Equal("test-cluster"))
|
||||||
called = true
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
Expect(called).To(BeFalse())
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -30,6 +30,7 @@ import (
|
|||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"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"
|
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/config"
|
||||||
@ -113,9 +114,7 @@ func (r ReconcilerImplementation) Pre(
|
|||||||
barmanObjects = append(barmanObjects, barmanObject)
|
barmanObjects = append(barmanObjects, barmanObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := rbac.EnsureRole(ctx, r.Client, &cluster, barmanObjects, func(role *rbacv1.Role) error {
|
if err := rbac.EnsureRole(ctx, r.Client, &cluster, barmanObjects); err != nil {
|
||||||
return setOwnerReference(&cluster, role)
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +163,7 @@ func (r ReconcilerImplementation) createRoleBinding(
|
|||||||
cluster *cnpgv1.Cluster,
|
cluster *cnpgv1.Cluster,
|
||||||
) error {
|
) error {
|
||||||
roleBinding := specs.BuildRoleBinding(cluster)
|
roleBinding := specs.BuildRoleBinding(cluster)
|
||||||
if err := setOwnerReference(cluster, roleBinding); err != nil {
|
if err := controllerutil.SetControllerReference(cluster, roleBinding, r.Client.Scheme()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return r.Client.Create(ctx, roleBinding)
|
return r.Client.Create(ctx, roleBinding)
|
||||||
|
|||||||
@ -22,7 +22,6 @@ package restore
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
|
|
||||||
"github.com/cloudnative-pg/machinery/pkg/log"
|
"github.com/cloudnative-pg/machinery/pkg/log"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
@ -33,14 +32,20 @@ import (
|
|||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
||||||
|
pluginscheme "github.com/cloudnative-pg/plugin-barman-cloud/internal/scheme"
|
||||||
)
|
)
|
||||||
|
|
||||||
var scheme = runtime.NewScheme()
|
// 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()
|
||||||
|
|
||||||
func init() {
|
utilruntime.Must(barmancloudv1.AddToScheme(result))
|
||||||
utilruntime.Must(barmancloudv1.AddToScheme(scheme))
|
utilruntime.Must(clientgoscheme.AddToScheme(result))
|
||||||
utilruntime.Must(cnpgv1.AddToScheme(scheme))
|
pluginscheme.AddCNPGToScheme(ctx, result)
|
||||||
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts the sidecar informers and CNPG-i server
|
// Start starts the sidecar informers and CNPG-i server
|
||||||
@ -48,6 +53,8 @@ func Start(ctx context.Context) error {
|
|||||||
setupLog := log.FromContext(ctx)
|
setupLog := log.FromContext(ctx)
|
||||||
setupLog.Info("Starting barman cloud instance plugin")
|
setupLog.Info("Starting barman cloud instance plugin")
|
||||||
|
|
||||||
|
scheme := generateScheme(ctx)
|
||||||
|
|
||||||
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
|
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
|
||||||
Scheme: scheme,
|
Scheme: scheme,
|
||||||
Client: client.Options{
|
Client: client.Options{
|
||||||
|
|||||||
@ -25,13 +25,11 @@ import (
|
|||||||
|
|
||||||
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
|
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
|
||||||
"github.com/cloudnative-pg/machinery/pkg/log"
|
"github.com/cloudnative-pg/machinery/pkg/log"
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
|
||||||
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
ctrl "sigs.k8s.io/controller-runtime"
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||||
|
|
||||||
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
||||||
@ -117,9 +115,7 @@ func (r *ObjectStoreReconciler) reconcileRBACForCluster(
|
|||||||
barmanObjects = append(barmanObjects, barmanObject)
|
barmanObjects = append(barmanObjects, barmanObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
return rbac.EnsureRole(ctx, r.Client, cluster, barmanObjects, func(role *rbacv1.Role) error {
|
return rbac.EnsureRole(ctx, r.Client, cluster, barmanObjects)
|
||||||
return controllerutil.SetControllerReference(cluster, role, r.Scheme)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// referencesObjectStore checks if the given ObjectStore is in the list
|
// referencesObjectStore checks if the given ObjectStore is in the list
|
||||||
|
|||||||
@ -20,81 +20,14 @@ SPDX-License-Identifier: Apache-2.0
|
|||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
"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/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "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) {
|
func TestControllers(t *testing.T) {
|
||||||
RegisterFailHandler(Fail)
|
RegisterFailHandler(Fail)
|
||||||
|
|
||||||
RunSpecs(t, "Controller Suite")
|
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())
|
|
||||||
})
|
|
||||||
|
|||||||
58
internal/scheme/cnpg.go
Normal file
58
internal/scheme/cnpg.go
Normal 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 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)
|
||||||
|
}
|
||||||
@ -870,6 +870,7 @@ rules:
|
|||||||
- postgresql.cnpg.io
|
- postgresql.cnpg.io
|
||||||
resources:
|
resources:
|
||||||
- backups
|
- backups
|
||||||
|
- clusters
|
||||||
verbs:
|
verbs:
|
||||||
- get
|
- get
|
||||||
- list
|
- list
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user