plugin-barman-cloud/internal/cnpgi/operator/reconciler.go
Leonardo Cecchi 2f62d539c9
feat: sidecar role and rolebinding (#23)
Signed-off-by: Leonardo Cecchi <leonardo.cecchi@enterprisedb.com>
2024-10-02 15:03:07 +02:00

213 lines
5.2 KiB
Go

package operator
import (
"context"
"fmt"
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
"github.com/cloudnative-pg/cnpg-i-machinery/pkg/pluginhelper/decoder"
"github.com/cloudnative-pg/cnpg-i-machinery/pkg/pluginhelper/object"
"github.com/cloudnative-pg/cnpg-i/pkg/reconciler"
rbacv1 "k8s.io/api/rbac/v1"
apierrs "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config"
)
// ReconcilerImplementation implements the Reconciler capability
type ReconcilerImplementation struct {
Client client.Client
reconciler.UnimplementedReconcilerHooksServer
}
// GetCapabilities implements the Reconciler interface
func (r ReconcilerImplementation) GetCapabilities(
_ context.Context,
_ *reconciler.ReconcilerHooksCapabilitiesRequest,
) (*reconciler.ReconcilerHooksCapabilitiesResult, error) {
return &reconciler.ReconcilerHooksCapabilitiesResult{
ReconcilerCapabilities: []*reconciler.ReconcilerHooksCapability{
{
Kind: reconciler.ReconcilerHooksCapability_KIND_CLUSTER,
},
{
Kind: reconciler.ReconcilerHooksCapability_KIND_BACKUP,
},
},
}, nil
}
// Pre implements the reconciler interface
func (r ReconcilerImplementation) Pre(
ctx context.Context,
request *reconciler.ReconcilerHooksRequest,
) (*reconciler.ReconcilerHooksResult, error) {
reconciledKind, err := object.GetKind(request.GetResourceDefinition())
if err != nil {
return nil, err
}
if reconciledKind != "Cluster" {
return &reconciler.ReconcilerHooksResult{
Behavior: reconciler.ReconcilerHooksResult_BEHAVIOR_CONTINUE,
}, nil
}
cluster, err := decoder.DecodeClusterJSON(request.GetResourceDefinition())
if err != nil {
return nil, err
}
pluginConfiguration, err := config.NewFromCluster(cluster)
if err != nil {
return nil, err
}
if err := r.ensureRole(ctx, cluster, pluginConfiguration.BarmanObjectName); err != nil {
return nil, err
}
if err := r.ensureRoleBinding(ctx, cluster); err != nil {
return nil, err
}
return &reconciler.ReconcilerHooksResult{
Behavior: reconciler.ReconcilerHooksResult_BEHAVIOR_CONTINUE,
}, nil
}
// Post implements the reconciler interface
func (r ReconcilerImplementation) Post(
_ context.Context,
_ *reconciler.ReconcilerHooksRequest,
) (*reconciler.ReconcilerHooksResult, error) {
return &reconciler.ReconcilerHooksResult{
Behavior: reconciler.ReconcilerHooksResult_BEHAVIOR_CONTINUE,
}, nil
}
func (r ReconcilerImplementation) ensureRole(
ctx context.Context,
cluster *cnpgv1.Cluster,
barmanObjectName string,
) error {
var role rbacv1.Role
if err := r.Client.Get(ctx, client.ObjectKey{
Namespace: cluster.Namespace,
Name: getRBACName(cluster.Name),
}, &role); err != nil {
if apierrs.IsNotFound(err) {
return r.createRole(ctx, cluster, barmanObjectName)
}
return err
}
// TODO: patch existing role
return nil
}
func (r ReconcilerImplementation) ensureRoleBinding(
ctx context.Context,
cluster *cnpgv1.Cluster,
) error {
var role rbacv1.RoleBinding
if err := r.Client.Get(ctx, client.ObjectKey{
Namespace: cluster.Namespace,
Name: getRBACName(cluster.Name),
}, &role); err != nil {
if apierrs.IsNotFound(err) {
return r.createRoleBinding(ctx, cluster)
}
return err
}
// TODO: patch existing role binding
return nil
}
func (r ReconcilerImplementation) createRole(
ctx context.Context,
cluster *cnpgv1.Cluster,
barmanObjectName string,
) error {
role := buildRole(cluster, barmanObjectName)
if err := ctrl.SetControllerReference(cluster, role, r.Client.Scheme()); err != nil {
return err
}
return r.Client.Create(ctx, role)
}
func (r ReconcilerImplementation) createRoleBinding(
ctx context.Context,
cluster *cnpgv1.Cluster,
) error {
roleBinding := buildRoleBinding(cluster)
if err := ctrl.SetControllerReference(cluster, roleBinding, r.Client.Scheme()); err != nil {
return err
}
return r.Client.Create(ctx, roleBinding)
}
func buildRole(
cluster *cnpgv1.Cluster,
barmanObjectName string,
) *rbacv1.Role {
return &rbacv1.Role{
ObjectMeta: metav1.ObjectMeta{
Namespace: cluster.Namespace,
Name: getRBACName(cluster.Name),
},
Rules: []rbacv1.PolicyRule{
{
APIGroups: []string{
"barmancloud.cnpg.io",
},
Verbs: []string{
"get",
"watch",
"list",
},
Resources: []string{
"objectstores",
},
ResourceNames: []string{
barmanObjectName,
},
},
},
}
}
func buildRoleBinding(
cluster *cnpgv1.Cluster,
) *rbacv1.RoleBinding {
return &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Namespace: cluster.Namespace,
Name: getRBACName(cluster.Name),
},
Subjects: []rbacv1.Subject{
{
Kind: "ServiceAccount",
APIGroup: "",
Name: cluster.Name,
Namespace: cluster.Namespace,
},
},
RoleRef: rbacv1.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "Role",
Name: getRBACName(cluster.Name),
},
}
}
// getRBACName returns the name of the RBAC entities for the
// barman cloud plugin
func getRBACName(clusterName string) string {
return fmt.Sprintf("%s-barman", clusterName)
}