Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion api/v1alpha1/account_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ const (
// AccountSpec defines the desired state of Account
type AccountSpec struct {
// Type specifies the intended type for this Account object.
// +kubebuilder:validation:Enum=org;account
Type AccountType `json:"type"`
Comment thread
aaronschweig marked this conversation as resolved.

// The display name for this account
Expand Down
20 changes: 15 additions & 5 deletions api/v1alpha1/account_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import (
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

func SetupAccountWebhookWithManager(mgr ctrl.Manager, denyList []string) error {
func SetupAccountWebhookWithManager(mgr ctrl.Manager, organizationNameDenyList []string, accountTypeAllowList []AccountType) error {
return ctrl.NewWebhookManagedBy(mgr).
For(&Account{}).
WithDefaulter(&AccountDefaulter{}).
WithValidator(&AccountValidator{DenyList: denyList}).
WithValidator(&AccountValidator{OrganizationNameDenyList: organizationNameDenyList, AccountTypeAllowList: accountTypeAllowList}).
Complete()
}

Expand All @@ -40,11 +40,17 @@ var _ webhook.CustomDefaulter = &AccountDefaulter{}
var _ webhook.CustomValidator = &AccountValidator{}

type AccountValidator struct {
DenyList []string
OrganizationNameDenyList []string
AccountTypeAllowList []AccountType
}

func (v *AccountValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
account := obj.(*Account)

if !slices.Contains(v.AccountTypeAllowList, account.Spec.Type) {
return nil, fmt.Errorf("account type %s not in allowlist", account.Spec.Type)
}

if account.Spec.Type != AccountTypeOrg {
return nil, nil
}
Expand All @@ -53,7 +59,7 @@ func (v *AccountValidator) ValidateCreate(ctx context.Context, obj runtime.Objec
return nil, fmt.Errorf("organization name %q is too short, must be at least 3 characters", account.Name)
}

if slices.Contains(v.DenyList, account.Name) {
if slices.Contains(v.OrganizationNameDenyList, account.Name) {
return nil, fmt.Errorf("organization name %q is not allowed", account.Name)
}

Expand All @@ -63,6 +69,10 @@ func (v *AccountValidator) ValidateCreate(ctx context.Context, obj runtime.Objec
func (v *AccountValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
account := newObj.(*Account)

if !slices.Contains(v.AccountTypeAllowList, account.Spec.Type) {
return nil, fmt.Errorf("account type %s not in allowlist", account.Spec.Type)
}

if account.Spec.Type != AccountTypeOrg {
return nil, nil
}
Expand All @@ -71,7 +81,7 @@ func (v *AccountValidator) ValidateUpdate(ctx context.Context, oldObj, newObj ru
return nil, fmt.Errorf("organization name %q is too short, must be at least 3 characters", account.Name)
}

if slices.Contains(v.DenyList, account.Name) {
if slices.Contains(v.OrganizationNameDenyList, account.Name) {
return nil, fmt.Errorf("organization name %q is not allowed", account.Name)
}

Expand Down
24 changes: 12 additions & 12 deletions api/v1alpha1/account_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func TestAccountValidator_ValidateCreate(t *testing.T) {
name: "non-organization account with denied name",
account: &Account{
ObjectMeta: metav1.ObjectMeta{Name: "admin"},
Spec: AccountSpec{Type: "personal"},
Spec: AccountSpec{Type: "account"},
},
denyList: []string{"admin", "root", "system"},
expectError: false,
Expand Down Expand Up @@ -67,7 +67,7 @@ func TestAccountValidator_ValidateCreate(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
validator := &AccountValidator{DenyList: tt.denyList}
validator := &AccountValidator{OrganizationNameDenyList: tt.denyList, AccountTypeAllowList: []AccountType{AccountTypeAccount, AccountTypeOrg}}
_, err := validator.ValidateCreate(context.Background(), tt.account)

if tt.expectError {
Expand Down Expand Up @@ -117,10 +117,10 @@ func TestAccountValidator_ValidateUpdate(t *testing.T) {
expectError: false,
},
{
name: "changing from personal to organization with denied name",
name: "changing from account to organization with denied name",
oldAccount: &Account{
ObjectMeta: metav1.ObjectMeta{Name: "admin"},
Spec: AccountSpec{Type: "personal"},
Spec: AccountSpec{Type: "account"},
},
newAccount: &Account{
ObjectMeta: metav1.ObjectMeta{Name: "admin"},
Expand All @@ -131,10 +131,10 @@ func TestAccountValidator_ValidateUpdate(t *testing.T) {
errorMsg: `organization name "admin" is not allowed`,
},
{
name: "changing from personal to organization with allowed name",
name: "changing from account to organization with allowed name",
oldAccount: &Account{
ObjectMeta: metav1.ObjectMeta{Name: "my-account"},
Spec: AccountSpec{Type: "personal"},
Spec: AccountSpec{Type: "account"},
},
newAccount: &Account{
ObjectMeta: metav1.ObjectMeta{Name: "my-account"},
Expand All @@ -144,27 +144,27 @@ func TestAccountValidator_ValidateUpdate(t *testing.T) {
expectError: false,
},
{
name: "changing from organization to personal with denied name",
name: "changing from organization to account with denied name",
oldAccount: &Account{
ObjectMeta: metav1.ObjectMeta{Name: "admin"},
Spec: AccountSpec{Type: "org"},
},
newAccount: &Account{
ObjectMeta: metav1.ObjectMeta{Name: "admin"},
Spec: AccountSpec{Type: "personal"},
Spec: AccountSpec{Type: "account"},
},
denyList: []string{"admin", "root", "system"},
expectError: false,
},
{
name: "updating personal account with denied name",
name: "updating account account with denied name",
oldAccount: &Account{
ObjectMeta: metav1.ObjectMeta{Name: "admin"},
Spec: AccountSpec{Type: "personal"},
Spec: AccountSpec{Type: "account"},
},
newAccount: &Account{
ObjectMeta: metav1.ObjectMeta{Name: "admin"},
Spec: AccountSpec{Type: "personal"},
Spec: AccountSpec{Type: "account"},
},
denyList: []string{"admin", "root", "system"},
expectError: false,
Expand Down Expand Up @@ -200,7 +200,7 @@ func TestAccountValidator_ValidateUpdate(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
validator := &AccountValidator{DenyList: tt.denyList}
validator := &AccountValidator{OrganizationNameDenyList: tt.denyList, AccountTypeAllowList: []AccountType{AccountTypeAccount, AccountTypeOrg}}
_, err := validator.ValidateUpdate(context.Background(), tt.oldAccount, tt.newAccount)

if tt.expectError {
Expand Down
9 changes: 7 additions & 2 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion cmd/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,13 @@ func RunController(_ *cobra.Command, _ []string) { // coverage-ignore
}
}

accountTypeAllowList := []v1alpha1.AccountType{v1alpha1.AccountTypeOrg}
for _, additionalType := range operatorCfg.Webhooks.AdditionalAccountTypes {
accountTypeAllowList = append(accountTypeAllowList, v1alpha1.AccountType(additionalType))
}

log.Info().Strs("deniedNames", denyList).Msg("webhooks are enabled")
if err := v1alpha1.SetupAccountWebhookWithManager(mgr.GetLocalManager(), denyList); err != nil {
if err := v1alpha1.SetupAccountWebhookWithManager(mgr.GetLocalManager(), denyList, accountTypeAllowList); err != nil {
log.Fatal().Err(err).Str("webhook", "Account").Msg("unable to create webhook")
}
}
Expand Down
3 changes: 0 additions & 3 deletions config/crd/core.platform-mesh.io_accounts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,6 @@ spec:
type: array
type:
description: Type specifies the intended type for this Account object.
enum:
- org
- account
type: string
required:
- displayName
Expand Down
24 changes: 12 additions & 12 deletions config/resources/apiexport-core.platform-mesh.io.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ metadata:
name: core.platform-mesh.io
spec:
latestResourceSchemas:
- v250715-5b899f6.accountinfos.core.platform-mesh.io
- v250915-1185c0b.accounts.core.platform-mesh.io
- v250715-5b899f6.accountinfos.core.platform-mesh.io
- v260109-82344be.accounts.core.platform-mesh.io
permissionClaims:
- resource: namespaces
all: true
- group: tenancy.kcp.io
identityHash: '{{ .data.apiExportRootTenancyKcpIoIdentityHash }}'
resource: workspaces
all: true
- group: tenancy.kcp.io
identityHash: '{{ .data.apiExportRootTenancyKcpIoIdentityHash }}'
resource: workspacetypes
all: true
- all: true
resource: namespaces
- all: true
group: tenancy.kcp.io
identityHash: '{{ .data.apiExportRootTenancyKcpIoIdentityHash }}'
resource: workspaces
- all: true
group: tenancy.kcp.io
identityHash: '{{ .data.apiExportRootTenancyKcpIoIdentityHash }}'
resource: workspacetypes
status: {}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ apiVersion: apis.kcp.io/v1alpha1
kind: APIResourceSchema
metadata:
creationTimestamp: null
name: v250915-1185c0b.accounts.core.platform-mesh.io
name: v260109-82344be.accounts.core.platform-mesh.io
spec:
group: core.platform-mesh.io
names:
Expand Down Expand Up @@ -93,9 +93,6 @@ spec:
type: array
type:
description: Type specifies the intended type for this Account object.
enum:
- org
- account
type: string
required:
- displayName
Expand Down
9 changes: 5 additions & 4 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ package config
// OperatorConfig struct to hold the app config
type OperatorConfig struct {
Webhooks struct {
Enabled bool `mapstructure:"webhooks-enabled" default:"false"`
CertDir string `mapstructure:"webhooks-cert-dir" default:"certs"`
Port int `mapstructure:"webhooks-port" default:"9443"`
DenyList string `mapstructure:"webhooks-deny-list"`
Enabled bool `mapstructure:"webhooks-enabled" default:"false"`
CertDir string `mapstructure:"webhooks-cert-dir" default:"certs"`
Port int `mapstructure:"webhooks-port" default:"9443"`
DenyList string `mapstructure:"webhooks-deny-list"`
AdditionalAccountTypes []string `mapstructure:"webhooks-account-additional-types"`
Comment thread
aaronschweig marked this conversation as resolved.
Outdated
} `mapstructure:",squash"`
Subroutines struct {
WorkspaceType struct {
Expand Down
12 changes: 6 additions & 6 deletions pkg/subroutines/util/util.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package util

import "fmt"
import (
"fmt"

func GetOrgWorkspaceTypeName(accountName string) string {
return fmt.Sprintf("%s-org", accountName)
}
"github.com/platform-mesh/account-operator/api/v1alpha1"
)

func GetAccountWorkspaceTypeName(accountName string) string {
return fmt.Sprintf("%s-acc", accountName)
func GetWorkspaceTypeName(accountName string, accountType v1alpha1.AccountType) string {
return fmt.Sprintf("%s-%s", accountName, accountType)
}
10 changes: 3 additions & 7 deletions pkg/subroutines/util/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,12 @@ package util_test
import (
"testing"

"github.com/platform-mesh/account-operator/api/v1alpha1"
"github.com/platform-mesh/account-operator/pkg/subroutines/util"
"github.com/stretchr/testify/assert"
)

func TestGetOrgWorkspaceTypeName(t *testing.T) {
got := util.GetOrgWorkspaceTypeName("test")
func TestGetWorkspaceTypeName(t *testing.T) {
got := util.GetWorkspaceTypeName("test", v1alpha1.AccountTypeOrg)
assert.Equal(t, "test-org", got)
}

func TestGetAccountWorkspaceTypeName(t *testing.T) {
got := util.GetAccountWorkspaceTypeName("test")
assert.Equal(t, "test-acc", got)
}
13 changes: 4 additions & 9 deletions pkg/subroutines/workspace/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,10 @@ func (r *WorkspaceSubroutine) Process(ctx context.Context, ro runtimeobject.Runt
if err != nil {
return ctrl.Result{}, errors.NewOperatorError(err, true, true)
}

clusterClient := clusterRef.GetClient()

workspaceTypeName := util.GetOrgWorkspaceTypeName(instance.Name)
if instance.Spec.Type == corev1alpha1.AccountTypeAccount {

workspaceTypeName := util.GetWorkspaceTypeName(instance.Name, instance.Spec.Type)
if instance.Spec.Type != corev1alpha1.AccountTypeOrg {
accountInfo := &corev1alpha1.AccountInfo{}
if err := clusterClient.Get(ctx, client.ObjectKey{Name: accountinfo.DefaultAccountInfoName}, accountInfo); err != nil {
if kerrors.IsNotFound(err) {
Expand All @@ -111,8 +109,6 @@ func (r *WorkspaceSubroutine) Process(ctx context.Context, ro runtimeobject.Runt
if accountInfo.Spec.Organization.Name == "" {
return ctrl.Result{RequeueAfter: r.limiter.When(cn)}, nil
}
Comment thread
simontesar marked this conversation as resolved.

workspaceTypeName = util.GetAccountWorkspaceTypeName(accountInfo.Spec.Organization.Name)
}

ready, err := r.checkWorkspaceTypeReady(ctx, workspaceTypeName)
Expand All @@ -124,15 +120,14 @@ func (r *WorkspaceSubroutine) Process(ctx context.Context, ro runtimeobject.Runt
}

createdWorkspace := &kcptenancyv1alpha.Workspace{ObjectMeta: metav1.ObjectMeta{Name: instance.Name}}
_, err = controllerutil.CreateOrUpdate(ctx, clusterClient, createdWorkspace, func() error {
if _, err = controllerutil.CreateOrUpdate(ctx, clusterClient, createdWorkspace, func() error {
createdWorkspace.Spec.Type = &kcptenancyv1alpha.WorkspaceTypeReference{
Name: kcptenancyv1alpha.WorkspaceTypeName(workspaceTypeName),
Path: orgsWorkspacePath,
}

return controllerutil.SetOwnerReference(instance, createdWorkspace, clusterClient.Scheme())
})
if err != nil {
}); err != nil {
return ctrl.Result{}, errors.NewOperatorError(err, true, true)
}

Expand Down
8 changes: 4 additions & 4 deletions pkg/subroutines/workspacetype/workspace_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ func (w *WorkspaceTypeSubroutine) Process(ctx context.Context, ro runtimeobject.
return ctrl.Result{}, nil
}

orgWorkspaceTypeName := util.GetOrgWorkspaceTypeName(instance.Name)
accountWorkspaceTypeName := util.GetAccountWorkspaceTypeName(instance.Name)
orgWorkspaceTypeName := util.GetWorkspaceTypeName(instance.Name, instance.Spec.Type)
accountWorkspaceTypeName := util.GetWorkspaceTypeName(instance.Name, v1alpha1.AccountTypeAccount)

orgWst := generateOrgWorkspaceType(instance, orgWorkspaceTypeName, accountWorkspaceTypeName)
accWst := generateAccountWorkspaceType(instance, orgWorkspaceTypeName, accountWorkspaceTypeName)
Expand Down Expand Up @@ -85,8 +85,8 @@ func (w *WorkspaceTypeSubroutine) Finalize(ctx context.Context, ro runtimeobject
return ctrl.Result{}, nil
}

orgWorkspaceTypeName := util.GetOrgWorkspaceTypeName(instance.Name)
accountWorkspaceTypeName := util.GetAccountWorkspaceTypeName(instance.Name)
orgWorkspaceTypeName := util.GetWorkspaceTypeName(instance.Name, instance.Spec.Type)
accountWorkspaceTypeName := util.GetWorkspaceTypeName(instance.Name, v1alpha1.AccountTypeAccount)

if err := w.orgsClient.Delete(ctx, &kcptenancyv1alpha.WorkspaceType{ObjectMeta: metav1.ObjectMeta{Name: orgWorkspaceTypeName}}); err != nil {
if !kerrors.IsNotFound(err) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ metadata:
name: core.platform-mesh.io
spec:
latestResourceSchemas:
- v250715-5b899f6.accountinfos.core.platform-mesh.io
- v250915-1185c0b.accounts.core.platform-mesh.io
- v250715-5b899f6.accountinfos.core.platform-mesh.io
- v260109-82344be.accounts.core.platform-mesh.io
permissionClaims:
- resource: namespaces
all: true
- group: tenancy.kcp.io
identityHash: '{{ .data.apiExportRootTenancyKcpIoIdentityHash }}'
resource: workspaces
all: true
- group: tenancy.kcp.io
identityHash: '{{ .data.apiExportRootTenancyKcpIoIdentityHash }}'
resource: workspacetypes
all: true
- all: true
resource: namespaces
- all: true
group: tenancy.kcp.io
identityHash: '{{ .data.apiExportRootTenancyKcpIoIdentityHash }}'
resource: workspaces
- all: true
group: tenancy.kcp.io
identityHash: '{{ .data.apiExportRootTenancyKcpIoIdentityHash }}'
resource: workspacetypes
status: {}
Loading
Loading