mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-08-19 04:26:45 +00:00
feat: add administrator capabilities (#2049)
* feat: implement performance optimizations Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat: add administrator capabilities Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat: add administrator capabilities Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * chore * feat: add administrator capabilities Signed-off-by: Oliver Baehler <oliver@sudo-i.net> --------- Signed-off-by: Oliver Baehler <oliver@sudo-i.net>
This commit is contained in:
@@ -195,19 +195,21 @@ var _ = Describe("Administrators", Ordered, Label("namespace", "permissions", "a
|
||||
By("creating namespace with no label", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, admin, defaultTimeoutInterval).Should(Succeed())
|
||||
DeferCleanup(func() { EventuallyDeletion(ns) })
|
||||
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, ns)).Should(Succeed())
|
||||
Expect(len(ns.OwnerReferences)).To(Equal(0))
|
||||
ExpectNamespaceNotAssignedToTenant(context.Background(), ns.Name)
|
||||
})
|
||||
|
||||
By("creating namespace with no label", func() {
|
||||
By("joining an existing unassigned namespace", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, admin, defaultTimeoutInterval).Should(Succeed())
|
||||
DeferCleanup(func() { EventuallyDeletion(ns) })
|
||||
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, ns)).Should(Succeed())
|
||||
Expect(len(ns.OwnerReferences)).To(Equal(0))
|
||||
ExpectNamespaceNotAssignedToTenant(context.Background(), ns.Name)
|
||||
|
||||
PatchTenantLabelForNamespace(tnt1, ns, ownerClient(admin), defaultTimeoutInterval).Should(Succeed())
|
||||
Eventually(func() error {
|
||||
return PatchTenantAssignmentForNamespace(tnt1, ns, ownerClient(admin))
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
NamespaceIsPartOfTenant(tnt1, ns).Should(Succeed())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -27,6 +27,13 @@ import (
|
||||
)
|
||||
|
||||
var _ = Describe("creating several Namespaces for a Tenant", Ordered, Label("config", "namespace", "hijack"), func() {
|
||||
administrator := rbac.UserSpec{
|
||||
Name: "e2e-namespace-hijacking-admin",
|
||||
Kind: rbac.UserOwner,
|
||||
}
|
||||
|
||||
var originalConfigurationSpec *capsulev1beta2.CapsuleConfigurationSpec
|
||||
|
||||
t1 := &capsulev1beta2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "e2e-ns-attack-1",
|
||||
@@ -290,6 +297,27 @@ var _ = Describe("creating several Namespaces for a Tenant", Ordered, Label("con
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
}
|
||||
|
||||
BeforeAll(func() {
|
||||
configuration := &capsulev1beta2.CapsuleConfiguration{}
|
||||
Expect(k8sClient.Get(
|
||||
context.Background(),
|
||||
types.NamespacedName{Name: defaultConfigurationName},
|
||||
configuration,
|
||||
)).To(Succeed())
|
||||
|
||||
originalConfigurationSpec = configuration.Spec.DeepCopy()
|
||||
|
||||
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1beta2.CapsuleConfiguration) {
|
||||
configuration.Spec.Administrators = append(configuration.Spec.Administrators, administrator)
|
||||
})
|
||||
})
|
||||
|
||||
AfterAll(func() {
|
||||
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1beta2.CapsuleConfiguration) {
|
||||
configuration.Spec = *originalConfigurationSpec
|
||||
})
|
||||
})
|
||||
|
||||
JustBeforeEach(func() {
|
||||
waitForTenantNamespacesDeletion(t1.Name, t2.Name, t3.Name)
|
||||
|
||||
@@ -1096,6 +1124,96 @@ var _ = Describe("creating several Namespaces for a Tenant", Ordered, Label("con
|
||||
}
|
||||
})
|
||||
|
||||
It("Administrators can migrate managed namespaces to another Tenant", func() {
|
||||
tenantA := getTenant(t1.Name)
|
||||
tenantB := getTenant(t2.Name)
|
||||
|
||||
ns := NewNamespace("", map[string]string{
|
||||
meta.TenantLabel: tenantA.GetName(),
|
||||
})
|
||||
NamespaceCreation(ns, t1.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceIsPartOfTenant(t1, ns).Should(Succeed())
|
||||
DeferCleanup(func() { EventuallyDeletion(ns) })
|
||||
|
||||
ref, err := GetTenantOwnerReferenceAsPatch(tenantB)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
patch := map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
meta.TenantLabel: tenantB.GetName(),
|
||||
},
|
||||
"ownerReferences": []map[string]interface{}{ref},
|
||||
},
|
||||
}
|
||||
|
||||
Eventually(func() error {
|
||||
return PatchNamespace(ns, ownerClient(administrator), patch)
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
|
||||
Eventually(func(g Gomega) {
|
||||
current := getNamespace(ns.Name)
|
||||
|
||||
g.Expect(current.Labels).To(HaveKeyWithValue(meta.TenantLabel, tenantB.GetName()))
|
||||
g.Expect(tenantOwnerReferences(current)).To(Equal([]string{tenantB.GetName()}))
|
||||
g.Expect(hasTenantOwnerReference(current, tenantB)).To(BeTrue())
|
||||
g.Expect(hasTenantOwnerReference(current, tenantA)).To(BeFalse())
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
|
||||
NamespaceIsNotPartOfTenant(t1, ns).Should(Succeed())
|
||||
NamespaceIsPartOfTenant(t2, ns).Should(Succeed())
|
||||
})
|
||||
|
||||
It("Administrators can remove complete Tenant ownership while partial removal is reverted", func() {
|
||||
tenant := getTenant(t1.Name)
|
||||
|
||||
ns := NewNamespace("", map[string]string{
|
||||
meta.TenantLabel: tenant.GetName(),
|
||||
})
|
||||
NamespaceCreation(ns, t1.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceIsPartOfTenant(t1, ns).Should(Succeed())
|
||||
DeferCleanup(func() { EventuallyDeletion(ns) })
|
||||
|
||||
adminClient := ownerClient(administrator)
|
||||
Eventually(func() error {
|
||||
_, err := adminClient.CoreV1().Namespaces().Patch(
|
||||
context.TODO(),
|
||||
ns.Name,
|
||||
types.MergePatchType,
|
||||
[]byte(`{"metadata":{"annotations":{"e2e.capsule.clastix.io/admin":"true"}}}`),
|
||||
metav1.PatchOptions{},
|
||||
)
|
||||
|
||||
return err
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
|
||||
Eventually(func() error {
|
||||
_, err := adminClient.CoreV1().Namespaces().Patch(
|
||||
context.TODO(),
|
||||
ns.Name,
|
||||
types.MergePatchType,
|
||||
[]byte(fmt.Sprintf(`{"metadata":{"labels":{"%s":null}}}`, meta.TenantLabel)),
|
||||
metav1.PatchOptions{},
|
||||
)
|
||||
|
||||
return err
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
expectOriginalTenantOwnership(ns.Name, tenant)
|
||||
|
||||
patch := map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
meta.TenantLabel: nil,
|
||||
},
|
||||
"ownerReferences": []interface{}{},
|
||||
},
|
||||
}
|
||||
Expect(PatchNamespace(ns, adminClient, patch)).To(Succeed())
|
||||
|
||||
ExpectNamespaceNotAssignedToTenant(context.TODO(), ns.Name)
|
||||
NamespaceIsNotPartOfTenant(t1, ns).Should(Succeed())
|
||||
})
|
||||
|
||||
It("Owners can not migrate managed namespaces to another Tenant", func() {
|
||||
tenantA := getTenant(t1.Name)
|
||||
tenantB := getTenant(t2.Name)
|
||||
@@ -1157,6 +1275,18 @@ var _ = Describe("creating several Namespaces for a Tenant", Ordered, Label("con
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
expectOriginalTenantOwnership(ns.Name, tenant)
|
||||
|
||||
patchRemoveOwnership := map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
meta.TenantLabel: nil,
|
||||
},
|
||||
"ownerReferences": []interface{}{},
|
||||
},
|
||||
}
|
||||
|
||||
Expect(PatchNamespace(ns, cs, patchRemoveOwnership)).ToNot(Succeed())
|
||||
expectOriginalTenantOwnership(ns.Name, tenant)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -0,0 +1,290 @@
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
"github.com/projectcapsule/capsule/pkg/api/rbac"
|
||||
"github.com/projectcapsule/capsule/pkg/utils"
|
||||
)
|
||||
|
||||
var _ = Describe("Capsule administrators changing existing Namespace tenant assignment", Ordered, Serial,
|
||||
Label("config", "namespace", "administrators", "assignment", "rolebindings"), func() {
|
||||
administrator := rbac.UserSpec{
|
||||
Name: "e2e-namespace-assignment-administrator",
|
||||
Kind: rbac.UserOwner,
|
||||
}
|
||||
unprivilegedAdmin := rbac.UserSpec{
|
||||
Name: "admin",
|
||||
Kind: rbac.UserOwner,
|
||||
}
|
||||
ownerA := rbac.OwnerSpec{CoreOwnerSpec: rbac.CoreOwnerSpec{
|
||||
UserSpec: rbac.UserSpec{
|
||||
Name: "e2e-namespace-assignment-owner-a",
|
||||
Kind: rbac.UserOwner,
|
||||
},
|
||||
ClusterRoles: []string{"admin"},
|
||||
}}
|
||||
ownerB := rbac.OwnerSpec{CoreOwnerSpec: rbac.CoreOwnerSpec{
|
||||
UserSpec: rbac.UserSpec{
|
||||
Name: "e2e-namespace-assignment-owner-b",
|
||||
Kind: rbac.UserOwner,
|
||||
},
|
||||
ClusterRoles: []string{"view"},
|
||||
}}
|
||||
|
||||
tenantA := &capsulev1beta2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "e2e-namespace-assignment-a",
|
||||
Labels: map[string]string{"env": "e2e"},
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{Owners: rbac.OwnerListSpec{ownerA}},
|
||||
}
|
||||
tenantB := &capsulev1beta2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "e2e-namespace-assignment-b",
|
||||
Labels: map[string]string{"env": "e2e"},
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{Owners: rbac.OwnerListSpec{ownerB}},
|
||||
}
|
||||
|
||||
bindingA := ownerA.CoreOwnerSpec.ToAdditionalRolebindings()[0]
|
||||
bindingB := ownerB.CoreOwnerSpec.ToAdditionalRolebindings()[0]
|
||||
|
||||
var originalConfigurationSpec *capsulev1beta2.CapsuleConfigurationSpec
|
||||
|
||||
BeforeAll(func() {
|
||||
configuration := &capsulev1beta2.CapsuleConfiguration{}
|
||||
Expect(k8sClient.Get(
|
||||
context.Background(),
|
||||
client.ObjectKey{Name: defaultConfigurationName},
|
||||
configuration,
|
||||
)).To(Succeed())
|
||||
|
||||
originalConfigurationSpec = configuration.Spec.DeepCopy()
|
||||
|
||||
})
|
||||
|
||||
JustBeforeEach(func() {
|
||||
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1beta2.CapsuleConfiguration) {
|
||||
configuration.Spec.Administrators = rbac.UserListSpec{administrator}
|
||||
})
|
||||
|
||||
for _, tnt := range []*capsulev1beta2.Tenant{tenantA, tenantB} {
|
||||
EventuallyCreation(func() error {
|
||||
tnt.ResourceVersion = ""
|
||||
|
||||
return k8sClient.Create(context.Background(), tnt)
|
||||
}).Should(Succeed())
|
||||
TenantReady(tnt, metav1.ConditionTrue, defaultTimeoutInterval)
|
||||
}
|
||||
})
|
||||
|
||||
JustAfterEach(func() {
|
||||
EventuallyDeletion(tenantA)
|
||||
EventuallyDeletion(tenantB)
|
||||
|
||||
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1beta2.CapsuleConfiguration) {
|
||||
configuration.Spec = *originalConfigurationSpec
|
||||
})
|
||||
})
|
||||
|
||||
newUnassignedNamespace := func() *corev1.Namespace {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, administrator, defaultTimeoutInterval).Should(Succeed())
|
||||
DeferCleanup(func() { EventuallyDeletion(ns) })
|
||||
|
||||
ExpectNamespaceNotAssignedToTenant(context.Background(), ns.Name)
|
||||
|
||||
return ns
|
||||
}
|
||||
|
||||
patchAssignment := func(
|
||||
ns *corev1.Namespace,
|
||||
tnt *capsulev1beta2.Tenant,
|
||||
cs kubernetes.Interface,
|
||||
) error {
|
||||
return PatchTenantAssignmentForNamespace(tnt, ns, cs)
|
||||
}
|
||||
|
||||
patchDetachment := func(ns *corev1.Namespace, cs kubernetes.Interface) error {
|
||||
return PatchNamespace(ns, cs, map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
meta.TenantLabel: nil,
|
||||
},
|
||||
"ownerReferences": []interface{}{},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
expectAssignment := func(
|
||||
ns *corev1.Namespace,
|
||||
assigned, unassigned *capsulev1beta2.Tenant,
|
||||
) {
|
||||
Eventually(func(g Gomega) {
|
||||
current := &corev1.Namespace{}
|
||||
g.Expect(k8sClient.Get(
|
||||
context.Background(),
|
||||
types.NamespacedName{Name: ns.Name},
|
||||
current,
|
||||
)).To(Succeed())
|
||||
g.Expect(current.Labels).To(HaveKeyWithValue(meta.TenantLabel, assigned.Name))
|
||||
g.Expect(tenantOwnerReferences(current)).To(Equal([]string{assigned.Name}))
|
||||
g.Expect(hasTenantOwnerReferenceByNameAndUID(current, assigned.Name, assigned.UID)).To(BeTrue())
|
||||
g.Expect(hasTenantOwnerReferenceByNameAndUID(current, unassigned.Name, unassigned.UID)).To(BeFalse())
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
|
||||
NamespaceIsPartOfTenant(assigned, ns).Should(Succeed())
|
||||
NamespaceIsNotPartOfTenant(unassigned, ns).Should(Succeed())
|
||||
}
|
||||
|
||||
expectRoleBindings := func(
|
||||
ns *corev1.Namespace,
|
||||
present map[*capsulev1beta2.Tenant]rbac.AdditionalRoleBindingsSpec,
|
||||
absent ...rbac.AdditionalRoleBindingsSpec,
|
||||
) {
|
||||
Eventually(func(g Gomega) {
|
||||
for tnt, binding := range present {
|
||||
name := meta.NameForManagedRoleBindings(utils.RoleBindingHashFunc(binding))
|
||||
roleBinding := &rbacv1.RoleBinding{}
|
||||
err := k8sClient.Get(context.Background(), client.ObjectKey{
|
||||
Namespace: ns.Name,
|
||||
Name: name,
|
||||
}, roleBinding)
|
||||
g.Expect(err).NotTo(HaveOccurred(),
|
||||
"expected managed RoleBinding %s/%s for Tenant %s", ns.Name, name, tnt.Name)
|
||||
g.Expect(roleBinding.RoleRef).To(Equal(rbacv1.RoleRef{
|
||||
APIGroup: rbacv1.GroupName,
|
||||
Kind: "ClusterRole",
|
||||
Name: binding.ClusterRoleName,
|
||||
}))
|
||||
g.Expect(roleBinding.Subjects).To(ConsistOf(binding.Subjects))
|
||||
g.Expect(roleBinding.Labels).To(HaveKeyWithValue(meta.NewTenantLabel, tnt.Name))
|
||||
g.Expect(roleBinding.Labels).To(HaveKeyWithValue(
|
||||
meta.NewManagedByCapsuleLabel,
|
||||
meta.ValueController,
|
||||
))
|
||||
}
|
||||
|
||||
for _, binding := range absent {
|
||||
name := meta.NameForManagedRoleBindings(utils.RoleBindingHashFunc(binding))
|
||||
roleBinding := &rbacv1.RoleBinding{}
|
||||
err := k8sClient.Get(context.Background(), client.ObjectKey{
|
||||
Namespace: ns.Name,
|
||||
Name: name,
|
||||
}, roleBinding)
|
||||
g.Expect(apierrors.IsNotFound(err)).To(BeTrue(),
|
||||
"expected managed RoleBinding %s/%s to be absent, got %v", ns.Name, name, err)
|
||||
}
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
}
|
||||
|
||||
expectUnassigned := func(ns *corev1.Namespace) {
|
||||
ExpectNamespaceNotAssignedToTenant(context.Background(), ns.Name)
|
||||
NamespaceIsNotPartOfTenant(tenantA, ns).Should(Succeed())
|
||||
NamespaceIsNotPartOfTenant(tenantB, ns).Should(Succeed())
|
||||
}
|
||||
|
||||
It("allows a configured administrator to join, migrate, and unjoin an existing Namespace", func() {
|
||||
ns := newUnassignedNamespace()
|
||||
adminClient := ownerClient(administrator)
|
||||
|
||||
By("starting without either Tenant's managed RoleBinding")
|
||||
expectRoleBindings(ns, nil, bindingA, bindingB)
|
||||
|
||||
By("joining the existing Namespace to the first Tenant")
|
||||
Eventually(func() error {
|
||||
return patchAssignment(ns, tenantA, adminClient)
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
expectAssignment(ns, tenantA, tenantB)
|
||||
expectRoleBindings(ns, map[*capsulev1beta2.Tenant]rbac.AdditionalRoleBindingsSpec{
|
||||
tenantA: bindingA,
|
||||
}, bindingB)
|
||||
|
||||
By("migrating the Namespace from the first Tenant to the second Tenant")
|
||||
Eventually(func() error {
|
||||
return patchAssignment(ns, tenantB, adminClient)
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
expectAssignment(ns, tenantB, tenantA)
|
||||
expectRoleBindings(ns, map[*capsulev1beta2.Tenant]rbac.AdditionalRoleBindingsSpec{
|
||||
tenantB: bindingB,
|
||||
}, bindingA)
|
||||
|
||||
By("unjoining the Namespace from the second Tenant")
|
||||
Eventually(func() error {
|
||||
return patchDetachment(ns, adminClient)
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
expectUnassigned(ns)
|
||||
expectRoleBindings(ns, nil, bindingA, bindingB)
|
||||
})
|
||||
|
||||
It("rejects join, migration, and unjoin attempts from other users", func() {
|
||||
actors := []struct {
|
||||
name string
|
||||
user rbac.UserSpec
|
||||
}{
|
||||
{name: "an unconfigured admin user", user: unprivilegedAdmin},
|
||||
{name: "a Tenant owner", user: ownerA.UserSpec},
|
||||
}
|
||||
|
||||
for _, actor := range actors {
|
||||
By(fmt.Sprintf("checking all assignment transitions for %s", actor.name))
|
||||
ns := newUnassignedNamespace()
|
||||
actorClient := ownerClient(actor.user)
|
||||
adminClient := ownerClient(administrator)
|
||||
|
||||
expectRoleBindings(ns, nil, bindingA, bindingB)
|
||||
|
||||
By(fmt.Sprintf("rejecting a join by %s", actor.name))
|
||||
Expect(patchAssignment(ns, tenantA, actorClient)).To(HaveOccurred())
|
||||
expectUnassigned(ns)
|
||||
expectRoleBindings(ns, nil, bindingA, bindingB)
|
||||
|
||||
By("preparing an administrator-owned transition baseline")
|
||||
Eventually(func() error {
|
||||
return patchAssignment(ns, tenantA, adminClient)
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
expectAssignment(ns, tenantA, tenantB)
|
||||
expectRoleBindings(ns, map[*capsulev1beta2.Tenant]rbac.AdditionalRoleBindingsSpec{
|
||||
tenantA: bindingA,
|
||||
}, bindingB)
|
||||
|
||||
By(fmt.Sprintf("rejecting a migration by %s", actor.name))
|
||||
Expect(patchAssignment(ns, tenantB, actorClient)).To(HaveOccurred())
|
||||
expectAssignment(ns, tenantA, tenantB)
|
||||
expectRoleBindings(ns, map[*capsulev1beta2.Tenant]rbac.AdditionalRoleBindingsSpec{
|
||||
tenantA: bindingA,
|
||||
}, bindingB)
|
||||
|
||||
By(fmt.Sprintf("rejecting an unjoin by %s", actor.name))
|
||||
Expect(patchDetachment(ns, actorClient)).To(HaveOccurred())
|
||||
expectAssignment(ns, tenantA, tenantB)
|
||||
expectRoleBindings(ns, map[*capsulev1beta2.Tenant]rbac.AdditionalRoleBindingsSpec{
|
||||
tenantA: bindingA,
|
||||
}, bindingB)
|
||||
|
||||
By("detaching with the configured administrator before cleanup")
|
||||
Eventually(func() error {
|
||||
return patchDetachment(ns, adminClient)
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
expectUnassigned(ns)
|
||||
expectRoleBindings(ns, nil, bindingA, bindingB)
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -144,18 +144,20 @@ var _ = Describe("creating a Namespace with Tenant selector when user owns multi
|
||||
|
||||
patch := map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]string{},
|
||||
"labels": map[string]interface{}{
|
||||
meta.TenantLabel: nil,
|
||||
},
|
||||
"ownerReferences": []map[string]interface{}{ref},
|
||||
},
|
||||
}
|
||||
|
||||
err = PatchNamespace(ns, ownerClient(t2.Spec.Owners[0].UserSpec), patch)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(err).To(HaveOccurred())
|
||||
|
||||
new := &corev1.Namespace{}
|
||||
k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, new)
|
||||
|
||||
NamespaceIsPartOfTenant(t1, ns).Should(Succeed())
|
||||
NamespaceIsPartOfTenant(t1, new).Should(Succeed())
|
||||
})
|
||||
|
||||
By("assigning to the Namespace the Capsule Tenant label (Empty Ownerreferences)", func() {
|
||||
@@ -169,7 +171,7 @@ var _ = Describe("creating a Namespace with Tenant selector when user owns multi
|
||||
}
|
||||
|
||||
err := PatchNamespace(ns, ownerClient(t2.Spec.Owners[0].UserSpec), patch)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(err).To(HaveOccurred())
|
||||
|
||||
new := &corev1.Namespace{}
|
||||
k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, new)
|
||||
@@ -178,22 +180,22 @@ var _ = Describe("creating a Namespace with Tenant selector when user owns multi
|
||||
})
|
||||
|
||||
By("assigning to the Namespace the Capsule Tenant label (Empty Ownerreferences) - Without Label", func() {
|
||||
ns.Labels = map[string]string{}
|
||||
|
||||
patch := map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]string{},
|
||||
"labels": map[string]interface{}{
|
||||
meta.TenantLabel: nil,
|
||||
},
|
||||
"ownerReferences": []string{},
|
||||
},
|
||||
}
|
||||
|
||||
err := PatchNamespace(ns, ownerClient(t2.Spec.Owners[0].UserSpec), patch)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(err).To(HaveOccurred())
|
||||
|
||||
new := &corev1.Namespace{}
|
||||
k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, new)
|
||||
|
||||
NamespaceIsPartOfTenant(t1, ns).Should(Succeed())
|
||||
NamespaceIsPartOfTenant(t1, new).Should(Succeed())
|
||||
})
|
||||
|
||||
By("assigning to the Namespace the Capsule Tenant label (2nd Tenant Label + Ownerreference)", func() {
|
||||
@@ -210,7 +212,7 @@ var _ = Describe("creating a Namespace with Tenant selector when user owns multi
|
||||
}
|
||||
|
||||
err = PatchNamespace(ns, ownerClient(t2.Spec.Owners[0].UserSpec), patch)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(err).To(HaveOccurred())
|
||||
|
||||
new := &corev1.Namespace{}
|
||||
k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, new)
|
||||
|
||||
+17
-11
@@ -366,18 +366,24 @@ func GetTenantOwnerReferenceAsPatch(
|
||||
|
||||
}
|
||||
|
||||
func PatchTenantLabelForNamespace(tnt *capsulev1beta2.Tenant, ns *corev1.Namespace, cs kubernetes.Interface, timeout time.Duration) AsyncAssertion {
|
||||
return Eventually(func() (err error) {
|
||||
patch := map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
meta.TenantLabel: tnt.GetName(),
|
||||
},
|
||||
},
|
||||
}
|
||||
func PatchTenantAssignmentForNamespace(
|
||||
tnt *capsulev1beta2.Tenant,
|
||||
ns *corev1.Namespace,
|
||||
cs kubernetes.Interface,
|
||||
) error {
|
||||
ref, err := GetTenantOwnerReferenceAsPatch(tnt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return PatchNamespace(ns, cs, patch)
|
||||
}, timeout, defaultPollInterval)
|
||||
return PatchNamespace(ns, cs, map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
meta.TenantLabel: tnt.GetName(),
|
||||
},
|
||||
"ownerReferences": []map[string]interface{}{ref},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func PatchNamespace(ns *corev1.Namespace, cs kubernetes.Interface, patch map[string]interface{}) error {
|
||||
|
||||
Reference in New Issue
Block a user