Skip to content

Commit 074eb40

Browse files
feat(config): add ignore user groups property (#1586)
* feat(config): add ignore user groups property Signed-off-by: Oliver Bähler <[email protected]> * feat(config): add ignore user groups property Signed-off-by: Oliver Bähler <[email protected]> * feat(config): add ignore user groups property Signed-off-by: Oliver Bähler <[email protected]> * feat(config): add ignore user groups property Signed-off-by: Oliver Bähler <[email protected]> * feat(config): add ignore user groups property Signed-off-by: Oliver Bähler <[email protected]> * feat(config): add ignore user groups property Signed-off-by: Oliver Bähler <[email protected]> --------- Signed-off-by: Oliver Bähler <[email protected]>
1 parent 1336ebe commit 074eb40

File tree

14 files changed

+116
-10
lines changed

14 files changed

+116
-10
lines changed

.golangci.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ linters:
2323
- unparam
2424
- varnamelen
2525
- wrapcheck
26+
- interfacebloat
2627
- noinlineerr
2728
- revive
2829
settings:

api/v1beta2/capsuleconfiguration_types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ type CapsuleConfigurationSpec struct {
1414
// Names of the groups for Capsule users.
1515
// +kubebuilder:default={capsule.clastix.io}
1616
UserGroups []string `json:"userGroups,omitempty"`
17+
// Define groups which when found in the request of a user will be ignored by the Capsule
18+
// this might be useful if you have one group where all the users are in, but you want to separate administrators from normal users with additional groups.
19+
IgnoreUserWithGroups []string `json:"ignoreUserWithGroups,omitempty"`
1720
// Enforces the Tenant owner, during Namespace creation, to name it using the selected Tenant name as prefix,
1821
// separated by a dash. This is useful to avoid Namespace name collision in a public CaaS environment.
1922
// +kubebuilder:default=false

api/v1beta2/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

charts/capsule/crds/capsule.clastix.io_capsuleconfigurations.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ spec:
5252
Enforces the Tenant owner, during Namespace creation, to name it using the selected Tenant name as prefix,
5353
separated by a dash. This is useful to avoid Namespace name collision in a public CaaS environment.
5454
type: boolean
55+
ignoreUserWithGroups:
56+
description: |-
57+
Define groups which when found in the request of a user will be ignored by the Capsule
58+
this might be useful if you have one group where all the users are in, but you want to separate administrators from normal users with additional groups.
59+
items:
60+
type: string
61+
type: array
5562
nodeMetadata:
5663
description: |-
5764
Allows to set the forbidden metadata for the worker nodes that could be patched by a Tenant.

e2e/custom_capsule_group_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@ import (
99
. "github.com/onsi/ginkgo/v2"
1010
. "github.com/onsi/gomega"
1111
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
"sigs.k8s.io/controller-runtime/pkg/client"
1213

1314
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
1415
)
1516

1617
var _ = Describe("creating a Namespace as Tenant owner with custom --capsule-group", Label("config"), func() {
18+
originConfig := &capsulev1beta2.CapsuleConfiguration{}
19+
1720
tnt := &capsulev1beta2.Tenant{
1821
ObjectMeta: metav1.ObjectMeta{
1922
Name: "tenant-assigned-custom-group",
@@ -29,13 +32,26 @@ var _ = Describe("creating a Namespace as Tenant owner with custom --capsule-gro
2932
}
3033

3134
JustBeforeEach(func() {
35+
Expect(k8sClient.Get(context.Background(), client.ObjectKey{Name: defaultConfigurationName}, originConfig)).To(Succeed())
36+
3237
EventuallyCreation(func() error {
3338
tnt.ResourceVersion = ""
3439
return k8sClient.Create(context.TODO(), tnt)
3540
}).Should(Succeed())
3641
})
3742
JustAfterEach(func() {
3843
Expect(k8sClient.Delete(context.TODO(), tnt)).Should(Succeed())
44+
45+
// Restore Configuration
46+
Eventually(func() error {
47+
c := &capsulev1beta2.CapsuleConfiguration{}
48+
if err := k8sClient.Get(context.Background(), client.ObjectKey{Name: originConfig.Name}, c); err != nil {
49+
return err
50+
}
51+
// Apply the initial configuration from originConfig to c
52+
c.Spec = originConfig.Spec
53+
return k8sClient.Update(context.Background(), c)
54+
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
3955
})
4056

4157
It("should fail using a User non matching the capsule-user-group flag", func() {
@@ -68,4 +84,16 @@ var _ = Describe("creating a Namespace as Tenant owner with custom --capsule-gro
6884
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
6985
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
7086
})
87+
88+
It("should fail when group is ignored", func() {
89+
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1beta2.CapsuleConfiguration) {
90+
configuration.Spec.UserGroups = []string{"projectcapsule.dev"}
91+
configuration.Spec.IgnoreUserWithGroups = []string{"projectcapsule.dev"}
92+
})
93+
94+
ns := NewNamespace("")
95+
96+
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).ShouldNot(Succeed())
97+
})
98+
7199
})

e2e/node_user_metadata_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,16 @@ import (
1212
corev1 "k8s.io/api/core/v1"
1313
rbacv1 "k8s.io/api/rbac/v1"
1414
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
15+
"sigs.k8s.io/controller-runtime/pkg/client"
1516

1617
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
1718
"github.com/projectcapsule/capsule/pkg/api"
1819
"github.com/projectcapsule/capsule/pkg/webhook/utils"
1920
)
2021

2122
var _ = Describe("modifying node labels and annotations", Label("config", "nodes"), func() {
23+
originConfig := &capsulev1beta2.CapsuleConfiguration{}
24+
2225
tnt := &capsulev1beta2.Tenant{
2326
ObjectMeta: metav1.ObjectMeta{
2427
Name: "tenant-node-user-metadata-forbidden",
@@ -72,6 +75,8 @@ var _ = Describe("modifying node labels and annotations", Label("config", "nodes
7275
Skip(fmt.Sprintf("Node webhook is disabled for current version %s", version.String()))
7376
}
7477

78+
Expect(k8sClient.Get(context.Background(), client.ObjectKey{Name: defaultConfigurationName}, originConfig)).To(Succeed())
79+
7580
EventuallyCreation(func() error {
7681
tnt.ResourceVersion = ""
7782
return k8sClient.Create(context.TODO(), tnt)
@@ -110,6 +115,17 @@ var _ = Describe("modifying node labels and annotations", Label("config", "nodes
110115
return k8sClient.Update(context.Background(), node)
111116
})
112117
}).Should(Succeed())
118+
119+
// Restore Configuration
120+
Eventually(func() error {
121+
c := &capsulev1beta2.CapsuleConfiguration{}
122+
if err := k8sClient.Get(context.Background(), client.ObjectKey{Name: originConfig.Name}, c); err != nil {
123+
return err
124+
}
125+
// Apply the initial configuration from originConfig to c
126+
c.Spec = originConfig.Spec
127+
return k8sClient.Update(context.Background(), c)
128+
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
113129
})
114130

115131
It("should allow", func() {
@@ -171,6 +187,19 @@ var _ = Describe("modifying node labels and annotations", Label("config", "nodes
171187
})
172188
})
173189
It("should fail", func() {
190+
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1beta2.CapsuleConfiguration) {
191+
configuration.Spec.NodeMetadata = &capsulev1beta2.NodeMetadata{
192+
ForbiddenLabels: api.ForbiddenListSpec{
193+
Exact: []string{"foo", "bar"},
194+
Regex: "^gatsby-.*$",
195+
},
196+
ForbiddenAnnotations: api.ForbiddenListSpec{
197+
Exact: []string{"foo", "bar"},
198+
Regex: "^gatsby-.*$",
199+
},
200+
}
201+
})
202+
174203
Expect(ModifyNode(func(node *corev1.Node) error {
175204
node.Labels["foo"] = "bar"
176205
node.Labels["gatsby-foo"] = "bar"

e2e/protected_namespace_regex_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@ import (
99
. "github.com/onsi/ginkgo/v2"
1010
. "github.com/onsi/gomega"
1111
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
"sigs.k8s.io/controller-runtime/pkg/client"
1213

1314
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
1415
)
1516

1617
var _ = Describe("creating a Namespace with a protected Namespace regex enabled", Label("namespace"), func() {
18+
originConfig := &capsulev1beta2.CapsuleConfiguration{}
19+
1720
tnt := &capsulev1beta2.Tenant{
1821
ObjectMeta: metav1.ObjectMeta{
1922
Name: "tenant-protected-namespace",
@@ -29,13 +32,26 @@ var _ = Describe("creating a Namespace with a protected Namespace regex enabled"
2932
}
3033

3134
JustBeforeEach(func() {
35+
Expect(k8sClient.Get(context.Background(), client.ObjectKey{Name: defaultConfigurationName}, originConfig)).To(Succeed())
36+
3237
EventuallyCreation(func() error {
3338
tnt.ResourceVersion = ""
3439
return k8sClient.Create(context.TODO(), tnt)
3540
}).Should(Succeed())
3641
})
3742
JustAfterEach(func() {
3843
Expect(k8sClient.Delete(context.TODO(), tnt)).Should(Succeed())
44+
45+
// Restore Configuration
46+
Eventually(func() error {
47+
c := &capsulev1beta2.CapsuleConfiguration{}
48+
if err := k8sClient.Get(context.Background(), client.ObjectKey{Name: originConfig.Name}, c); err != nil {
49+
return err
50+
}
51+
// Apply the initial configuration from originConfig to c
52+
c.Spec = originConfig.Spec
53+
return k8sClient.Update(context.Background(), c)
54+
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
3955
})
4056

4157
It("should succeed and be available in Tenant namespaces list", func() {

e2e/utils_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ import (
2727
)
2828

2929
const (
30-
defaultTimeoutInterval = 40 * time.Second
31-
defaultPollInterval = time.Second
30+
defaultTimeoutInterval = 40 * time.Second
31+
defaultPollInterval = time.Second
32+
defaultConfigurationName = "default"
3233
)
3334

3435
func NewService(svc types.NamespacedName) *corev1.Service {
@@ -100,7 +101,7 @@ func EventuallyCreation(f interface{}) AsyncAssertion {
100101

101102
func ModifyCapsuleConfigurationOpts(fn func(configuration *capsulev1beta2.CapsuleConfiguration)) {
102103
config := &capsulev1beta2.CapsuleConfiguration{}
103-
Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: "default"}, config)).ToNot(HaveOccurred())
104+
Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: defaultConfigurationName}, config)).ToNot(HaveOccurred())
104105

105106
fn(config)
106107

pkg/configuration/client.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ func (c *capsuleConfiguration) UserGroups() []string {
8585
return c.retrievalFn().Spec.UserGroups
8686
}
8787

88+
func (c *capsuleConfiguration) IgnoreUserWithGroups() []string {
89+
return c.retrievalFn().Spec.IgnoreUserWithGroups
90+
}
91+
8892
func (c *capsuleConfiguration) ForbiddenUserNodeLabels() *capsuleapi.ForbiddenListSpec {
8993
if c.retrievalFn().Spec.NodeMetadata == nil {
9094
return nil

pkg/configuration/configuration.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ type Configuration interface {
2424
ValidatingWebhookConfigurationName() string
2525
TenantCRDName() string
2626
UserGroups() []string
27+
IgnoreUserWithGroups() []string
2728
ForbiddenUserNodeLabels() *capsuleapi.ForbiddenListSpec
2829
ForbiddenUserNodeAnnotations() *capsuleapi.ForbiddenListSpec
2930
}

0 commit comments

Comments
 (0)