Skip to content

Commit

Permalink
Simplify tenant role management; allow tenant SA to patch cluster s…
Browse files Browse the repository at this point in the history
…tatus (#272)

Allows commodore CI to set compilation metadata.

Strongly simplifies tenant role management. The PR assumes the created
roles, which always had full `controller` owner references set, are
fully controller owned. Changes to those roles get overridden by the
controller on the next reconcile.
  • Loading branch information
bastjan authored Jun 4, 2024
1 parent 5b9b4dd commit 3cd83e6
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 409 deletions.
50 changes: 0 additions & 50 deletions controllers/cluster/role.go

This file was deleted.

1 change: 0 additions & 1 deletion controllers/cluster/steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ func SpecificSteps(obj pipeline.Object, data *pipeline.Context) pipeline.Result
{Name: "delete vault entries", F: vault.HandleVaultDeletion},
{Name: "set tenant owner", F: setTenantOwner},
{Name: "apply cluster template from tenant", F: applyClusterTemplateFromTenant},
{Name: "update Role", F: clusterUpdateRole},
}

return pipeline.RunPipeline(obj, data, steps)
Expand Down
85 changes: 39 additions & 46 deletions controllers/tenant/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,73 +3,66 @@ package tenant
import (
"fmt"

"github.com/projectsyn/lieutenant-operator/api/v1alpha1"
synv1alpha1 "github.com/projectsyn/lieutenant-operator/api/v1alpha1"
"github.com/projectsyn/lieutenant-operator/pipeline"
roleUtil "github.com/projectsyn/lieutenant-operator/role"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

func createRole(obj pipeline.Object, data *pipeline.Context) pipeline.Result {
func reconcileRole(obj pipeline.Object, data *pipeline.Context) pipeline.Result {
tenant, ok := obj.(*synv1alpha1.Tenant)
if !ok {
return pipeline.Result{Err: fmt.Errorf("object is not a tenant")}
}

role, err := newRole(data.Client.Scheme(), tenant)
if err != nil {
return pipeline.Result{Err: fmt.Errorf("failed to create Role for tenant: %w", err)}
cls := v1alpha1.ClusterList{}
if err := data.Client.List(data.Context, &cls,
client.InNamespace(tenant.Namespace),
client.MatchingFields{"spec.tenantRef.name": tenant.Name},
); err != nil {
return pipeline.Result{Err: fmt.Errorf("failed to list clusters: %w", err)}
}

err = data.Client.Create(data.Context, role)
if err != nil && !errors.IsAlreadyExists(err) {
return pipeline.Result{Err: fmt.Errorf("create role: %w", err)}
clusterNames := make([]string, 0, len(cls.Items))
for _, c := range cls.Items {
clusterNames = append(clusterNames, c.Name)
}

return pipeline.Result{}
}

// newRole returns a new Role object.
// The Role controls access to the tenant and its related clusters.
func newRole(scheme *runtime.Scheme, tenant *synv1alpha1.Tenant) (*rbacv1.Role, error) {
role := &rbacv1.Role{
role := rbacv1.Role{
ObjectMeta: metav1.ObjectMeta{
Name: tenant.Name,
Namespace: tenant.Namespace,
},
}
setManagedByLabel(role)
if err := controllerutil.SetControllerReference(tenant, role, scheme); err != nil {
return nil, err
}
roleUtil.EnsureRules(role)

return role, nil
}

func tenantUpdateRole(obj pipeline.Object, data *pipeline.Context) pipeline.Result {
tenant, ok := obj.(*synv1alpha1.Tenant)
if !ok {
return pipeline.Result{Err: fmt.Errorf("object is not a tenant")}
}

name := types.NamespacedName{Name: tenant.Name, Namespace: tenant.Namespace}
role := &rbacv1.Role{}
if err := data.Client.Get(data.Context, name, role); err != nil {
return pipeline.Result{Err: fmt.Errorf("failed to get role for tenant: %v", err)}
}

if !roleUtil.AddResourceNames(role, tenant.Name) {
return pipeline.Result{}
}

if err := data.Client.Update(data.Context, role); err != nil {
return pipeline.Result{Err: fmt.Errorf("failed to update role for tenant: %v", err)}
op, err := controllerutil.CreateOrUpdate(data.Context, data.Client, &role, func() error {
role.Rules = []rbacv1.PolicyRule{
{
APIGroups: []string{synv1alpha1.GroupVersion.Group},
Verbs: []string{"get"},
Resources: []string{"tenants"},
ResourceNames: []string{tenant.Name},
},
{
APIGroups: []string{synv1alpha1.GroupVersion.Group},
Verbs: []string{"get"},
Resources: []string{"clusters"},
ResourceNames: clusterNames,
},
{
APIGroups: []string{synv1alpha1.GroupVersion.Group},
Verbs: []string{"get", "update", "patch"},
Resources: []string{"clusters/status"},
ResourceNames: clusterNames,
},
}
return controllerutil.SetControllerReference(tenant, &role, data.Client.Scheme())
})
if err != nil {
return pipeline.Result{Err: fmt.Errorf("failed to create or update role (op: %s): %w", op, err)}
}

data.Log.Info("Role reconciled", "role", role.Name, "operation", op)
return pipeline.Result{}
}
3 changes: 1 addition & 2 deletions controllers/tenant/steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ func Steps(obj pipeline.Object, data *pipeline.Context) pipeline.Result {
{Name: "uptade tenant git repo", F: updateTenantGitRepo},
{Name: "set global git repo url", F: setGlobalGitRepoURL},
{Name: "create ServiceAccount", F: createServiceAccount},
{Name: "create Role", F: createRole},
{Name: "reconcile Role", F: reconcileRole},
{Name: "create RoleBinding", F: createRoleBinding},
{Name: "update Role", F: tenantUpdateRole},
}

return pipeline.RunPipeline(obj, data, steps)
Expand Down
12 changes: 11 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"sigs.k8s.io/controller-runtime/pkg/metrics/server"
Expand Down Expand Up @@ -51,6 +52,8 @@ func init() {
}

func main() {
ctx := ctrl.SetupSignalHandler()

var metricsAddr string
var enableLeaderElection bool
var probeAddr string
Expand Down Expand Up @@ -101,6 +104,13 @@ func main() {
setupLog.Error(err, "unable to start manager")
os.Exit(1)
}
if err := mgr.GetFieldIndexer().IndexField(ctx, &synv1alpha1.Cluster{}, "spec.tenantRef.name", func(o client.Object) []string {
cluster := o.(*synv1alpha1.Cluster)
return []string{cluster.Spec.TenantRef.Name}
}); err != nil {
setupLog.Error(err, "unable to create tenantRef field indexer for Cluster")
os.Exit(1)
}

if err = (&controllers.ClusterReconciler{
Client: mgr.GetClient(),
Expand Down Expand Up @@ -140,7 +150,7 @@ func main() {
}

setupLog.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
if err := mgr.Start(ctx); err != nil {
setupLog.Error(err, "problem running manager")
os.Exit(1)
}
Expand Down
83 changes: 0 additions & 83 deletions role/role.go

This file was deleted.

Loading

0 comments on commit 3cd83e6

Please sign in to comment.