Providing namespace metadata additional labels and annotations (#80)

This commit is contained in:
Dario Tranchitella
2020-09-07 15:09:34 +02:00
committed by GitHub
parent a99153cbe7
commit ee0261c069
17 changed files with 236 additions and 67 deletions
+9
View File
@@ -25,9 +25,18 @@ import (
// +kubebuilder:validation:Minimum=1
type NamespaceQuota uint
type NamespaceMetadata struct {
// +nullable
AdditionalLabels map[string]string `json:"additionalLabels"`
// +nullable
AdditionalAnnotations map[string]string `json:"additionalAnnotations"`
}
// TenantSpec defines the desired state of Tenant
type TenantSpec struct {
Owner string `json:"owner"`
// +kubebuilder:validation:Optional
NamespacesMetadata NamespaceMetadata `json:"namespacesMetadata"`
// +kubebuilder:validation:Required
StorageClasses StorageClassList `json:"storageClasses"`
IngressClasses IngressClassList `json:"ingressClasses"`
+30
View File
@@ -64,6 +64,35 @@ func (in NamespaceList) DeepCopy() NamespaceList {
return *out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NamespaceMetadata) DeepCopyInto(out *NamespaceMetadata) {
*out = *in
if in.AdditionalLabels != nil {
in, out := &in.AdditionalLabels, &out.AdditionalLabels
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.AdditionalAnnotations != nil {
in, out := &in.AdditionalAnnotations, &out.AdditionalAnnotations
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceMetadata.
func (in *NamespaceMetadata) DeepCopy() *NamespaceMetadata {
if in == nil {
return nil
}
out := new(NamespaceMetadata)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in StorageClassList) DeepCopyInto(out *StorageClassList) {
{
@@ -145,6 +174,7 @@ func (in *TenantList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TenantSpec) DeepCopyInto(out *TenantSpec) {
*out = *in
in.NamespacesMetadata.DeepCopyInto(&out.NamespacesMetadata)
if in.StorageClasses != nil {
in, out := &in.StorageClasses, &out.StorageClasses
*out = make(StorageClassList, len(*in))
@@ -139,6 +139,22 @@ spec:
namespaceQuota:
minimum: 1
type: integer
namespacesMetadata:
properties:
additionalAnnotations:
additionalProperties:
type: string
nullable: true
type: object
additionalLabels:
additionalProperties:
type: string
nullable: true
type: object
required:
- additionalAnnotations
- additionalLabels
type: object
networkPolicies:
items:
description: NetworkPolicySpec provides the specification of a NetworkPolicy
+29 -12
View File
@@ -393,29 +393,46 @@ func (r *TenantReconciler) syncLimitRanges(tenant *capsulev1alpha1.Tenant) error
return nil
}
func (r *TenantReconciler) syncNamespace(namespace string, ingressClasses []string, storageClasses []string, tenantLabel string, wg *sync.WaitGroup, channel chan error) {
func (r *TenantReconciler) syncNamespace(namespace string, ingressClasses []string, storageClasses []string, nsMetadata capsulev1alpha1.NamespaceMetadata, tenantLabel string, wg *sync.WaitGroup, channel chan error) {
defer wg.Done()
t := &corev1.Namespace{}
if err := r.Client.Get(context.TODO(), types.NamespacedName{Name: namespace}, t); err != nil {
ns := &corev1.Namespace{}
if err := r.Client.Get(context.TODO(), types.NamespacedName{Name: namespace}, ns); err != nil {
channel <- err
}
channel <- retry.RetryOnConflict(retry.DefaultBackoff, func() error {
if t.Annotations == nil {
t.Annotations = make(map[string]string)
a := ns.GetAnnotations()
if a == nil {
a = make(map[string]string)
}
t.Annotations[capsulev1alpha1.AvailableIngressClassesAnnotation] = strings.Join(ingressClasses, ",")
t.Annotations[capsulev1alpha1.AvailableStorageClassesAnnotation] = strings.Join(storageClasses, ",")
if t.Labels == nil {
t.Labels = make(map[string]string)
a[capsulev1alpha1.AvailableIngressClassesAnnotation] = strings.Join(ingressClasses, ",")
a[capsulev1alpha1.AvailableStorageClassesAnnotation] = strings.Join(storageClasses, ",")
if aa := nsMetadata.AdditionalAnnotations; aa != nil {
for k, v := range aa {
a[k] = v
}
}
l := ns.GetLabels()
if l == nil {
l = make(map[string]string)
}
capsuleLabel, err := capsulev1alpha1.GetTypeLabel(&capsulev1alpha1.Tenant{})
if err != nil {
return err
}
t.Labels[capsuleLabel] = tenantLabel
return r.Client.Update(context.TODO(), t, &client.UpdateOptions{})
l[capsuleLabel] = tenantLabel
if al := nsMetadata.AdditionalLabels; al != nil {
for k, v := range al {
l[k] = v
}
}
ns.SetLabels(l)
ns.SetAnnotations(a)
return r.Client.Update(context.TODO(), ns, &client.UpdateOptions{})
})
}
@@ -427,7 +444,7 @@ func (r *TenantReconciler) syncNamespaces(tenant *capsulev1alpha1.Tenant) (err e
wg.Add(tenant.Status.Namespaces.Len())
for _, ns := range tenant.Status.Namespaces {
go r.syncNamespace(ns, tenant.Spec.IngressClasses, tenant.Spec.StorageClasses, tenant.GetName(), wg, ch)
go r.syncNamespace(ns, tenant.Spec.IngressClasses, tenant.Spec.StorageClasses, tenant.Spec.NamespacesMetadata, tenant.GetName(), wg, ch)
}
wg.Wait()
+8 -7
View File
@@ -35,13 +35,14 @@ var _ = Describe("creating a Namespace as Tenant owner with custom --capsule-gro
Name: "tenant-assigned-custom-group",
},
Spec: v1alpha1.TenantSpec{
Owner: "alice",
StorageClasses: []string{},
IngressClasses: []string{},
LimitRanges: []corev1.LimitRangeSpec{},
NamespaceQuota: 10,
NodeSelector: map[string]string{},
ResourceQuota: []corev1.ResourceQuotaSpec{},
Owner: "alice",
NamespacesMetadata: v1alpha1.NamespaceMetadata{},
StorageClasses: []string{},
IngressClasses: []string{},
LimitRanges: []corev1.LimitRangeSpec{},
NamespaceQuota: 10,
NodeSelector: map[string]string{},
ResourceQuota: []corev1.ResourceQuotaSpec{},
},
}
JustBeforeEach(func() {
+3 -2
View File
@@ -41,8 +41,9 @@ var _ = Describe("when Tenant handles Ingress classes", func() {
Name: "ingress-class",
},
Spec: v1alpha1.TenantSpec{
Owner: "ingress",
StorageClasses: []string{},
Owner: "ingress",
NamespacesMetadata: v1alpha1.NamespaceMetadata{},
StorageClasses: []string{},
IngressClasses: []string{
"nginx",
"haproxy",
+81
View File
@@ -0,0 +1,81 @@
//+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"
"k8s.io/apimachinery/pkg/types"
"github.com/clastix/capsule/api/v1alpha1"
)
var _ = Describe("creating a Namespace for a Tenant with additional metadata", func() {
tnt := &v1alpha1.Tenant{
ObjectMeta: metav1.ObjectMeta{
Name: "tenant-metadata",
},
Spec: v1alpha1.TenantSpec{
Owner: "gatsby",
StorageClasses: []string{},
IngressClasses: []string{},
NamespacesMetadata: v1alpha1.NamespaceMetadata{
AdditionalLabels: map[string]string{
"k8s.io/custom-label": "foo",
"clastix.io/custom-label": "bar",
},
AdditionalAnnotations: map[string]string{
"k8s.io/custom-annotation": "bizz",
"clastix.io/custom-annotation": "buzz",
},
},
LimitRanges: []corev1.LimitRangeSpec{},
NamespaceQuota: 10,
NodeSelector: map[string]string{},
ResourceQuota: []corev1.ResourceQuotaSpec{},
},
}
JustBeforeEach(func() {
Expect(k8sClient.Create(context.TODO(), tnt)).Should(Succeed())
})
JustAfterEach(func() {
Expect(k8sClient.Delete(context.TODO(), tnt)).Should(Succeed())
})
It("should contains additional Namespace metadata", func() {
ns := NewNamespace("namespace-metadata")
NamespaceCreationShouldSucceed(ns, tnt, defaultTimeoutInterval)
NamespaceShouldBeManagedByTenant(ns, tnt, defaultTimeoutInterval)
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, ns)).Should(Succeed())
By("checking additional labels", func() {
for _, l := range tnt.Spec.NamespacesMetadata.AdditionalLabels {
Expect(ns.Labels).Should(ContainElement(l))
}
})
By("checking additional annotations", func() {
for _, a := range tnt.Spec.NamespacesMetadata.AdditionalAnnotations {
Expect(ns.Annotations).Should(ContainElement(a))
}
})
})
})
+1
View File
@@ -38,6 +38,7 @@ var _ = Describe("creating a Namespace as Tenant owner", func() {
Owner: "alice",
StorageClasses: []string{},
IngressClasses: []string{},
NamespacesMetadata: v1alpha1.NamespaceMetadata{},
LimitRanges: []corev1.LimitRangeSpec{},
NamespaceQuota: 10,
NodeSelector: map[string]string{},
+8 -7
View File
@@ -35,13 +35,14 @@ var _ = Describe("creating a Namespace over-quota", func() {
Name: "overquota-tenant",
},
Spec: v1alpha1.TenantSpec{
Owner: "bob",
StorageClasses: []string{},
IngressClasses: []string{},
LimitRanges: []corev1.LimitRangeSpec{},
NamespaceQuota: 3,
NodeSelector: map[string]string{},
ResourceQuota: []corev1.ResourceQuotaSpec{},
Owner: "bob",
NamespacesMetadata: v1alpha1.NamespaceMetadata{},
StorageClasses: []string{},
IngressClasses: []string{},
LimitRanges: []corev1.LimitRangeSpec{},
NamespaceQuota: 3,
NodeSelector: map[string]string{},
ResourceQuota: []corev1.ResourceQuotaSpec{},
},
}
JustBeforeEach(func() {
+2 -1
View File
@@ -39,7 +39,8 @@ var _ = Describe("when Tenant owner interacts with the webhooks", func() {
Name: "tenant-owner",
},
Spec: v1alpha1.TenantSpec{
Owner: "ruby",
Owner: "ruby",
NamespacesMetadata: v1alpha1.NamespaceMetadata{},
StorageClasses: []string{
"cephfs",
"glusterfs",
+11 -7
View File
@@ -2,10 +2,13 @@
/*
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.
@@ -32,13 +35,14 @@ var _ = Describe("creating a Namespace with --protected-namespace-regex enabled"
Name: "tenantprotectednamespace",
},
Spec: v1alpha1.TenantSpec{
Owner: "alice",
StorageClasses: []string{},
IngressClasses: []string{},
LimitRanges: []corev1.LimitRangeSpec{},
NamespaceQuota: 10,
NodeSelector: map[string]string{},
ResourceQuota: []corev1.ResourceQuotaSpec{},
Owner: "alice",
NamespacesMetadata: v1alpha1.NamespaceMetadata{},
StorageClasses: []string{},
IngressClasses: []string{},
LimitRanges: []corev1.LimitRangeSpec{},
NamespaceQuota: 10,
NodeSelector: map[string]string{},
ResourceQuota: []corev1.ResourceQuotaSpec{},
},
}
JustBeforeEach(func() {
+4 -3
View File
@@ -42,9 +42,10 @@ var _ = Describe("exceeding Tenant resource quota", func() {
Name: "tenant-resources-changes",
},
Spec: v1alpha1.TenantSpec{
Owner: "bobby",
StorageClasses: []string{},
IngressClasses: []string{},
Owner: "bobby",
NamespacesMetadata: v1alpha1.NamespaceMetadata{},
StorageClasses: []string{},
IngressClasses: []string{},
LimitRanges: []corev1.LimitRangeSpec{
{
Limits: []corev1.LimitRangeItem{
+8 -7
View File
@@ -35,13 +35,14 @@ var _ = Describe("creating a Namespace trying to select a third Tenant", func()
Name: "tenant-non-owned",
},
Spec: v1alpha1.TenantSpec{
Owner: "undefined",
StorageClasses: []string{},
IngressClasses: []string{},
LimitRanges: []corev1.LimitRangeSpec{},
NamespaceQuota: 10,
NodeSelector: map[string]string{},
ResourceQuota: []corev1.ResourceQuotaSpec{},
Owner: "undefined",
NamespacesMetadata: v1alpha1.NamespaceMetadata{},
StorageClasses: []string{},
IngressClasses: []string{},
LimitRanges: []corev1.LimitRangeSpec{},
NamespaceQuota: 10,
NodeSelector: map[string]string{},
ResourceQuota: []corev1.ResourceQuotaSpec{},
},
}
JustBeforeEach(func() {
+16 -14
View File
@@ -35,13 +35,14 @@ var _ = Describe("creating a Namespace with Tenant selector", func() {
Name: "tenant-one",
},
Spec: v1alpha1.TenantSpec{
Owner: "john",
StorageClasses: []string{},
IngressClasses: []string{},
LimitRanges: []corev1.LimitRangeSpec{},
NamespaceQuota: 10,
NodeSelector: map[string]string{},
ResourceQuota: []corev1.ResourceQuotaSpec{},
Owner: "john",
NamespacesMetadata: v1alpha1.NamespaceMetadata{},
StorageClasses: []string{},
IngressClasses: []string{},
LimitRanges: []corev1.LimitRangeSpec{},
NamespaceQuota: 10,
NodeSelector: map[string]string{},
ResourceQuota: []corev1.ResourceQuotaSpec{},
},
}
t2 := &v1alpha1.Tenant{
@@ -49,13 +50,14 @@ var _ = Describe("creating a Namespace with Tenant selector", func() {
Name: "tenant-two",
},
Spec: v1alpha1.TenantSpec{
Owner: "john",
StorageClasses: []string{},
IngressClasses: []string{},
LimitRanges: []corev1.LimitRangeSpec{},
NamespaceQuota: 10,
NodeSelector: map[string]string{},
ResourceQuota: []corev1.ResourceQuotaSpec{},
Owner: "john",
NamespacesMetadata: v1alpha1.NamespaceMetadata{},
StorageClasses: []string{},
IngressClasses: []string{},
LimitRanges: []corev1.LimitRangeSpec{},
NamespaceQuota: 10,
NodeSelector: map[string]string{},
ResourceQuota: []corev1.ResourceQuotaSpec{},
},
}
JustBeforeEach(func() {
+2 -1
View File
@@ -38,7 +38,8 @@ var _ = Describe("when Tenant handles Storage classes", func() {
Name: "storage-class",
},
Spec: v1alpha1.TenantSpec{
Owner: "storage",
Owner: "storage",
NamespacesMetadata: v1alpha1.NamespaceMetadata{},
StorageClasses: []string{
"cephfs",
"glusterfs",
+4 -3
View File
@@ -40,9 +40,10 @@ var _ = Describe("changing Tenant managed Kubernetes resources", func() {
Name: "tenant-resources-changes",
},
Spec: v1alpha1.TenantSpec{
Owner: "laura",
StorageClasses: []string{},
IngressClasses: []string{},
Owner: "laura",
NamespacesMetadata: v1alpha1.NamespaceMetadata{},
StorageClasses: []string{},
IngressClasses: []string{},
LimitRanges: []corev1.LimitRangeSpec{
{
Limits: []corev1.LimitRangeItem{
+4 -3
View File
@@ -41,9 +41,10 @@ var _ = Describe("creating namespaces within a Tenant with resources", func() {
Name: "tenant-resources",
},
Spec: v1alpha1.TenantSpec{
Owner: "john",
StorageClasses: []string{},
IngressClasses: []string{},
Owner: "john",
NamespacesMetadata: v1alpha1.NamespaceMetadata{},
StorageClasses: []string{},
IngressClasses: []string{},
LimitRanges: []corev1.LimitRangeSpec{
{
Limits: []corev1.LimitRangeItem{