Files
capsule/controllers/tenant/namespaces.go
2022-12-27 17:53:17 +01:00

185 lines
5.4 KiB
Go

// Copyright 2020-2021 Clastix Labs
// SPDX-License-Identifier: Apache-2.0
package tenant
import (
"context"
"fmt"
"strings"
"golang.org/x/sync/errgroup"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
capsulev1beta2 "github.com/clastix/capsule/api/v1beta2"
"github.com/clastix/capsule/pkg/api"
"github.com/clastix/capsule/pkg/utils"
)
// Ensuring all annotations are applied to each Namespace handled by the Tenant.
func (r *Manager) syncNamespaces(ctx context.Context, tenant *capsulev1beta2.Tenant) (err error) {
group := new(errgroup.Group)
for _, item := range tenant.Status.Namespaces {
namespace := item
group.Go(func() error {
return r.syncNamespaceMetadata(ctx, namespace, tenant)
})
}
if err = group.Wait(); err != nil {
r.Log.Error(err, "Cannot sync Namespaces")
err = fmt.Errorf("cannot sync Namespaces: %w", err)
}
return
}
// nolint:gocognit
func (r *Manager) syncNamespaceMetadata(ctx context.Context, namespace string, tnt *capsulev1beta2.Tenant) (err error) {
var res controllerutil.OperationResult
err = retry.RetryOnConflict(retry.DefaultBackoff, func() (conflictErr error) {
ns := &corev1.Namespace{}
if conflictErr = r.Client.Get(ctx, types.NamespacedName{Name: namespace}, ns); err != nil {
return
}
capsuleLabel, _ := utils.GetTypeLabel(&capsulev1beta2.Tenant{})
res, conflictErr = controllerutil.CreateOrUpdate(ctx, r.Client, ns, func() error {
annotations := make(map[string]string)
labels := map[string]string{
"name": namespace,
capsuleLabel: tnt.GetName(),
}
if tnt.Spec.NamespaceOptions != nil && tnt.Spec.NamespaceOptions.AdditionalMetadata != nil {
for k, v := range tnt.Spec.NamespaceOptions.AdditionalMetadata.Annotations {
annotations[k] = v
}
}
if tnt.Spec.NamespaceOptions != nil && tnt.Spec.NamespaceOptions.AdditionalMetadata != nil {
for k, v := range tnt.Spec.NamespaceOptions.AdditionalMetadata.Labels {
labels[k] = v
}
}
if tnt.Spec.NodeSelector != nil {
annotations = utils.BuildNodeSelector(tnt, annotations)
}
if tnt.Spec.IngressOptions.AllowedClasses != nil {
if len(tnt.Spec.IngressOptions.AllowedClasses.Exact) > 0 {
annotations[AvailableIngressClassesAnnotation] = strings.Join(tnt.Spec.IngressOptions.AllowedClasses.Exact, ",")
}
if len(tnt.Spec.IngressOptions.AllowedClasses.Regex) > 0 {
annotations[AvailableIngressClassesRegexpAnnotation] = tnt.Spec.IngressOptions.AllowedClasses.Regex
}
}
if tnt.Spec.StorageClasses != nil {
if len(tnt.Spec.StorageClasses.Exact) > 0 {
annotations[AvailableStorageClassesAnnotation] = strings.Join(tnt.Spec.StorageClasses.Exact, ",")
}
if len(tnt.Spec.StorageClasses.Regex) > 0 {
annotations[AvailableStorageClassesRegexpAnnotation] = tnt.Spec.StorageClasses.Regex
}
}
if tnt.Spec.ContainerRegistries != nil {
if len(tnt.Spec.ContainerRegistries.Exact) > 0 {
annotations[AllowedRegistriesAnnotation] = strings.Join(tnt.Spec.ContainerRegistries.Exact, ",")
}
if len(tnt.Spec.ContainerRegistries.Regex) > 0 {
annotations[AllowedRegistriesRegexpAnnotation] = tnt.Spec.ContainerRegistries.Regex
}
}
if value, ok := tnt.Annotations[api.ForbiddenNamespaceLabelsAnnotation]; ok {
annotations[api.ForbiddenNamespaceLabelsAnnotation] = value
}
if value, ok := tnt.Annotations[api.ForbiddenNamespaceLabelsRegexpAnnotation]; ok {
annotations[api.ForbiddenNamespaceLabelsRegexpAnnotation] = value
}
if value, ok := tnt.Annotations[api.ForbiddenNamespaceAnnotationsAnnotation]; ok {
annotations[api.ForbiddenNamespaceAnnotationsAnnotation] = value
}
if value, ok := tnt.Annotations[api.ForbiddenNamespaceAnnotationsRegexpAnnotation]; ok {
annotations[api.ForbiddenNamespaceAnnotationsRegexpAnnotation] = value
}
if ns.Annotations == nil {
ns.SetAnnotations(annotations)
} else {
for k, v := range annotations {
ns.Annotations[k] = v
}
}
if ns.Labels == nil {
ns.SetLabels(labels)
} else {
for k, v := range labels {
ns.Labels[k] = v
}
}
return nil
})
return
})
r.emitEvent(tnt, namespace, res, "Ensuring Namespace metadata", err)
return err
}
func (r *Manager) ensureNamespaceCount(ctx context.Context, tenant *capsulev1beta2.Tenant) error {
return retry.RetryOnConflict(retry.DefaultBackoff, func() error {
tenant.Status.Size = uint(len(tenant.Status.Namespaces))
found := &capsulev1beta2.Tenant{}
if err := r.Client.Get(ctx, types.NamespacedName{Name: tenant.GetName()}, found); err != nil {
return err
}
found.Status.Size = tenant.Status.Size
return r.Client.Status().Update(ctx, found, &client.UpdateOptions{})
})
}
func (r *Manager) collectNamespaces(ctx context.Context, tenant *capsulev1beta2.Tenant) error {
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
list := &corev1.NamespaceList{}
err = r.Client.List(ctx, list, client.MatchingFieldsSelector{
Selector: fields.OneTermEqualSelector(".metadata.ownerReferences[*].capsule", tenant.GetName()),
})
if err != nil {
return
}
_, err = controllerutil.CreateOrUpdate(ctx, r.Client, tenant.DeepCopy(), func() error {
tenant.AssignNamespaces(list.Items)
return r.Client.Status().Update(ctx, tenant, &client.UpdateOptions{})
})
return
})
}