Skip to content

Commit

Permalink
Support Groups as Subject Kind for Tenant Namespace RoleBindings crea…
Browse files Browse the repository at this point in the history
…ted by Capsule (#71)

Modified CRD to support Owner struct.

Added Tenant name validation webhook.

Rewrote owner_reference hook logic.

Updated and added new e2e tests.

Co-authored-by: Maksim Fedotov <[email protected]>
  • Loading branch information
MaxFedotov and Maksim Fedotov authored Sep 10, 2020
1 parent 10dcfea commit 303fc4d
Show file tree
Hide file tree
Showing 33 changed files with 750 additions and 82 deletions.
18 changes: 16 additions & 2 deletions api/v1alpha1/tenant_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type AdditionalMetadata struct {

// TenantSpec defines the desired state of Tenant
type TenantSpec struct {
Owner string `json:"owner"`
Owner OwnerSpec `json:"owner"`
// +kubebuilder:validation:Optional
NamespacesMetadata AdditionalMetadata `json:"namespacesMetadata"`
// +kubebuilder:validation:Optional
Expand All @@ -51,6 +51,19 @@ type TenantSpec struct {
ResourceQuota []corev1.ResourceQuotaSpec `json:"resourceQuotas"`
}

// OwnerSpec defines tenant owner name and kind
type OwnerSpec struct {
Name string `json:"name"`
Kind Kind `json:"kind"`
}

// +kubebuilder:validation:Enum=User;Group
type Kind string

func (k Kind) String() string {
return string(k)
}

// TenantStatus defines the observed state of Tenant
type TenantStatus struct {
Size uint `json:"size"`
Expand All @@ -64,7 +77,8 @@ type TenantStatus struct {
// +kubebuilder:resource:scope=Cluster,shortName=tnt
// +kubebuilder:printcolumn:name="Namespace quota",type="integer",JSONPath=".spec.namespaceQuota",description="The max amount of Namespaces can be created"
// +kubebuilder:printcolumn:name="Namespace count",type="integer",JSONPath=".status.size",description="The total amount of Namespaces in use"
// +kubebuilder:printcolumn:name="Owner",type="string",JSONPath=".spec.owner",description="The assigned Tenant owner"
// +kubebuilder:printcolumn:name="Owner name",type="string",JSONPath=".spec.owner.name",description="The assigned Tenant owner"
// +kubebuilder:printcolumn:name="Owner kind",type="string",JSONPath=".spec.owner.kind",description="The assigned Tenant owner kind"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Age"

// Tenant is the Schema for the tenants API
Expand Down
16 changes: 16 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

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

22 changes: 19 additions & 3 deletions config/crd/bases/capsule.clastix.io_tenants.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ spec:
description: The total amount of Namespaces in use
name: Namespace count
type: integer
- JSONPath: .spec.owner
- JSONPath: .spec.owner.name
description: The assigned Tenant owner
name: Owner
name: Owner name
type: string
- JSONPath: .spec.owner.kind
description: The assigned Tenant owner kind
name: Owner kind
type: string
- JSONPath: .metadata.creationTimestamp
description: Age
Expand Down Expand Up @@ -617,7 +621,19 @@ spec:
type: string
type: object
owner:
type: string
description: OwnerSpec defines tenant owner name and kind
properties:
kind:
enum:
- User
- Group
type: string
name:
type: string
required:
- kind
- name
type: object
resourceQuotas:
items:
description: ResourceQuotaSpec defines the desired hard limits to
Expand Down
4 changes: 3 additions & 1 deletion config/samples/capsule_v1alpha1_tenant.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ spec:
- Egress
nodeSelector:
kubernetes.io/os: linux
owner: alice
owner:
name: alice
kind: User
resourceQuotas:
-
hard:
Expand Down
17 changes: 17 additions & 0 deletions config/webhook/manifests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,23 @@ webhooks:
- CREATE
resources:
- persistentvolumeclaims
- clientConfig:
caBundle: Cg==
service:
name: webhook-service
namespace: system
path: /validating-v1-tenant-name
failurePolicy: Fail
name: tenant.name.capsule.clastix.io
rules:
- apiGroups:
- capsule.clastix.io
apiVersions:
- v1alpha1
operations:
- CREATE
resources:
- tenants
- clientConfig:
caBundle: Cg==
service:
Expand Down
4 changes: 2 additions & 2 deletions controllers/tenant_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -519,8 +519,8 @@ func (r *TenantReconciler) ownerRoleBinding(tenant *capsulev1alpha1.Tenant) erro
l := map[string]string{tl: tenant.Name}
s := []rbacv1.Subject{
{
Kind: "User",
Name: tenant.Spec.Owner,
Kind: tenant.Spec.Owner.Kind.String(),
Name: tenant.Spec.Owner.Name,
},
}

Expand Down
7 changes: 5 additions & 2 deletions e2e/custom_capsule_group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,13 @@ import (
var _ = Describe("creating a Namespace as Tenant owner with custom --capsule-group", func() {
tnt := &v1alpha1.Tenant{
ObjectMeta: metav1.ObjectMeta{
Name: "tenant-assigned-custom-group",
Name: "tenantassignedcustomgroup",
},
Spec: v1alpha1.TenantSpec{
Owner: "alice",
Owner: v1alpha1.OwnerSpec{
Name: "alice",
Kind: "User",
},
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
ServicesMetadata: v1alpha1.AdditionalMetadata{},
StorageClasses: []string{},
Expand Down
96 changes: 96 additions & 0 deletions e2e/force_tenant_prefix_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
//+build e2e

/*
Copyright 2020 Clastix Labs.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package e2e

import (
"context"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/clastix/capsule/api/v1alpha1"
)

var _ = Describe("creating a Namespace with --force-tenant-name flag", func() {
t1 := &v1alpha1.Tenant{
ObjectMeta: metav1.ObjectMeta{
Name: "first",
},
Spec: v1alpha1.TenantSpec{
Owner: v1alpha1.OwnerSpec{
Name: "john",
Kind: "User",
},
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
ServicesMetadata: v1alpha1.AdditionalMetadata{},
StorageClasses: []string{},
IngressClasses: []string{},
LimitRanges: []corev1.LimitRangeSpec{},
NamespaceQuota: 10,
NodeSelector: map[string]string{},
ResourceQuota: []corev1.ResourceQuotaSpec{},
},
}
t2 := &v1alpha1.Tenant{
ObjectMeta: metav1.ObjectMeta{
Name: "second",
},
Spec: v1alpha1.TenantSpec{
Owner: v1alpha1.OwnerSpec{
Name: "john",
Kind: "User",
},
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
ServicesMetadata: v1alpha1.AdditionalMetadata{},
StorageClasses: []string{},
IngressClasses: []string{},
LimitRanges: []corev1.LimitRangeSpec{},
NamespaceQuota: 10,
NodeSelector: map[string]string{},
ResourceQuota: []corev1.ResourceQuotaSpec{},
},
}
JustBeforeEach(func() {
t1.ResourceVersion = ""
t2.ResourceVersion = ""
Expect(k8sClient.Create(context.TODO(), t1)).Should(Succeed())
Expect(k8sClient.Create(context.TODO(), t2)).Should(Succeed())
})
JustAfterEach(func() {
Expect(k8sClient.Delete(context.TODO(), t1)).Should(Succeed())
Expect(k8sClient.Delete(context.TODO(), t2)).Should(Succeed())
})
It("should fail", func() {
args := append(defaulManagerPodArgs, []string{"--force-tenant-prefix"}...)
ModifyCapsuleManagerPodArgs(args)
ns := NewNamespace("test")
NamespaceCreationShouldNotSucceed(ns, t1, podRecreationTimeoutInterval)
})
It("should be assigned to the second Tenant", func() {
ns := NewNamespace("second-test")
ns2 := NewNamespace("second-test2")
NamespaceCreationShouldSucceed(ns, t2, podRecreationTimeoutInterval)
NamespaceShouldBeManagedByTenant(ns, t2, podRecreationTimeoutInterval)
NamespaceCreationShouldNotSucceed(ns2, t1, podRecreationTimeoutInterval)
args := defaulManagerPodArgs
ModifyCapsuleManagerPodArgs(args)
})
})
7 changes: 5 additions & 2 deletions e2e/ingress_class_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@ import (
var _ = Describe("when Tenant handles Ingress classes", func() {
tnt := &v1alpha1.Tenant{
ObjectMeta: metav1.ObjectMeta{
Name: "ingress-class",
Name: "ingressclass",
},
Spec: v1alpha1.TenantSpec{
Owner: "ingress",
Owner: v1alpha1.OwnerSpec{
Name: "ingress",
Kind: "User",
},
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
ServicesMetadata: v1alpha1.AdditionalMetadata{},
StorageClasses: []string{},
Expand Down
5 changes: 4 additions & 1 deletion e2e/missing_tenant_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ var _ = Describe("Namespace creation with no Tenant assigned", func() {
It("should fail", func() {
tnt := &v1alpha1.Tenant{
Spec: v1alpha1.TenantSpec{
Owner: "missing",
Owner: v1alpha1.OwnerSpec{
Name: "missing",
Kind: "User",
},
},
}
ns := NewNamespace("no-namespace")
Expand Down
7 changes: 5 additions & 2 deletions e2e/namespace_metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@ import (
var _ = Describe("creating a Namespace for a Tenant with additional metadata", func() {
tnt := &v1alpha1.Tenant{
ObjectMeta: metav1.ObjectMeta{
Name: "tenant-metadata",
Name: "tenantmetadata",
},
Spec: v1alpha1.TenantSpec{
Owner: "gatsby",
Owner: v1alpha1.OwnerSpec{
Name: "gatsby",
Kind: "User",
},
StorageClasses: []string{},
IngressClasses: []string{},
NamespacesMetadata: v1alpha1.AdditionalMetadata{
Expand Down
7 changes: 5 additions & 2 deletions e2e/new_namespace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,13 @@ import (
var _ = Describe("creating a Namespace as Tenant owner", func() {
tnt := &v1alpha1.Tenant{
ObjectMeta: metav1.ObjectMeta{
Name: "tenant-assigned",
Name: "tenantassigned",
},
Spec: v1alpha1.TenantSpec{
Owner: "alice",
Owner: v1alpha1.OwnerSpec{
Name: "alice",
Kind: "User",
},
StorageClasses: []string{},
IngressClasses: []string{},
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
Expand Down
7 changes: 5 additions & 2 deletions e2e/overquota_namespace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,13 @@ import (
var _ = Describe("creating a Namespace over-quota", func() {
tnt := &v1alpha1.Tenant{
ObjectMeta: metav1.ObjectMeta{
Name: "overquota-tenant",
Name: "overquotatenant",
},
Spec: v1alpha1.TenantSpec{
Owner: "bob",
Owner: v1alpha1.OwnerSpec{
Name: "bob",
Kind: "User",
},
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
ServicesMetadata: v1alpha1.AdditionalMetadata{},
StorageClasses: []string{},
Expand Down
7 changes: 5 additions & 2 deletions e2e/owner_webhooks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,13 @@ import (
var _ = Describe("when Tenant owner interacts with the webhooks", func() {
tnt := &v1alpha1.Tenant{
ObjectMeta: metav1.ObjectMeta{
Name: "tenant-owner",
Name: "tenantowner",
},
Spec: v1alpha1.TenantSpec{
Owner: "ruby",
Owner: v1alpha1.OwnerSpec{
Name: "ruby",
Kind: "User",
},
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
ServicesMetadata: v1alpha1.AdditionalMetadata{},
StorageClasses: []string{
Expand Down
7 changes: 5 additions & 2 deletions e2e/protected_namespace_regex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ var _ = Describe("creating a Namespace with --protected-namespace-regex enabled"
Name: "tenantprotectednamespace",
},
Spec: v1alpha1.TenantSpec{
Owner: "alice",
Owner: v1alpha1.OwnerSpec{
Name: "alice",
Kind: "User",
},
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
ServicesMetadata: v1alpha1.AdditionalMetadata{},
StorageClasses: []string{},
Expand All @@ -61,8 +64,8 @@ var _ = Describe("creating a Namespace with --protected-namespace-regex enabled"
NamespaceShouldBeManagedByTenant(ns, tnt, podRecreationTimeoutInterval)
})
It("should fail", func() {
ModifyCapsuleManagerPodArgs(defaulManagerPodArgs)
ns := NewNamespace("test-system")
NamespaceCreationShouldNotSucceed(ns, tnt, podRecreationTimeoutInterval)
ModifyCapsuleManagerPodArgs(defaulManagerPodArgs)
})
})
7 changes: 5 additions & 2 deletions e2e/resource_quota_exceeded_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,13 @@ import (
var _ = Describe("exceeding Tenant resource quota", func() {
tnt := &v1alpha1.Tenant{
ObjectMeta: metav1.ObjectMeta{
Name: "tenant-resources-changes",
Name: "tenantresourceschanges",
},
Spec: v1alpha1.TenantSpec{
Owner: "bobby",
Owner: v1alpha1.OwnerSpec{
Name: "bobby",
Kind: "User",
},
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
ServicesMetadata: v1alpha1.AdditionalMetadata{},
StorageClasses: []string{},
Expand Down
Loading

0 comments on commit 303fc4d

Please sign in to comment.