Skip to content

Commit 581a8fe

Browse files
feat(controller): administration persona (#1739)
* chore(refactor): project and api refactoring Signed-off-by: Oliver Bähler <[email protected]> * chore(refactor): project and api refactoring Signed-off-by: Oliver Bähler <[email protected]> --------- Signed-off-by: Oliver Bähler <[email protected]>
1 parent be99fc5 commit 581a8fe

File tree

254 files changed

+3725
-2327
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

254 files changed

+3725
-2327
lines changed

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ dev-setup:
153153
--set 'crds.exclusive=true'\
154154
--set 'crds.createConfig=true'\
155155
--set "webhooks.exclusive=true"\
156+
--set "webhooks.hooks.nodes.enabled=true"\
156157
--set "webhooks.service.url=$${WEBHOOK_URL}" \
157158
--set "webhooks.service.caBundle=$${CA_BUNDLE}" \
158159
capsule \
@@ -243,9 +244,12 @@ e2e-install-deps:
243244
@$(KUBECTL) apply --force-conflicts --server-side=true -f https://github.com/$(API_GW_LOOKUP)/releases/download/$(API_GW_VERSION)/standard-install.yaml
244245

245246
e2e-build: kind
247+
$(MAKE) e2e-build-cluster
248+
$(MAKE) e2e-install
249+
250+
e2e-build-cluster: kind
246251
$(KIND) create cluster --wait=60s --name $(CLUSTER_NAME) --image kindest/node:$(KUBERNETES_SUPPORTED_VERSION)
247252
$(MAKE) e2e-install-deps
248-
$(MAKE) e2e-install
249253

250254
.PHONY: e2e-install
251255
e2e-install: ko-build-all

api/v1beta1/namespace_options.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strings"
88

99
"github.com/projectcapsule/capsule/pkg/api"
10+
"github.com/projectcapsule/capsule/pkg/api/meta"
1011
)
1112

