mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-08-19 04:26:45 +00:00
Only owner Tenant specification key is mandatory (#153)
* Only Tenant owner specification key is mandatory * Increasing default timeout to avoid e2e flakiness on GH Actions * Ensuring also empty Namespace annotations and labels
This commit is contained in:
@@ -23,7 +23,11 @@ import (
|
||||
)
|
||||
|
||||
func (t *Tenant) IsFull() bool {
|
||||
return t.Status.Namespaces.Len() >= int(t.Spec.NamespaceQuota)
|
||||
// we don't have limits on assigned Namespaces
|
||||
if t.Spec.NamespaceQuota == nil {
|
||||
return false
|
||||
}
|
||||
return t.Status.Namespaces.Len() >= int(*t.Spec.NamespaceQuota)
|
||||
}
|
||||
|
||||
func (t *Tenant) AssignNamespaces(namespaces []corev1.Namespace) {
|
||||
|
||||
@@ -23,55 +23,42 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// +kubebuilder:validation:Minimum=1
|
||||
type NamespaceQuota uint
|
||||
|
||||
type AdditionalMetadata struct {
|
||||
// +nullable
|
||||
AdditionalLabels map[string]string `json:"additionalLabels"`
|
||||
// +nullable
|
||||
AdditionalAnnotations map[string]string `json:"additionalAnnotations"`
|
||||
AdditionalLabels map[string]string `json:"additionalLabels,omitempty"`
|
||||
AdditionalAnnotations map[string]string `json:"additionalAnnotations,omitempty"`
|
||||
}
|
||||
|
||||
type StorageClassesSpec struct {
|
||||
// +nullable
|
||||
Allowed StorageClassList `json:"allowed"`
|
||||
// +nullable
|
||||
AllowedRegex string `json:"allowedRegex"`
|
||||
Allowed StorageClassList `json:"allowed,omitempty"`
|
||||
AllowedRegex string `json:"allowedRegex,omitempty"`
|
||||
}
|
||||
|
||||
type IngressClassesSpec struct {
|
||||
// +nullable
|
||||
Allowed IngressClassList `json:"allowed"`
|
||||
// +nullable
|
||||
AllowedRegex string `json:"allowedRegex"`
|
||||
Allowed IngressClassList `json:"allowed,omitempty"`
|
||||
AllowedRegex string `json:"allowedRegex,omitempty"`
|
||||
}
|
||||
|
||||
type ContainerRegistriesSpec struct {
|
||||
// +nullable
|
||||
Allowed RegistryList `json:"allowed"`
|
||||
// +nullable
|
||||
AllowedRegex string `json:"allowedRegex"`
|
||||
Allowed RegistryList `json:"allowed,omitempty"`
|
||||
AllowedRegex string `json:"allowedRegex,omitempty"`
|
||||
}
|
||||
|
||||
// TenantSpec defines the desired state of Tenant
|
||||
type TenantSpec struct {
|
||||
Owner OwnerSpec `json:"owner"`
|
||||
// +kubebuilder:validation:Optional
|
||||
NamespacesMetadata AdditionalMetadata `json:"namespacesMetadata"`
|
||||
// +kubebuilder:validation:Optional
|
||||
ServicesMetadata AdditionalMetadata `json:"servicesMetadata"`
|
||||
StorageClasses StorageClassesSpec `json:"storageClasses"`
|
||||
IngressClasses IngressClassesSpec `json:"ingressClasses"`
|
||||
ContainerRegistries *ContainerRegistriesSpec `json:"containerRegistries,omitempty"`
|
||||
// +kubebuilder:validation:Optional
|
||||
NodeSelector map[string]string `json:"nodeSelector"`
|
||||
NamespaceQuota NamespaceQuota `json:"namespaceQuota"`
|
||||
NetworkPolicies []networkingv1.NetworkPolicySpec `json:"networkPolicies,omitempty"`
|
||||
LimitRanges []corev1.LimitRangeSpec `json:"limitRanges"`
|
||||
// +kubebuilder:validation:Optional
|
||||
ResourceQuota []corev1.ResourceQuotaSpec `json:"resourceQuotas"`
|
||||
AdditionalRoleBindings []AdditionalRoleBindings `json:"additionalRoleBindings,omitempty"`
|
||||
|
||||
//+kubebuilder:validation:Minimum=1
|
||||
NamespaceQuota *int32 `json:"namespaceQuota,omitempty"`
|
||||
NamespacesMetadata AdditionalMetadata `json:"namespacesMetadata,omitempty"`
|
||||
ServicesMetadata AdditionalMetadata `json:"servicesMetadata,omitempty"`
|
||||
StorageClasses *StorageClassesSpec `json:"storageClasses,omitempty"`
|
||||
IngressClasses *IngressClassesSpec `json:"ingressClasses,omitempty"`
|
||||
ContainerRegistries *ContainerRegistriesSpec `json:"containerRegistries,omitempty"`
|
||||
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
|
||||
NetworkPolicies []networkingv1.NetworkPolicySpec `json:"networkPolicies,omitempty"`
|
||||
LimitRanges []corev1.LimitRangeSpec `json:"limitRanges,omitempty"`
|
||||
ResourceQuota []corev1.ResourceQuotaSpec `json:"resourceQuotas,omitempty"`
|
||||
AdditionalRoleBindings []AdditionalRoleBindings `json:"additionalRoleBindings,omitempty"`
|
||||
}
|
||||
|
||||
type AdditionalRoleBindings struct {
|
||||
@@ -97,8 +84,6 @@ func (k Kind) String() string {
|
||||
type TenantStatus struct {
|
||||
Size uint `json:"size"`
|
||||
Namespaces NamespaceList `json:"namespaces,omitempty"`
|
||||
Users []string `json:"users,omitempty"`
|
||||
Groups []string `json:"groups,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
@@ -290,10 +290,23 @@ func (in *TenantList) DeepCopyObject() runtime.Object {
|
||||
func (in *TenantSpec) DeepCopyInto(out *TenantSpec) {
|
||||
*out = *in
|
||||
out.Owner = in.Owner
|
||||
if in.NamespaceQuota != nil {
|
||||
in, out := &in.NamespaceQuota, &out.NamespaceQuota
|
||||
*out = new(int32)
|
||||
**out = **in
|
||||
}
|
||||
in.NamespacesMetadata.DeepCopyInto(&out.NamespacesMetadata)
|
||||
in.ServicesMetadata.DeepCopyInto(&out.ServicesMetadata)
|
||||
in.StorageClasses.DeepCopyInto(&out.StorageClasses)
|
||||
in.IngressClasses.DeepCopyInto(&out.IngressClasses)
|
||||
if in.StorageClasses != nil {
|
||||
in, out := &in.StorageClasses, &out.StorageClasses
|
||||
*out = new(StorageClassesSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.IngressClasses != nil {
|
||||
in, out := &in.IngressClasses, &out.IngressClasses
|
||||
*out = new(IngressClassesSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ContainerRegistries != nil {
|
||||
in, out := &in.ContainerRegistries, &out.ContainerRegistries
|
||||
*out = new(ContainerRegistriesSpec)
|
||||
@@ -354,16 +367,6 @@ func (in *TenantStatus) DeepCopyInto(out *TenantStatus) {
|
||||
*out = make(NamespaceList, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Users != nil {
|
||||
in, out := &in.Users, &out.Users
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Groups != nil {
|
||||
in, out := &in.Groups, &out.Groups
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantStatus.
|
||||
|
||||
@@ -7,37 +7,37 @@ metadata:
|
||||
name: tenants.capsule.clastix.io
|
||||
spec:
|
||||
additionalPrinterColumns:
|
||||
- JSONPath: .spec.namespaceQuota
|
||||
description: The max amount of Namespaces can be created
|
||||
name: Namespace quota
|
||||
type: integer
|
||||
- JSONPath: .status.size
|
||||
description: The total amount of Namespaces in use
|
||||
name: Namespace count
|
||||
type: integer
|
||||
- JSONPath: .spec.owner.name
|
||||
description: The assigned Tenant owner
|
||||
name: Owner name
|
||||
type: string
|
||||
- JSONPath: .spec.owner.kind
|
||||
description: The assigned Tenant owner kind
|
||||
name: Owner kind
|
||||
type: string
|
||||
- JSONPath: .spec.nodeSelector
|
||||
description: Node Selector applied to Pods
|
||||
name: Node selector
|
||||
type: string
|
||||
- JSONPath: .metadata.creationTimestamp
|
||||
description: Age
|
||||
name: Age
|
||||
type: date
|
||||
- JSONPath: .spec.namespaceQuota
|
||||
description: The max amount of Namespaces can be created
|
||||
name: Namespace quota
|
||||
type: integer
|
||||
- JSONPath: .status.size
|
||||
description: The total amount of Namespaces in use
|
||||
name: Namespace count
|
||||
type: integer
|
||||
- JSONPath: .spec.owner.name
|
||||
description: The assigned Tenant owner
|
||||
name: Owner name
|
||||
type: string
|
||||
- JSONPath: .spec.owner.kind
|
||||
description: The assigned Tenant owner kind
|
||||
name: Owner kind
|
||||
type: string
|
||||
- JSONPath: .spec.nodeSelector
|
||||
description: Node Selector applied to Pods
|
||||
name: Node selector
|
||||
type: string
|
||||
- JSONPath: .metadata.creationTimestamp
|
||||
description: Age
|
||||
name: Age
|
||||
type: date
|
||||
group: capsule.clastix.io
|
||||
names:
|
||||
kind: Tenant
|
||||
listKind: TenantList
|
||||
plural: tenants
|
||||
shortNames:
|
||||
- tnt
|
||||
- tnt
|
||||
singular: tenant
|
||||
scope: Cluster
|
||||
subresources:
|
||||
@@ -61,20 +61,6 @@ spec:
|
||||
spec:
|
||||
description: TenantSpec defines the desired state of Tenant
|
||||
properties:
|
||||
containerRegistries:
|
||||
properties:
|
||||
allowed:
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
allowedRegex:
|
||||
nullable: true
|
||||
type: string
|
||||
required:
|
||||
- allowed
|
||||
- allowedRegex
|
||||
type: object
|
||||
additionalRoleBindings:
|
||||
items:
|
||||
properties:
|
||||
@@ -109,28 +95,38 @@ spec:
|
||||
an error.
|
||||
type: string
|
||||
required:
|
||||
- kind
|
||||
- name
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- clusterRoleName
|
||||
- subjects
|
||||
- clusterRoleName
|
||||
- subjects
|
||||
type: object
|
||||
type: array
|
||||
containerRegistries:
|
||||
properties:
|
||||
allowed:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
allowedRegex:
|
||||
type: string
|
||||
required:
|
||||
- allowed
|
||||
- allowedRegex
|
||||
type: object
|
||||
ingressClasses:
|
||||
properties:
|
||||
allowed:
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
allowedRegex:
|
||||
nullable: true
|
||||
type: string
|
||||
required:
|
||||
- allowed
|
||||
- allowedRegex
|
||||
- allowed
|
||||
- allowedRegex
|
||||
type: object
|
||||
limitRanges:
|
||||
items:
|
||||
@@ -147,8 +143,8 @@ spec:
|
||||
default:
|
||||
additionalProperties:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
- type: integer
|
||||
- type: string
|
||||
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
|
||||
x-kubernetes-int-or-string: true
|
||||
description: Default resource requirement limit value by
|
||||
@@ -157,8 +153,8 @@ spec:
|
||||
defaultRequest:
|
||||
additionalProperties:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
- type: integer
|
||||
- type: string
|
||||
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
|
||||
x-kubernetes-int-or-string: true
|
||||
description: DefaultRequest is the default resource requirement
|
||||
@@ -168,8 +164,8 @@ spec:
|
||||
max:
|
||||
additionalProperties:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
- type: integer
|
||||
- type: string
|
||||
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
|
||||
x-kubernetes-int-or-string: true
|
||||
description: Max usage constraints on this kind by resource
|
||||
@@ -178,8 +174,8 @@ spec:
|
||||
maxLimitRequestRatio:
|
||||
additionalProperties:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
- type: integer
|
||||
- type: string
|
||||
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
|
||||
x-kubernetes-int-or-string: true
|
||||
description: MaxLimitRequestRatio if specified, the named
|
||||
@@ -191,8 +187,8 @@ spec:
|
||||
min:
|
||||
additionalProperties:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
- type: integer
|
||||
- type: string
|
||||
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
|
||||
x-kubernetes-int-or-string: true
|
||||
description: Min usage constraints on this kind by resource
|
||||
@@ -202,31 +198,29 @@ spec:
|
||||
description: Type of resource that this limit applies to.
|
||||
type: string
|
||||
required:
|
||||
- type
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- limits
|
||||
- limits
|
||||
type: object
|
||||
type: array
|
||||
namespaceQuota:
|
||||
minimum: 1
|
||||
format: int32
|
||||
type: integer
|
||||
namespacesMetadata:
|
||||
properties:
|
||||
additionalAnnotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
nullable: true
|
||||
type: object
|
||||
additionalLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
nullable: true
|
||||
type: object
|
||||
required:
|
||||
- additionalAnnotations
|
||||
- additionalLabels
|
||||
- additionalAnnotations
|
||||
- additionalLabels
|
||||
type: object
|
||||
networkPolicies:
|
||||
items:
|
||||
@@ -261,8 +255,8 @@ spec:
|
||||
properties:
|
||||
port:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
- type: integer
|
||||
- type: string
|
||||
description: The port on the given protocol. This
|
||||
can either be a numerical or named port on a pod.
|
||||
If this field is not provided, this matches all
|
||||
@@ -308,7 +302,7 @@ spec:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- cidr
|
||||
- cidr
|
||||
type: object
|
||||
namespaceSelector:
|
||||
description: "Selects Namespaces using cluster-scoped
|
||||
@@ -349,8 +343,8 @@ spec:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
matchLabels:
|
||||
@@ -403,8 +397,8 @@ spec:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
matchLabels:
|
||||
@@ -470,7 +464,7 @@ spec:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- cidr
|
||||
- cidr
|
||||
type: object
|
||||
namespaceSelector:
|
||||
description: "Selects Namespaces using cluster-scoped
|
||||
@@ -511,8 +505,8 @@ spec:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
matchLabels:
|
||||
@@ -565,8 +559,8 @@ spec:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
matchLabels:
|
||||
@@ -596,8 +590,8 @@ spec:
|
||||
properties:
|
||||
port:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
- type: integer
|
||||
- type: string
|
||||
description: The port on the given protocol. This
|
||||
can either be a numerical or named port on a pod.
|
||||
If this field is not provided, this matches all
|
||||
@@ -648,8 +642,8 @@ spec:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
matchLabels:
|
||||
@@ -682,7 +676,7 @@ spec:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- podSelector
|
||||
- podSelector
|
||||
type: object
|
||||
type: array
|
||||
nodeSelector:
|
||||
@@ -694,14 +688,14 @@ spec:
|
||||
properties:
|
||||
kind:
|
||||
enum:
|
||||
- User
|
||||
- Group
|
||||
- User
|
||||
- Group
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
required:
|
||||
- kind
|
||||
- name
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
resourceQuotas:
|
||||
items:
|
||||
@@ -711,8 +705,8 @@ spec:
|
||||
hard:
|
||||
additionalProperties:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
- type: integer
|
||||
- type: string
|
||||
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
|
||||
x-kubernetes-int-or-string: true
|
||||
description: 'hard is the set of desired hard limits for each
|
||||
@@ -752,8 +746,8 @@ spec:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- operator
|
||||
- scopeName
|
||||
- operator
|
||||
- scopeName
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
@@ -773,37 +767,29 @@ spec:
|
||||
additionalAnnotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
nullable: true
|
||||
type: object
|
||||
additionalLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
nullable: true
|
||||
type: object
|
||||
required:
|
||||
- additionalAnnotations
|
||||
- additionalLabels
|
||||
- additionalAnnotations
|
||||
- additionalLabels
|
||||
type: object
|
||||
storageClasses:
|
||||
properties:
|
||||
allowed:
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
allowedRegex:
|
||||
nullable: true
|
||||
type: string
|
||||
required:
|
||||
- allowed
|
||||
- allowedRegex
|
||||
- allowed
|
||||
- allowedRegex
|
||||
type: object
|
||||
required:
|
||||
- ingressClasses
|
||||
- limitRanges
|
||||
- namespaceQuota
|
||||
- owner
|
||||
- storageClasses
|
||||
- owner
|
||||
type: object
|
||||
status:
|
||||
description: TenantStatus defines the observed state of Tenant
|
||||
@@ -823,14 +809,14 @@ spec:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- size
|
||||
- size
|
||||
type: object
|
||||
type: object
|
||||
version: v1alpha1
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
|
||||
@@ -111,28 +111,18 @@ spec:
|
||||
allowed:
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
allowedRegex:
|
||||
nullable: true
|
||||
type: string
|
||||
required:
|
||||
- allowed
|
||||
- allowedRegex
|
||||
type: object
|
||||
ingressClasses:
|
||||
properties:
|
||||
allowed:
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
allowedRegex:
|
||||
nullable: true
|
||||
type: string
|
||||
required:
|
||||
- allowed
|
||||
- allowedRegex
|
||||
type: object
|
||||
limitRanges:
|
||||
items:
|
||||
@@ -212,6 +202,7 @@ spec:
|
||||
type: object
|
||||
type: array
|
||||
namespaceQuota:
|
||||
format: int32
|
||||
minimum: 1
|
||||
type: integer
|
||||
namespacesMetadata:
|
||||
@@ -219,16 +210,11 @@ spec:
|
||||
additionalAnnotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
nullable: true
|
||||
type: object
|
||||
additionalLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
nullable: true
|
||||
type: object
|
||||
required:
|
||||
- additionalAnnotations
|
||||
- additionalLabels
|
||||
type: object
|
||||
networkPolicies:
|
||||
items:
|
||||
@@ -775,55 +761,33 @@ spec:
|
||||
additionalAnnotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
nullable: true
|
||||
type: object
|
||||
additionalLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
nullable: true
|
||||
type: object
|
||||
required:
|
||||
- additionalAnnotations
|
||||
- additionalLabels
|
||||
type: object
|
||||
storageClasses:
|
||||
properties:
|
||||
allowed:
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
allowedRegex:
|
||||
nullable: true
|
||||
type: string
|
||||
required:
|
||||
- allowed
|
||||
- allowedRegex
|
||||
type: object
|
||||
required:
|
||||
- ingressClasses
|
||||
- limitRanges
|
||||
- namespaceQuota
|
||||
- owner
|
||||
- storageClasses
|
||||
type: object
|
||||
status:
|
||||
description: TenantStatus defines the observed state of Tenant
|
||||
properties:
|
||||
groups:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
namespaces:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
size:
|
||||
type: integer
|
||||
users:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- size
|
||||
type: object
|
||||
|
||||
@@ -478,23 +478,38 @@ func (r *TenantReconciler) syncNamespace(namespace string, tnt *capsulev1alpha1.
|
||||
if a == nil {
|
||||
a = make(map[string]string)
|
||||
}
|
||||
if len(tnt.Spec.IngressClasses.Allowed) > 0 {
|
||||
a[capsulev1alpha1.AvailableIngressClassesAnnotation] = strings.Join(tnt.Spec.IngressClasses.Allowed, ",")
|
||||
|
||||
// resetting Capsule annotations
|
||||
delete(a, capsulev1alpha1.AvailableIngressClassesAnnotation)
|
||||
delete(a, capsulev1alpha1.AvailableIngressClassesRegexpAnnotation)
|
||||
delete(a, capsulev1alpha1.AvailableStorageClassesAnnotation)
|
||||
delete(a, capsulev1alpha1.AvailableStorageClassesRegexpAnnotation)
|
||||
delete(a, capsulev1alpha1.AllowedRegistriesAnnotation)
|
||||
delete(a, capsulev1alpha1.AllowedRegistriesRegexpAnnotation)
|
||||
|
||||
if tnt.Spec.IngressClasses != nil {
|
||||
if len(tnt.Spec.IngressClasses.Allowed) > 0 {
|
||||
a[capsulev1alpha1.AvailableIngressClassesAnnotation] = strings.Join(tnt.Spec.IngressClasses.Allowed, ",")
|
||||
}
|
||||
if len(tnt.Spec.IngressClasses.AllowedRegex) > 0 {
|
||||
a[capsulev1alpha1.AvailableIngressClassesRegexpAnnotation] = tnt.Spec.IngressClasses.AllowedRegex
|
||||
}
|
||||
}
|
||||
if len(tnt.Spec.IngressClasses.AllowedRegex) > 0 {
|
||||
a[capsulev1alpha1.AvailableIngressClassesRegexpAnnotation] = tnt.Spec.IngressClasses.AllowedRegex
|
||||
if tnt.Spec.StorageClasses != nil {
|
||||
if len(tnt.Spec.StorageClasses.Allowed) > 0 {
|
||||
a[capsulev1alpha1.AvailableStorageClassesAnnotation] = strings.Join(tnt.Spec.StorageClasses.Allowed, ",")
|
||||
}
|
||||
if len(tnt.Spec.StorageClasses.AllowedRegex) > 0 {
|
||||
a[capsulev1alpha1.AvailableStorageClassesRegexpAnnotation] = tnt.Spec.StorageClasses.AllowedRegex
|
||||
}
|
||||
}
|
||||
if len(tnt.Spec.StorageClasses.Allowed) > 0 {
|
||||
a[capsulev1alpha1.AvailableStorageClassesAnnotation] = strings.Join(tnt.Spec.StorageClasses.Allowed, ",")
|
||||
}
|
||||
if len(tnt.Spec.StorageClasses.AllowedRegex) > 0 {
|
||||
a[capsulev1alpha1.AvailableStorageClassesRegexpAnnotation] = tnt.Spec.StorageClasses.AllowedRegex
|
||||
}
|
||||
if tnt.Spec.ContainerRegistries != nil && len(tnt.Spec.ContainerRegistries.Allowed) > 0 {
|
||||
a[capsulev1alpha1.AllowedRegistriesAnnotation] = strings.Join(tnt.Spec.ContainerRegistries.Allowed, ",")
|
||||
}
|
||||
if tnt.Spec.ContainerRegistries != nil && len(tnt.Spec.ContainerRegistries.AllowedRegex) > 0 {
|
||||
a[capsulev1alpha1.AllowedRegistriesRegexpAnnotation] = tnt.Spec.ContainerRegistries.AllowedRegex
|
||||
if tnt.Spec.ContainerRegistries != nil {
|
||||
if len(tnt.Spec.ContainerRegistries.Allowed) > 0 {
|
||||
a[capsulev1alpha1.AllowedRegistriesAnnotation] = strings.Join(tnt.Spec.ContainerRegistries.Allowed, ",")
|
||||
}
|
||||
if len(tnt.Spec.ContainerRegistries.AllowedRegex) > 0 {
|
||||
a[capsulev1alpha1.AllowedRegistriesRegexpAnnotation] = tnt.Spec.ContainerRegistries.AllowedRegex
|
||||
}
|
||||
}
|
||||
|
||||
if aa := tnt.Spec.NamespacesMetadata.AdditionalAnnotations; aa != nil {
|
||||
@@ -651,16 +666,7 @@ func (r *TenantReconciler) ownerRoleBinding(tenant *capsulev1alpha1.Tenant) erro
|
||||
}
|
||||
|
||||
func (r *TenantReconciler) ensureNodeSelector(tenant *capsulev1alpha1.Tenant) (err error) {
|
||||
if tenant.Spec.NodeSelector == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, namespace := range tenant.Status.Namespaces {
|
||||
selectorMap := tenant.Spec.NodeSelector
|
||||
if selectorMap == nil {
|
||||
return
|
||||
}
|
||||
|
||||
ns := &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: namespace,
|
||||
@@ -673,7 +679,7 @@ func (r *TenantReconciler) ensureNodeSelector(tenant *capsulev1alpha1.Tenant) (e
|
||||
ns.Annotations = make(map[string]string)
|
||||
}
|
||||
var selector []string
|
||||
for k, v := range selectorMap {
|
||||
for k, v := range tenant.Spec.NodeSelector {
|
||||
selector = append(selector, fmt.Sprintf("%s=%s", k, v))
|
||||
}
|
||||
ns.Annotations["scheduler.alpha.kubernetes.io/node-selector"] = strings.Join(selector, ",")
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
@@ -41,14 +40,6 @@ var _ = Describe("creating a Namespace with additional Role Binding", func() {
|
||||
Name: "dale",
|
||||
Kind: "User",
|
||||
},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{},
|
||||
LimitRanges: []corev1.LimitRangeSpec{},
|
||||
NamespaceQuota: 10,
|
||||
NodeSelector: map[string]string{},
|
||||
ResourceQuota: []corev1.ResourceQuotaSpec{},
|
||||
AdditionalRoleBindings: []v1alpha1.AdditionalRoleBindings{
|
||||
{
|
||||
ClusterRoleName: "crds-rolebinding",
|
||||
|
||||
@@ -46,10 +46,9 @@ var _ = Describe("enforcing a Container Registry", func() {
|
||||
},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
StorageClasses: &v1alpha1.StorageClassesSpec{},
|
||||
IngressClasses: &v1alpha1.IngressClassesSpec{},
|
||||
LimitRanges: []corev1.LimitRangeSpec{},
|
||||
NamespaceQuota: 10,
|
||||
NodeSelector: map[string]string{},
|
||||
ResourceQuota: []corev1.ResourceQuotaSpec{},
|
||||
},
|
||||
@@ -72,7 +71,7 @@ var _ = Describe("enforcing a Container Registry", func() {
|
||||
}
|
||||
|
||||
for a, expected := range map[string]string{
|
||||
"capsule.clastix.io/allowed-registries": "docker.io,docker.tld",
|
||||
"capsule.clastix.io/allowed-registries": "docker.io,docker.tld",
|
||||
"capsule.clastix.io/allowed-registries-regexp": `quay\.\w+`,
|
||||
} {
|
||||
var v string
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
|
||||
. "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"
|
||||
@@ -39,14 +38,6 @@ var _ = Describe("creating a Namespace as Tenant owner with custom --capsule-gro
|
||||
Name: "alice",
|
||||
Kind: "User",
|
||||
},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
LimitRanges: []corev1.LimitRangeSpec{},
|
||||
NamespaceQuota: 10,
|
||||
NodeSelector: map[string]string{},
|
||||
ResourceQuota: []corev1.ResourceQuotaSpec{},
|
||||
},
|
||||
}
|
||||
JustBeforeEach(func() {
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
|
||||
. "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"
|
||||
@@ -39,14 +38,6 @@ var _ = Describe("creating a Namespace with --force-tenant-name flag", func() {
|
||||
Name: "john",
|
||||
Kind: "User",
|
||||
},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{},
|
||||
LimitRanges: []corev1.LimitRangeSpec{},
|
||||
NamespaceQuota: 10,
|
||||
NodeSelector: map[string]string{},
|
||||
ResourceQuota: []corev1.ResourceQuotaSpec{},
|
||||
},
|
||||
}
|
||||
t2 := &v1alpha1.Tenant{
|
||||
@@ -58,14 +49,6 @@ var _ = Describe("creating a Namespace with --force-tenant-name flag", func() {
|
||||
Name: "john",
|
||||
Kind: "User",
|
||||
},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{},
|
||||
LimitRanges: []corev1.LimitRangeSpec{},
|
||||
NamespaceQuota: 10,
|
||||
NodeSelector: map[string]string{},
|
||||
ResourceQuota: []corev1.ResourceQuotaSpec{},
|
||||
},
|
||||
}
|
||||
JustBeforeEach(func() {
|
||||
|
||||
@@ -20,9 +20,7 @@ import (
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/utils/pointer"
|
||||
@@ -40,21 +38,13 @@ var _ = Describe("when Tenant handles Ingress classes", func() {
|
||||
Name: "ingress",
|
||||
Kind: "User",
|
||||
},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{
|
||||
IngressClasses: &v1alpha1.IngressClassesSpec{
|
||||
Allowed: []string{
|
||||
"nginx",
|
||||
"haproxy",
|
||||
},
|
||||
AllowedRegex: "^oil-.*$",
|
||||
},
|
||||
LimitRanges: []corev1.LimitRangeSpec{},
|
||||
NamespaceQuota: 3,
|
||||
NodeSelector: map[string]string{},
|
||||
NetworkPolicies: []networkingv1.NetworkPolicySpec{},
|
||||
ResourceQuota: []corev1.ResourceQuotaSpec{},
|
||||
},
|
||||
}
|
||||
JustBeforeEach(func() {
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
@@ -40,8 +39,6 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata", f
|
||||
Name: "gatsby",
|
||||
Kind: "User",
|
||||
},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{
|
||||
AdditionalLabels: map[string]string{
|
||||
"k8s.io/custom-label": "foo",
|
||||
@@ -52,11 +49,6 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata", f
|
||||
"clastix.io/custom-annotation": "buzz",
|
||||
},
|
||||
},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
LimitRanges: []corev1.LimitRangeSpec{},
|
||||
NamespaceQuota: 10,
|
||||
NodeSelector: map[string]string{},
|
||||
ResourceQuota: []corev1.ResourceQuotaSpec{},
|
||||
},
|
||||
}
|
||||
JustBeforeEach(func() {
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
|
||||
. "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"
|
||||
@@ -39,14 +38,6 @@ var _ = Describe("creating a Namespace as Tenant owner", func() {
|
||||
Name: "alice",
|
||||
Kind: "User",
|
||||
},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
LimitRanges: []corev1.LimitRangeSpec{},
|
||||
NamespaceQuota: 10,
|
||||
NodeSelector: map[string]string{},
|
||||
ResourceQuota: []corev1.ResourceQuotaSpec{},
|
||||
},
|
||||
}
|
||||
JustBeforeEach(func() {
|
||||
|
||||
@@ -23,8 +23,8 @@ import (
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
"github.com/clastix/capsule/api/v1alpha1"
|
||||
)
|
||||
@@ -39,14 +39,7 @@ var _ = Describe("creating a Namespace over-quota", func() {
|
||||
Name: "bob",
|
||||
Kind: "User",
|
||||
},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{},
|
||||
LimitRanges: []corev1.LimitRangeSpec{},
|
||||
NamespaceQuota: 3,
|
||||
NodeSelector: map[string]string{},
|
||||
ResourceQuota: []corev1.ResourceQuotaSpec{},
|
||||
NamespaceQuota: pointer.Int32Ptr(3),
|
||||
},
|
||||
}
|
||||
JustBeforeEach(func() {
|
||||
|
||||
@@ -43,15 +43,12 @@ var _ = Describe("when Tenant owner interacts with the webhooks", func() {
|
||||
Name: "ruby",
|
||||
Kind: "User",
|
||||
},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{
|
||||
StorageClasses: &v1alpha1.StorageClassesSpec{
|
||||
Allowed: []string{
|
||||
"cephfs",
|
||||
"glusterfs",
|
||||
},
|
||||
},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
LimitRanges: []corev1.LimitRangeSpec{
|
||||
{
|
||||
Limits: []corev1.LimitRangeItem{
|
||||
@@ -69,8 +66,6 @@ var _ = Describe("when Tenant owner interacts with the webhooks", func() {
|
||||
},
|
||||
},
|
||||
},
|
||||
NamespaceQuota: 3,
|
||||
NodeSelector: map[string]string{},
|
||||
NetworkPolicies: []networkingv1.NetworkPolicySpec{
|
||||
{
|
||||
Egress: []networkingv1.NetworkPolicyEgressRule{
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
|
||||
. "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"
|
||||
@@ -39,14 +38,6 @@ var _ = Describe("creating a Namespace with --protected-namespace-regex enabled"
|
||||
Name: "alice",
|
||||
Kind: "User",
|
||||
},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{},
|
||||
LimitRanges: []corev1.LimitRangeSpec{},
|
||||
NamespaceQuota: 10,
|
||||
NodeSelector: map[string]string{},
|
||||
ResourceQuota: []corev1.ResourceQuotaSpec{},
|
||||
},
|
||||
}
|
||||
JustBeforeEach(func() {
|
||||
|
||||
@@ -27,7 +27,6 @@ import (
|
||||
. "github.com/onsi/gomega"
|
||||
v1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
@@ -46,10 +45,6 @@ var _ = Describe("exceeding Tenant resource quota", func() {
|
||||
Name: "bobby",
|
||||
Kind: "User",
|
||||
},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{},
|
||||
LimitRanges: []corev1.LimitRangeSpec{
|
||||
{
|
||||
Limits: []corev1.LimitRangeItem{
|
||||
@@ -95,9 +90,6 @@ var _ = Describe("exceeding Tenant resource quota", func() {
|
||||
},
|
||||
},
|
||||
},
|
||||
NetworkPolicies: []networkingv1.NetworkPolicySpec{},
|
||||
NamespaceQuota: 2,
|
||||
NodeSelector: map[string]string{},
|
||||
ResourceQuota: []corev1.ResourceQuotaSpec{
|
||||
{
|
||||
Hard: map[corev1.ResourceName]resource.Quantity{
|
||||
|
||||
@@ -39,14 +39,6 @@ var _ = Describe("creating a Namespace trying to select a third Tenant", func()
|
||||
Name: "undefined",
|
||||
Kind: "User",
|
||||
},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{},
|
||||
LimitRanges: []corev1.LimitRangeSpec{},
|
||||
NamespaceQuota: 10,
|
||||
NodeSelector: map[string]string{},
|
||||
ResourceQuota: []corev1.ResourceQuotaSpec{},
|
||||
},
|
||||
}
|
||||
JustBeforeEach(func() {
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
|
||||
. "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"
|
||||
@@ -39,14 +38,6 @@ var _ = Describe("creating a Namespace without a Tenant selector when user owns
|
||||
Name: "john",
|
||||
Kind: "User",
|
||||
},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{},
|
||||
LimitRanges: []corev1.LimitRangeSpec{},
|
||||
NamespaceQuota: 10,
|
||||
NodeSelector: map[string]string{},
|
||||
ResourceQuota: []corev1.ResourceQuotaSpec{},
|
||||
},
|
||||
}
|
||||
t2 := &v1alpha1.Tenant{
|
||||
@@ -58,14 +49,6 @@ var _ = Describe("creating a Namespace without a Tenant selector when user owns
|
||||
Name: "john",
|
||||
Kind: "User",
|
||||
},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{},
|
||||
LimitRanges: []corev1.LimitRangeSpec{},
|
||||
NamespaceQuota: 10,
|
||||
NodeSelector: map[string]string{},
|
||||
ResourceQuota: []corev1.ResourceQuotaSpec{},
|
||||
},
|
||||
}
|
||||
t3 := &v1alpha1.Tenant{
|
||||
@@ -77,14 +60,6 @@ var _ = Describe("creating a Namespace without a Tenant selector when user owns
|
||||
Name: "john",
|
||||
Kind: "Group",
|
||||
},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{},
|
||||
LimitRanges: []corev1.LimitRangeSpec{},
|
||||
NamespaceQuota: 10,
|
||||
NodeSelector: map[string]string{},
|
||||
ResourceQuota: []corev1.ResourceQuotaSpec{},
|
||||
},
|
||||
}
|
||||
t4 := &v1alpha1.Tenant{
|
||||
@@ -96,14 +71,6 @@ var _ = Describe("creating a Namespace without a Tenant selector when user owns
|
||||
Name: "john",
|
||||
Kind: "Group",
|
||||
},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{},
|
||||
LimitRanges: []corev1.LimitRangeSpec{},
|
||||
NamespaceQuota: 10,
|
||||
NodeSelector: map[string]string{},
|
||||
ResourceQuota: []corev1.ResourceQuotaSpec{},
|
||||
},
|
||||
}
|
||||
It("should fail", func() {
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
|
||||
. "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"
|
||||
@@ -39,14 +38,6 @@ var _ = Describe("creating a Namespace with Tenant selector when user owns multi
|
||||
Name: "john",
|
||||
Kind: "User",
|
||||
},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{},
|
||||
LimitRanges: []corev1.LimitRangeSpec{},
|
||||
NamespaceQuota: 10,
|
||||
NodeSelector: map[string]string{},
|
||||
ResourceQuota: []corev1.ResourceQuotaSpec{},
|
||||
},
|
||||
}
|
||||
t2 := &v1alpha1.Tenant{
|
||||
@@ -58,14 +49,6 @@ var _ = Describe("creating a Namespace with Tenant selector when user owns multi
|
||||
Name: "john",
|
||||
Kind: "User",
|
||||
},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{},
|
||||
LimitRanges: []corev1.LimitRangeSpec{},
|
||||
NamespaceQuota: 10,
|
||||
NodeSelector: map[string]string{},
|
||||
ResourceQuota: []corev1.ResourceQuotaSpec{},
|
||||
},
|
||||
}
|
||||
JustBeforeEach(func() {
|
||||
|
||||
@@ -41,9 +41,6 @@ var _ = Describe("creating a Service/Endpoint/EndpointSlice for a Tenant with ad
|
||||
Name: "gatsby",
|
||||
Kind: "User",
|
||||
},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{
|
||||
AdditionalLabels: map[string]string{
|
||||
"k8s.io/custom-label": "foo",
|
||||
@@ -54,10 +51,6 @@ var _ = Describe("creating a Service/Endpoint/EndpointSlice for a Tenant with ad
|
||||
"clastix.io/custom-annotation": "buzz",
|
||||
},
|
||||
},
|
||||
LimitRanges: []corev1.LimitRangeSpec{},
|
||||
NamespaceQuota: 10,
|
||||
NodeSelector: map[string]string{},
|
||||
ResourceQuota: []corev1.ResourceQuotaSpec{},
|
||||
},
|
||||
}
|
||||
epsCR := &rbacv1.ClusterRole{
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/utils/pointer"
|
||||
@@ -39,21 +38,13 @@ var _ = Describe("when Tenant handles Storage classes", func() {
|
||||
Name: "storage",
|
||||
Kind: "User",
|
||||
},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{
|
||||
StorageClasses: &v1alpha1.StorageClassesSpec{
|
||||
Allowed: []string{
|
||||
"cephfs",
|
||||
"glusterfs",
|
||||
},
|
||||
AllowedRegex: "^oil-.*$",
|
||||
},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
LimitRanges: []corev1.LimitRangeSpec{},
|
||||
NamespaceQuota: 3,
|
||||
NodeSelector: map[string]string{},
|
||||
NetworkPolicies: []networkingv1.NetworkPolicySpec{},
|
||||
ResourceQuota: []corev1.ResourceQuotaSpec{},
|
||||
},
|
||||
}
|
||||
JustBeforeEach(func() {
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
|
||||
. "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"
|
||||
@@ -39,14 +38,6 @@ var _ = Describe("creating a Tenant with wrong name", func() {
|
||||
Name: "john",
|
||||
Kind: "User",
|
||||
},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{},
|
||||
LimitRanges: []corev1.LimitRangeSpec{},
|
||||
NamespaceQuota: 10,
|
||||
NodeSelector: map[string]string{},
|
||||
ResourceQuota: []corev1.ResourceQuotaSpec{},
|
||||
},
|
||||
}
|
||||
It("should fail", func() {
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
|
||||
. "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"
|
||||
@@ -39,14 +38,6 @@ var _ = Describe("creating a Namespace with group Tenant owner", func() {
|
||||
Name: "alice",
|
||||
Kind: "Group",
|
||||
},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{},
|
||||
LimitRanges: []corev1.LimitRangeSpec{},
|
||||
NamespaceQuota: 10,
|
||||
NodeSelector: map[string]string{},
|
||||
ResourceQuota: []corev1.ResourceQuotaSpec{},
|
||||
},
|
||||
}
|
||||
JustBeforeEach(func() {
|
||||
@@ -62,6 +53,8 @@ var _ = Describe("creating a Namespace with group Tenant owner", func() {
|
||||
ns := NewNamespace("gto-namespace")
|
||||
NamespaceCreation(ns, tnt, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, podRecreationTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
GroupShouldBeUsedInTenantRoleBinding(ns, tnt, defaultTimeoutInterval)
|
||||
for _, a := range KindInTenantRoleBindingAssertions(ns, defaultTimeoutInterval) {
|
||||
a.Should(BeIdenticalTo("Group"))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -44,10 +44,6 @@ var _ = Describe("changing Tenant managed Kubernetes resources", func() {
|
||||
Name: "laura",
|
||||
Kind: "User",
|
||||
},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{},
|
||||
LimitRanges: []corev1.LimitRangeSpec{
|
||||
{
|
||||
Limits: []corev1.LimitRangeItem{
|
||||
@@ -137,7 +133,6 @@ var _ = Describe("changing Tenant managed Kubernetes resources", func() {
|
||||
},
|
||||
},
|
||||
},
|
||||
NamespaceQuota: 4,
|
||||
NodeSelector: map[string]string{
|
||||
"kubernetes.io/os": "linux",
|
||||
},
|
||||
|
||||
@@ -45,10 +45,6 @@ var _ = Describe("creating namespaces within a Tenant with resources", func() {
|
||||
Name: "john",
|
||||
Kind: "User",
|
||||
},
|
||||
NamespacesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
ServicesMetadata: v1alpha1.AdditionalMetadata{},
|
||||
IngressClasses: v1alpha1.IngressClassesSpec{},
|
||||
StorageClasses: v1alpha1.StorageClassesSpec{},
|
||||
LimitRanges: []corev1.LimitRangeSpec{
|
||||
{
|
||||
Limits: []corev1.LimitRangeItem{
|
||||
@@ -138,7 +134,6 @@ var _ = Describe("creating namespaces within a Tenant with resources", func() {
|
||||
},
|
||||
},
|
||||
},
|
||||
NamespaceQuota: 3,
|
||||
NodeSelector: map[string]string{
|
||||
"kubernetes.io/os": "linux",
|
||||
},
|
||||
|
||||
+11
-9
@@ -38,7 +38,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTimeoutInterval = 10 * time.Second
|
||||
defaultTimeoutInterval = 20 * time.Second
|
||||
podRecreationTimeoutInterval = 90 * time.Second
|
||||
defaultPollInterval = time.Second
|
||||
)
|
||||
@@ -115,15 +115,17 @@ func ModifyCapsuleManagerPodArgs(args []string) {
|
||||
time.Sleep(defaultTimeoutInterval)
|
||||
}
|
||||
|
||||
func GroupShouldBeUsedInTenantRoleBinding(ns *corev1.Namespace, t *v1alpha1.Tenant, timeout time.Duration) {
|
||||
for _, roleBindingName := range tenantRoleBindingNames {
|
||||
tenantRoleBindig := &rbacv1.RoleBinding{}
|
||||
Eventually(func() string {
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: roleBindingName, Namespace: ns.GetName()}, tenantRoleBindig)).Should(Succeed())
|
||||
return tenantRoleBindig.Subjects[0].Kind
|
||||
}, timeout, defaultPollInterval).Should(BeIdenticalTo("Group"))
|
||||
|
||||
func KindInTenantRoleBindingAssertions(ns *corev1.Namespace, timeout time.Duration) (out []AsyncAssertion) {
|
||||
for _, rbn := range tenantRoleBindingNames {
|
||||
rb := &rbacv1.RoleBinding{}
|
||||
out = append(out, Eventually(func() string {
|
||||
if err := k8sClient.Get(context.TODO(), types.NamespacedName{Name: rbn, Namespace: ns.GetName()}, rb); err != nil {
|
||||
return ""
|
||||
}
|
||||
return rb.Subjects[0].Kind
|
||||
}, timeout, defaultPollInterval))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetKubernetesSemVer() (major, minor int, ver string) {
|
||||
|
||||
@@ -134,9 +134,13 @@ func (r *handler) validateIngress(ctx context.Context, c client.Client, object I
|
||||
|
||||
tnt := tl.Items[0]
|
||||
|
||||
if tnt.Spec.IngressClasses == nil {
|
||||
return admission.Allowed("")
|
||||
}
|
||||
|
||||
ingressClass := object.IngressClass()
|
||||
if ingressClass == nil {
|
||||
return admission.Errored(http.StatusBadRequest, NewIngressClassNotValid(tnt.Spec.IngressClasses))
|
||||
return admission.Errored(http.StatusBadRequest, NewIngressClassNotValid(*tnt.Spec.IngressClasses))
|
||||
}
|
||||
|
||||
if len(tnt.Spec.IngressClasses.Allowed) > 0 {
|
||||
@@ -148,7 +152,7 @@ func (r *handler) validateIngress(ctx context.Context, c client.Client, object I
|
||||
}
|
||||
|
||||
if !valid && !matched {
|
||||
return admission.Errored(http.StatusBadRequest, NewIngressClassForbidden(*ingressClass, tnt.Spec.IngressClasses))
|
||||
return admission.Errored(http.StatusBadRequest, NewIngressClassForbidden(*ingressClass, *tnt.Spec.IngressClasses))
|
||||
}
|
||||
|
||||
return admission.Allowed("")
|
||||
|
||||
@@ -79,11 +79,16 @@ func (h *handler) OnCreate(c client.Client, decoder *admission.Decoder) capsulew
|
||||
return admission.Allowed("")
|
||||
}
|
||||
|
||||
tnt := tl.Items[0]
|
||||
|
||||
if tnt.Spec.StorageClasses == nil {
|
||||
return admission.Allowed("")
|
||||
}
|
||||
|
||||
if pvc.Spec.StorageClassName == nil {
|
||||
return admission.Errored(http.StatusBadRequest, NewStorageClassNotValid(tl.Items[0].Spec.StorageClasses))
|
||||
return admission.Errored(http.StatusBadRequest, NewStorageClassNotValid(*tl.Items[0].Spec.StorageClasses))
|
||||
}
|
||||
|
||||
tnt := tl.Items[0]
|
||||
sc := *pvc.Spec.StorageClassName
|
||||
|
||||
if len(tnt.Spec.StorageClasses.Allowed) > 0 {
|
||||
@@ -95,10 +100,9 @@ func (h *handler) OnCreate(c client.Client, decoder *admission.Decoder) capsulew
|
||||
}
|
||||
|
||||
if !valid && !matched {
|
||||
return admission.Errored(http.StatusBadRequest, NewStorageClassForbidden(*pvc.Spec.StorageClassName, tnt.Spec.StorageClasses))
|
||||
return admission.Errored(http.StatusBadRequest, NewStorageClassForbidden(*pvc.Spec.StorageClassName, *tnt.Spec.StorageClasses))
|
||||
}
|
||||
return admission.Allowed("")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ func Handler() capsulewebhook.Handler {
|
||||
return &handler{}
|
||||
}
|
||||
|
||||
func (r *handler) OnCreate(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
|
||||
func (h *handler) OnCreate(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
|
||||
return func(ctx context.Context, req admission.Request) admission.Response {
|
||||
tnt := &v1alpha1.Tenant{}
|
||||
if err := decoder.Decode(req, tnt); err != nil {
|
||||
@@ -70,19 +70,26 @@ func (r *handler) OnCreate(client client.Client, decoder *admission.Decoder) cap
|
||||
}
|
||||
|
||||
// Validate ingressClasses regexp
|
||||
if len(tnt.Spec.IngressClasses.AllowedRegex) > 0 {
|
||||
if tnt.Spec.IngressClasses != nil && len(tnt.Spec.IngressClasses.AllowedRegex) > 0 {
|
||||
if _, err := regexp.Compile(tnt.Spec.IngressClasses.AllowedRegex); err != nil {
|
||||
return admission.Denied("Unable to compile ingressClasses allowedRegex")
|
||||
}
|
||||
}
|
||||
|
||||
// Validate storageClasses regexp
|
||||
if len(tnt.Spec.StorageClasses.AllowedRegex) > 0 {
|
||||
if tnt.Spec.StorageClasses != nil && len(tnt.Spec.StorageClasses.AllowedRegex) > 0 {
|
||||
if _, err := regexp.Compile(tnt.Spec.StorageClasses.AllowedRegex); err != nil {
|
||||
return admission.Denied("Unable to compile storageClasses allowedRegex")
|
||||
}
|
||||
}
|
||||
|
||||
// Validate containerRegistries regexp
|
||||
if tnt.Spec.ContainerRegistries != nil && len(tnt.Spec.ContainerRegistries.AllowedRegex) > 0 {
|
||||
if _, err := regexp.Compile(tnt.Spec.ContainerRegistries.AllowedRegex); err != nil {
|
||||
return admission.Denied("Unable to compile containerRegistries allowedRegex")
|
||||
}
|
||||
}
|
||||
|
||||
return admission.Allowed("")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user