mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-03-03 18:20:19 +00:00
185 lines
5.4 KiB
Go
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
|
|
})
|
|
}
|