1213
type NamespaceOptions struct {
@@ -18,23 +19,23 @@ type NamespaceOptions struct {
1819
}
1920

2021
func (in *Tenant) hasForbiddenNamespaceLabelsAnnotations() bool {
21-
if _, ok := in.Annotations[api.ForbiddenNamespaceLabelsAnnotation]; ok {
22+
if _, ok := in.Annotations[meta.ForbiddenNamespaceLabelsAnnotation]; ok {
2223
return true
2324
}
2425

25-
if _, ok := in.Annotations[api.ForbiddenNamespaceLabelsRegexpAnnotation]; ok {
26+
if _, ok := in.Annotations[meta.ForbiddenNamespaceLabelsRegexpAnnotation]; ok {
2627
return true
2728
}
2829

2930
return false
3031
}
3132

3233
func (in *Tenant) hasForbiddenNamespaceAnnotationsAnnotations() bool {
33-
if _, ok := in.Annotations[api.ForbiddenNamespaceAnnotationsAnnotation]; ok {
34+
if _, ok := in.Annotations[meta.ForbiddenNamespaceAnnotationsAnnotation]; ok {
3435
return true
3536
}
3637

37-
if _, ok := in.Annotations[api.ForbiddenNamespaceAnnotationsRegexpAnnotation]; ok {
38+
if _, ok := in.Annotations[meta.ForbiddenNamespaceAnnotationsRegexpAnnotation]; ok {
3839
return true
3940
}
4041

@@ -47,8 +48,8 @@ func (in *Tenant) ForbiddenUserNamespaceLabels() *api.ForbiddenListSpec {
4748
}
4849

4950
return &api.ForbiddenListSpec{
50-
Exact: strings.Split(in.Annotations[api.ForbiddenNamespaceLabelsAnnotation], ","),
51-
Regex: in.Annotations[api.ForbiddenNamespaceLabelsRegexpAnnotation],
51+
Exact: strings.Split(in.Annotations[meta.ForbiddenNamespaceLabelsAnnotation], ","),
52+
Regex: in.Annotations[meta.ForbiddenNamespaceLabelsRegexpAnnotation],
5253
}
5354
}
5455

@@ -58,7 +59,7 @@ func (in *Tenant) ForbiddenUserNamespaceAnnotations() *api.ForbiddenListSpec {
5859
}
5960

6061
return &api.ForbiddenListSpec{
61-
Exact: strings.Split(in.Annotations[api.ForbiddenNamespaceAnnotationsAnnotation], ","),
62-
Regex: in.Annotations[api.ForbiddenNamespaceAnnotationsRegexpAnnotation],
62+
Exact: strings.Split(in.Annotations[meta.ForbiddenNamespaceAnnotationsAnnotation], ","),
63+
Regex: in.Annotations[meta.ForbiddenNamespaceAnnotationsRegexpAnnotation],
6364
}
6465
}

api/v1beta2/capsuleconfiguration_types.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ type CapsuleConfigurationSpec struct {
4141
// when not using an already provided CA and certificate, or when these are managed externally with Vault, or cert-manager.
4242
// +kubebuilder:default=true
4343
EnableTLSReconciler bool `json:"enableTLSReconciler"` //nolint:tagliatelle
44+
// Define entities which can act as Administrators in the capsule construct
45+
// These entities are automatically owners for all existing tenants. Meaning they can add namespaces to any tenant. However they must be specific by using the capsule label
46+
// for interacting with namespaces. Because if that label is not defined, it's assumed that namespace interaction was not targeted towards a tenant and will therefor
47+
// be ignored by capsule.
48+
Administrators api.UserListSpec `json:"administrators,omitempty"`
4449
}
4550

4651
type NodeMetadata struct {

api/v1beta2/resourcepool_func.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package v1beta2
55

66
import (
77
"errors"
8+
"fmt"
89
"sort"
910

1011
corev1 "k8s.io/api/core/v1"
@@ -13,6 +14,10 @@ import (
1314
"github.com/projectcapsule/capsule/pkg/api"
1415
)
1516

17+
func (r *ResourcePool) GetQuotaName() string {
18+
return fmt.Sprintf("capsule-pool-%s", r.GetName())
19+
}
20+
1621
func (r *ResourcePool) AssignNamespaces(namespaces []corev1.Namespace) {
1722
var l []string
1823

api/v1beta2/resourcepool_func_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
"k8s.io/apimachinery/pkg/types"
1313

1414
"github.com/projectcapsule/capsule/pkg/api"
15-
"github.com/projectcapsule/capsule/pkg/meta"
15+
"github.com/projectcapsule/capsule/pkg/api/meta"
1616
"github.com/stretchr/testify/assert"
1717
)
1818

api/v1beta2/resourcepoolclaim_func.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ package v1beta2
66
import (
77
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
88

9-
"github.com/projectcapsule/capsule/pkg/meta"
9+
"github.com/projectcapsule/capsule/pkg/api/meta"
1010
)
1111

1212
// Indicate the claim is bound to a resource pool.

api/v1beta2/resourcepoolclaim_func_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ package v1beta2
66
import (
77
"testing"
88

9-
"github.com/projectcapsule/capsule/pkg/meta"
9+
"github.com/projectcapsule/capsule/pkg/api/meta"
1010
"github.com/stretchr/testify/assert"
1111
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1212
)

api/v1beta2/tenant_conversion_hub.go

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
capsulev1beta1 "github.com/projectcapsule/capsule/api/v1beta1"
1414
"github.com/projectcapsule/capsule/pkg/api"
15+
"github.com/projectcapsule/capsule/pkg/api/meta"
1516
)
1617

1718
func (in *Tenant) ConvertFrom(raw conversion.Hub) error {
@@ -26,27 +27,29 @@ func (in *Tenant) ConvertFrom(raw conversion.Hub) error {
2627
}
2728

2829
in.ObjectMeta = src.ObjectMeta
29-
in.Spec.Owners = make(OwnerListSpec, 0, len(src.Spec.Owners))
30+
in.Spec.Owners = make(api.OwnerListSpec, 0, len(src.Spec.Owners))
3031

3132
for index, owner := range src.Spec.Owners {
32-
proxySettings := make([]ProxySettings, 0, len(owner.ProxyOperations))
33+
proxySettings := make([]api.ProxySettings, 0, len(owner.ProxyOperations))
3334

3435
for _, proxyOp := range owner.ProxyOperations {
35-
ops := make([]ProxyOperation, 0, len(proxyOp.Operations))
36+
ops := make([]api.ProxyOperation, 0, len(proxyOp.Operations))
3637

3738
for _, op := range proxyOp.Operations {
38-
ops = append(ops, ProxyOperation(op))
39+
ops = append(ops, api.ProxyOperation(op))
3940
}
4041

41-
proxySettings = append(proxySettings, ProxySettings{
42-
Kind: ProxyServiceKind(proxyOp.Kind),
42+
proxySettings = append(proxySettings, api.ProxySettings{
43+
Kind: api.ProxyServiceKind(proxyOp.Kind),
4344
Operations: ops,
4445
})
4546
}
4647

47-
in.Spec.Owners = append(in.Spec.Owners, OwnerSpec{
48-
Kind: OwnerKind(owner.Kind),
49-
Name: owner.Name,
48+
in.Spec.Owners = append(in.Spec.Owners, api.OwnerSpec{
49+
UserSpec: api.UserSpec{
50+
Kind: api.OwnerKind(owner.Kind),
51+
Name: owner.Name,
52+
},
5053
ClusterRoles: owner.GetRoles(*src, index),
5154
ProxyOperations: proxySettings,
5255
})
@@ -59,28 +62,28 @@ func (in *Tenant) ConvertFrom(raw conversion.Hub) error {
5962

6063
in.Spec.NamespaceOptions.AdditionalMetadata = nsOpts.AdditionalMetadata
6164

62-
if value, found := annotations[api.ForbiddenNamespaceLabelsAnnotation]; found {
65+
if value, found := annotations[meta.ForbiddenNamespaceLabelsAnnotation]; found {
6366
in.Spec.NamespaceOptions.ForbiddenLabels.Exact = strings.Split(value, ",")
6467

65-
delete(annotations, api.ForbiddenNamespaceLabelsAnnotation)
68+
delete(annotations, meta.ForbiddenNamespaceLabelsAnnotation)
6669
}
6770

68-
if value, found := annotations[api.ForbiddenNamespaceLabelsRegexpAnnotation]; found {
71+
if value, found := annotations[meta.ForbiddenNamespaceLabelsRegexpAnnotation]; found {
6972
in.Spec.NamespaceOptions.ForbiddenLabels.Regex = value
7073

71-
delete(annotations, api.ForbiddenNamespaceLabelsRegexpAnnotation)
74+
delete(annotations, meta.ForbiddenNamespaceLabelsRegexpAnnotation)
7275
}
7376

74-
if value, found := annotations[api.ForbiddenNamespaceAnnotationsAnnotation]; found {
77+
if value, found := annotations[meta.ForbiddenNamespaceAnnotationsAnnotation]; found {
7578
in.Spec.NamespaceOptions.ForbiddenAnnotations.Exact = strings.Split(value, ",")
7679

77-
delete(annotations, api.ForbiddenNamespaceAnnotationsAnnotation)
80+
delete(annotations, meta.ForbiddenNamespaceAnnotationsAnnotation)
7881
}
7982

80-
if value, found := annotations[api.ForbiddenNamespaceAnnotationsRegexpAnnotation]; found {
83+
if value, found := annotations[meta.ForbiddenNamespaceAnnotationsRegexpAnnotation]; found {
8184
in.Spec.NamespaceOptions.ForbiddenAnnotations.Regex = value
8285

83-
delete(annotations, api.ForbiddenNamespaceAnnotationsRegexpAnnotation)
86+
delete(annotations, meta.ForbiddenNamespaceAnnotationsRegexpAnnotation)
8487
}
8588
}
8689

@@ -144,10 +147,10 @@ func (in *Tenant) ConvertFrom(raw conversion.Hub) error {
144147
in.Spec.Cordoned = value
145148
}
146149

147-
if _, found := annotations[api.ProtectedTenantAnnotation]; found {
150+
if _, found := annotations[meta.ProtectedTenantAnnotation]; found {
148151
in.Spec.PreventDeletion = true
149152

150-
delete(annotations, api.ProtectedTenantAnnotation)
153+
delete(annotations, meta.ProtectedTenantAnnotation)
151154
}
152155

153156
in.SetAnnotations(annotations)
@@ -215,19 +218,19 @@ func (in *Tenant) ConvertTo(raw conversion.Hub) error {
215218
dst.Spec.NamespaceOptions.AdditionalMetadata = nsOpts.AdditionalMetadata
216219

217220
if exact := nsOpts.ForbiddenAnnotations.Exact; len(exact) > 0 {
218-
annotations[api.ForbiddenNamespaceAnnotationsAnnotation] = strings.Join(exact, ",")
221+
annotations[meta.ForbiddenNamespaceAnnotationsAnnotation] = strings.Join(exact, ",")
219222
}
220223

221224
if regex := nsOpts.ForbiddenAnnotations.Regex; len(regex) > 0 {
222-
annotations[api.ForbiddenNamespaceAnnotationsRegexpAnnotation] = regex
225+
annotations[meta.ForbiddenNamespaceAnnotationsRegexpAnnotation] = regex
223226
}
224227

225228
if exact := nsOpts.ForbiddenLabels.Exact; len(exact) > 0 {
226-
annotations[api.ForbiddenNamespaceLabelsAnnotation] = strings.Join(exact, ",")
229+
annotations[meta.ForbiddenNamespaceLabelsAnnotation] = strings.Join(exact, ",")
227230
}
228231

229232
if regex := nsOpts.ForbiddenLabels.Regex; len(regex) > 0 {
230-
annotations[api.ForbiddenNamespaceLabelsRegexpAnnotation] = regex
233+
annotations[meta.ForbiddenNamespaceLabelsRegexpAnnotation] = regex
231234
}
232235
}
233236

@@ -264,7 +267,7 @@ func (in *Tenant) ConvertTo(raw conversion.Hub) error {
264267
}
265268

266269
if in.Spec.PreventDeletion {
267-
annotations[api.ProtectedTenantAnnotation] = "true" //nolint:goconst
270+
annotations[meta.ProtectedTenantAnnotation] = "true" //nolint:goconst
268271
}
269272

270273
if in.Spec.Cordoned {

api/v1beta2/tenant_func.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,14 @@ func (in *Tenant) AssignNamespaces(namespaces []corev1.Namespace) {
3737
in.Status.Size = uint(len(l))
3838
}
3939

40-
func (in *Tenant) GetOwnerProxySettings(name string, kind OwnerKind) []ProxySettings {
40+
func (in *Tenant) GetOwnerProxySettings(name string, kind api.OwnerKind) []api.ProxySettings {
4141
return in.Spec.Owners.FindOwner(name, kind).ProxyOperations
4242
}
4343

4444
// GetClusterRolePermissions returns a map where the clusterRole is the key
4545
// and the value is a list of permission subjects (kind and name) that reference that role.
4646
// These mappings are gathered from the owners and additionalRolebindings spec.
47-
func (in *Tenant) GetSubjectsByClusterRoles(ignoreOwnerKind []OwnerKind) (rolePerms map[string][]rbacv1.Subject) {
47+
func (in *Tenant) GetSubjectsByClusterRoles(ignoreOwnerKind []api.OwnerKind) (rolePerms map[string][]rbacv1.Subject) {
4848
rolePerms = make(map[string][]rbacv1.Subject)
4949

5050
// Helper to add permissions for a given clusterRole
@@ -97,7 +97,7 @@ func (in *Tenant) GetSubjectsByClusterRoles(ignoreOwnerKind []OwnerKind) (rolePe
9797
}
9898

9999
// Get the permissions for a tenant ordered by groups and users.
100-
func (in *Tenant) GetClusterRolesBySubject(ignoreOwnerKind []OwnerKind) (maps map[string]map[string]api.TenantSubjectRoles) {
100+
func (in *Tenant) GetClusterRolesBySubject(ignoreOwnerKind []api.OwnerKind) (maps map[string]map[string]api.TenantSubjectRoles) {
101101
maps = make(map[string]map[string]api.TenantSubjectRoles)
102102

103103
// Initialize a nested map for kind ("User", "Group") and name

api/v1beta2/tenant_func_test.go

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,26 @@ import (
1313

1414
var tenant = &Tenant{
1515
Spec: TenantSpec{
16-
Owners: []OwnerSpec{
16+
Owners: []api.OwnerSpec{
1717
{
18-
Kind: "User",
19-
Name: "user1",
18+
UserSpec: api.UserSpec{
19+
Kind: "User",
20+
Name: "user1",
21+
},
2022
ClusterRoles: []string{"cluster-admin", "read-only"},
2123
},
2224
{
23-
Kind: "Group",
24-
Name: "group1",
25+
UserSpec: api.UserSpec{
26+
Kind: "Group",
27+
Name: "group1",
28+
},
2529
ClusterRoles: []string{"edit"},
2630
},
2731
{
28-
Kind: ServiceAccountOwner,
29-
Name: "service",
32+
UserSpec: api.UserSpec{
33+
Kind: api.ServiceAccountOwner,
34+
Name: "service",
35+
},
3036
ClusterRoles: []string{"read-only"},
3137
},
3238
},
@@ -96,7 +102,7 @@ func TestGetSubjectsByClusterRoles(t *testing.T) {
96102
}
97103

98104
// Ignore SubjectTypes (Ignores ServiceAccounts)
99-
ignored := tenant.GetSubjectsByClusterRoles([]OwnerKind{"ServiceAccount"})
105+
ignored := tenant.GetSubjectsByClusterRoles([]api.OwnerKind{"ServiceAccount"})
100106
expectedIgnored := map[string][]rbacv1.Subject{
101107
"cluster-admin": {
102108
{Kind: "User", Name: "user1"},
@@ -156,7 +162,7 @@ func TestGetClusterRolesBySubject(t *testing.T) {
156162
}
157163

158164
delete(expected, "ServiceAccount")
159-
ignored := tenant.GetClusterRolesBySubject([]OwnerKind{"ServiceAccount"})
165+
ignored := tenant.GetClusterRolesBySubject([]api.OwnerKind{"ServiceAccount"})
160166

161167
if !reflect.DeepEqual(ignored, expected) {
162168
t.Errorf("Expected %v, but got %v", expected, ignored)

0 commit comments

Comments
 (0)