Skip to content

Commit 4efce96

Browse files
WIP wsa pipeline
1 parent dc19693 commit 4efce96

File tree

1 file changed

+96
-12
lines changed

1 file changed

+96
-12
lines changed

internal/rules/engine.go

Lines changed: 96 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ package rules
33
import (
44
"context"
55
"fmt"
6+
"time"
67

78
"github.com/octopusdeploy/octopus-permissions-controller/api/v1beta1"
9+
"go.uber.org/multierr"
810
corev1 "k8s.io/api/core/v1"
911
rbacv1 "k8s.io/api/rbac/v1"
1012
"k8s.io/apimachinery/pkg/api/errors"
@@ -46,8 +48,9 @@ type Engine interface {
4648
}
4749

4850
type InMemoryEngine struct {
49-
rules map[AgentName]map[Scope]ServiceAccountName
50-
client client.Client
51+
rules map[AgentName]map[Scope]ServiceAccountName
52+
createdRoles map[string]string
53+
client client.Client
5154
}
5255

5356
func (s *Scope) IsEmpty() bool {
@@ -70,6 +73,59 @@ func (i *InMemoryEngine) GetServiceAccountForScope(scope Scope, agentName AgentN
7073
return "", nil
7174
}
7275

76+
func (i *InMemoryEngine) Reconcile2(ctx context.Context) error {
77+
logger := log.FromContext(ctx).WithName("engine")
78+
i.createdRoles = make(map[string]string)
79+
80+
wsaList, err := getWorkloadServiceAccounts(ctx, i.client)
81+
if err != nil {
82+
return err
83+
}
84+
85+
err = i.ensureRoles(&wsaList)
86+
if err != nil {
87+
logger.Error(err, "failed to ensure roles for workload service accounts")
88+
}
89+
90+
err = i.generateRoleBindings(&wsaList)
91+
if err != nil {
92+
logger.Error(err, "failed to ensure role bindings for workload service accounts")
93+
}
94+
95+
return nil
96+
}
97+
98+
func (i *InMemoryEngine) ensureRoles(wsaList *[]v1beta1.WorkloadServiceAccount) error {
99+
var err error
100+
for _, wsa := range *wsaList {
101+
ctx := getContextWithTimeout(time.Second * 30)
102+
if role, createErr := i.createRoleIfNeeded(ctx, wsa); createErr != nil {
103+
err = multierr.Append(err, createErr)
104+
} else if role != nil {
105+
i.createdRoles[wsa.Name] = role.Name
106+
}
107+
}
108+
return err
109+
}
110+
111+
func (i *InMemoryEngine) generateRoleBindings(wsaList *[]v1beta1.WorkloadServiceAccount) error {
112+
var err error
113+
for _, wsa := range *wsaList {
114+
if role, createErr := i.createRoleIfNeeded(ctx, wsa); createErr != nil {
115+
err = multierr.Append(err, createErr)
116+
} else if role != nil {
117+
i.createdRoles[wsa.Name] = role.Name
118+
}
119+
}
120+
return err
121+
}
122+
123+
func getContextWithTimeout(timeout time.Duration) context.Context {
124+
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(timeout))
125+
defer cancel()
126+
return ctx
127+
}
128+
73129
func (i *InMemoryEngine) Reconcile(ctx context.Context, namespace string) error {
74130
logger := log.FromContext(ctx).WithName("engine")
75131

@@ -153,12 +209,14 @@ func createServiceAccount(ctx context.Context, c client.Client, namespace string
153209
}
154210

155211
// createRoleIfNeeded creates a Role for inline permissions if they exist
156-
func createRoleIfNeeded(ctx context.Context, c client.Client, namespace string, permissions []rbacv1.PolicyRule) (string, error) {
212+
func (i *InMemoryEngine) createRoleIfNeeded(ctx context.Context, wsa v1beta1.WorkloadServiceAccount) (*rbacv1.Role, error) {
157213
logger := log.FromContext(ctx).WithName("createRoleIfNeeded")
158214

159-
if len(permissions) == 0 {
160-
return "", nil
215+
if len(wsa.Spec.Permissions.Permissions) == 0 {
216+
return nil, nil
161217
}
218+
permissions := wsa.Spec.Permissions.Permissions
219+
namespace := wsa.GetNamespace()
162220

163221
// Generate a role name based on permissions hash to ensure uniqueness
164222
permissionsHash := shortHash(fmt.Sprintf("%+v", permissions))
@@ -175,22 +233,48 @@ func createRoleIfNeeded(ctx context.Context, c client.Client, namespace string,
175233
Rules: permissions,
176234
}
177235

178-
err := c.Create(ctx, role)
236+
existingRole := &rbacv1.Role{}
237+
err := i.client.Get(ctx, client.ObjectKeyFromObject(role), existingRole)
238+
if err == nil {
239+
//TODO: Compare existing rules with desired rules and update if necessary
240+
logger.Info("Role already exists", "name", roleName)
241+
return existingRole, nil
242+
}
243+
244+
err = i.client.Create(ctx, role)
179245
if err != nil {
180246
if errors.IsAlreadyExists(err) {
181247
logger.Info("Role already exists", "name", roleName)
182-
return roleName, nil
248+
return role, nil
183249
}
184-
return "", fmt.Errorf("failed to create Role %s: %w", roleName, err)
250+
return nil, fmt.Errorf("failed to create Role %s: %w", roleName, err)
185251
}
186252

187253
logger.Info("Created Role", "name", roleName, "permissions", len(permissions))
188-
return roleName, nil
254+
return role, nil
189255
}
190256

191-
// createRoleBindings creates RoleBindings to bind the ServiceAccount to Roles and ClusterRoles
192-
func createRoleBindings(ctx context.Context, c client.Client, namespace string, serviceAccountName ServiceAccountName, permissions v1beta1.WorkloadServiceAccountPermissions, generatedRoleName string) error {
193-
logger := log.FromContext(ctx).WithName("createRoleBindings")
257+
func (i *InMemoryEngine) generateRoleBindings(ctx context.Context, wsa v1beta1.WorkloadServiceAccount) []*rbacv1.RoleBinding {
258+
logger := log.FromContext(ctx).WithName("generateRoleBindings")
259+
namespace := wsa.GetNamespace()
260+
261+
if len(wsa.Spec.Permissions.Roles) != 0 {
262+
roleRefs := wsa.Spec.Permissions.Roles
263+
264+
for _, roleRef := range roleRefs {
265+
roleBindingName := fmt.Sprintf("%s-%s-binding", wsa.Name, roleRef.Name)
266+
roleBinding := &rbacv1.RoleBinding{
267+
ObjectMeta: metav1.ObjectMeta{
268+
Name: roleBindingName,
269+
Namespace: namespace,
270+
Labels: map[string]string{
271+
PermissionsKey: "enabled",
272+
},
273+
},
274+
RoleRef: roleRef,
275+
}
276+
}
277+
}
194278

195279
// Bind to existing Roles
196280
for _, roleRef := range permissions.Roles {

0 commit comments

Comments
 (0)