Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement support for podOptions and additionalMetadata #871

Merged
merged 7 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,8 @@ dev-setup:
{'op': 'replace', 'path': '/webhooks/5/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/pods\",'caBundle':\"$${CA_BUNDLE}\"}},\
{'op': 'replace', 'path': '/webhooks/6/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/persistentvolumeclaims\",'caBundle':\"$${CA_BUNDLE}\"}},\
{'op': 'replace', 'path': '/webhooks/7/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/services\",'caBundle':\"$${CA_BUNDLE}\"}},\
{'op': 'replace', 'path': '/webhooks/8/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/tenants\",'caBundle':\"$${CA_BUNDLE}\"}}\
{'op': 'replace', 'path': '/webhooks/8/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/tenants\",'caBundle':\"$${CA_BUNDLE}\"}},\
{'op': 'replace', 'path': '/webhooks/9/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/tenants\",'caBundle':\"$${CA_BUNDLE}\"}}\
]" && \
kubectl patch crd tenants.capsule.clastix.io \
--type='json' -p="[\
Expand Down
2 changes: 2 additions & 0 deletions api/v1beta2/tenant_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ type TenantSpec struct {
NamespaceOptions *NamespaceOptions `json:"namespaceOptions,omitempty"`
// Specifies options for the Service, such as additional metadata or block of certain type of Services. Optional.
ServiceOptions *api.ServiceOptions `json:"serviceOptions,omitempty"`
// Specifies options for the Pods deployed in the Tenant namespaces, such as additional metadata.
PodOptions *api.PodOptions `json:"podOptions,omitempty"`
// Specifies the allowed StorageClasses assigned to the Tenant.
// Capsule assures that all PersistentVolumeClaim resources created in the Tenant can use only one of the allowed StorageClasses.
// A default value can be specified, and all the PersistentVolumeClaim resources created will inherit the declared class.
Expand Down
5 changes: 5 additions & 0 deletions api/v1beta2/zz_generated.deepcopy.go

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

48 changes: 48 additions & 0 deletions charts/capsule/crds/tenant-crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,22 @@ spec:
- name
type: object
type: array
podOptions:
description: Specifies options for the Pod, such as additional metadata. Optional.
properties:
additionalMetadata:
description: Specifies additional labels and annotations the Capsule operator places on any Service resource in the Tenant. Optional.
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
type: object
preventDeletion:
description: Prevent accidental deletion of the Tenant. When enabled, the deletion request will be declined.
type: boolean
Expand Down Expand Up @@ -1737,6 +1753,22 @@ spec:
- name
type: object
type: array
podOptions:
description: Specifies options for the Pod, such as additional metadata. Optional.
properties:
additionalMetadata:
description: Specifies additional labels and annotations the Capsule operator places on any Service resource in the Tenant. Optional.
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
type: object
priorityClasses:
description: Specifies the allowed priorityClasses assigned to the
Tenant. Capsule assures that all Pods resources created in the Tenant
Expand Down Expand Up @@ -2869,6 +2901,22 @@ spec:
- name
type: object
type: array
podOptions:
description: Specifies options for the Pod, such as additional metadata. Optional.
properties:
additionalMetadata:
description: Specifies additional labels and annotations the Capsule operator places on any Service resource in the Tenant. Optional.
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
type: object
preventDeletion:
description: Prevent accidental deletion of the Tenant. When enabled,
the deletion request will be declined.
Expand Down
18 changes: 18 additions & 0 deletions config/crd/bases/capsule.clastix.io_tenants.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2859,6 +2859,24 @@ spec:
- name
type: object
type: array
podOptions:
description: Specifies options for the Pods deployed in the Tenant
namespaces, such as additional metadata.
properties:
additionalMetadata:
description: Specifies additional labels and annotations the Capsule
operator places on any Pod resource in the Tenant. Optional.
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
type: object
preventDeletion:
description: Prevent accidental deletion of the Tenant. When enabled,
the deletion request will be declined.
Expand Down
16 changes: 16 additions & 0 deletions config/install.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2437,6 +2437,22 @@ spec:
- name
type: object
type: array
podOptions:
description: Specifies options for the Pods deployed in the Tenant namespaces, such as additional metadata.
properties:
additionalMetadata:
description: Specifies additional labels and annotations the Capsule operator places on any Pod resource in the Tenant. Optional.
properties:
annotations:
additionalProperties:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
type: object
type: object
preventDeletion:
description: Prevent accidental deletion of the Tenant. When enabled, the deletion request will be declined.
type: boolean
Expand Down
30 changes: 30 additions & 0 deletions controllers/pod/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0

package pod

import "fmt"

type NonTenantObjectError struct {
objectName string
}

func NewNonTenantObject(objectName string) error {
return &NonTenantObjectError{objectName: objectName}
}

func (n NonTenantObjectError) Error() string {
return fmt.Sprintf("Skipping labels sync for %s as it doesn't belong to tenant", n.objectName)
}

type NoPodMetadataError struct {
objectName string
}

func NewNoPodMetadata(objectName string) error {
return &NoPodMetadataError{objectName: objectName}
}

func (n NoPodMetadataError) Error() string {
return fmt.Sprintf("Skipping labels sync for %s because no AdditionalLabels or AdditionalAnnotations presents in Tenant spec", n.objectName)
}
130 changes: 130 additions & 0 deletions controllers/pod/metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0

package pod

import (
"context"
"fmt"

"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
apierr "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
"github.com/projectcapsule/capsule/pkg/utils"
)

type MetadataReconciler struct {
Client client.Client
}

func (m *MetadataReconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.Result, error) {
var pod corev1.Pod

logger := log.FromContext(ctx)

tenant, err := m.getTenant(ctx, request.NamespacedName, m.Client)
if err != nil {
noTenantObjError := &NonTenantObjectError{}
noPodMetaError := &NoPodMetadataError{}

if errors.As(err, &noTenantObjError) || errors.As(err, &noPodMetaError) {
return reconcile.Result{}, nil
}

logger.Error(err, fmt.Sprintf("Cannot get tenant corev1.Pod %s/%s", request.Namespace, request.Name))

return reconcile.Result{}, err
}

err = m.Client.Get(ctx, request.NamespacedName, &pod)
if err != nil {
if apierr.IsNotFound(err) {
return reconcile.Result{}, nil
}

return reconcile.Result{}, err
}

_, err = controllerutil.CreateOrUpdate(ctx, m.Client, &pod, func() (err error) {
pod.SetLabels(m.sync(pod.GetLabels(), tenant.Spec.PodOptions.AdditionalMetadata.Labels))
pod.SetAnnotations(m.sync(pod.GetAnnotations(), tenant.Spec.PodOptions.AdditionalMetadata.Annotations))

return nil
})

return reconcile.Result{}, err
}

func (m *MetadataReconciler) getTenant(ctx context.Context, namespacedName types.NamespacedName, client client.Client) (*capsulev1beta2.Tenant, error) {
ns := &corev1.Namespace{}
tenant := &capsulev1beta2.Tenant{}

if err := client.Get(ctx, types.NamespacedName{Name: namespacedName.Namespace}, ns); err != nil {
return nil, err
}

capsuleLabel, _ := utils.GetTypeLabel(&capsulev1beta2.Tenant{})
if _, ok := ns.GetLabels()[capsuleLabel]; !ok {
return nil, NewNonTenantObject(namespacedName.Name)
}

if err := client.Get(ctx, types.NamespacedName{Name: ns.Labels[capsuleLabel]}, tenant); err != nil {
return nil, err
}

if tenant.Spec.PodOptions == nil || tenant.Spec.PodOptions.AdditionalMetadata == nil {
return nil, NewNoPodMetadata(namespacedName.Name)
}

return tenant, nil
}

func (m *MetadataReconciler) sync(available map[string]string, tenantSpec map[string]string) map[string]string {
if tenantSpec != nil {
if available == nil {
return tenantSpec
}

for key, value := range tenantSpec {
if available[key] != value {
available[key] = value
}
}
}

return available
}

func (m *MetadataReconciler) forOptionPerInstanceName(ctx context.Context) builder.ForOption {
return builder.WithPredicates(predicate.NewPredicateFuncs(func(object client.Object) bool {
return m.isNamespaceInTenant(ctx, object.GetNamespace())
}))
}

func (m *MetadataReconciler) isNamespaceInTenant(ctx context.Context, namespace string) bool {
tl := &capsulev1beta2.TenantList{}
if err := m.Client.List(ctx, tl, client.MatchingFieldsSelector{
Selector: fields.OneTermEqualSelector(".status.namespaces", namespace),
}); err != nil {
return false
}

return len(tl.Items) > 0
}

func (m *MetadataReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&corev1.Pod{}, m.forOptionPerInstanceName(ctx)).
Complete(m)
}
66 changes: 66 additions & 0 deletions docs/content/general/crds-apis.md
Original file line number Diff line number Diff line change
Expand Up @@ -2963,6 +2963,13 @@ TenantSpec defines the desired state of Tenant.
Specifies the label to control the placement of pods on a given pool of worker nodes. All namespaces created within the Tenant will have the node selector annotation. This annotation tells the Kubernetes scheduler to place pods on the nodes having the selector label. Optional.<br/>
</td>
<td>false</td>
</tr><tr>
<td><b><a href="#tenantspecpodoptions">podOptions</a></b></td>
<td>object</td>
<td>
Specifies options for the Pods deployed in the Tenant namespaces, such as additional metadata.<br/>
</td>
<td>false</td>
</tr><tr>
<td><b>preventDeletion</b></td>
<td>boolean</td>
Expand Down Expand Up @@ -4397,6 +4404,65 @@ NetworkPolicyPort describes a port to allow traffic on
</table>


### Tenant.spec.podOptions



Specifies options for the Pods deployed in the Tenant namespaces, such as additional metadata.

<table>
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Description</th>
<th>Required</th>
</tr>
</thead>
<tbody><tr>
<td><b><a href="#tenantspecpodoptionsadditionalmetadata">additionalMetadata</a></b></td>
<td>object</td>
<td>
Specifies additional labels and annotations the Capsule operator places on any Pod resource in the Tenant. Optional.<br/>
</td>
<td>false</td>
</tr></tbody>
</table>


### Tenant.spec.podOptions.additionalMetadata



Specifies additional labels and annotations the Capsule operator places on any Pod resource in the Tenant. Optional.

<table>
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Description</th>
<th>Required</th>
</tr>
</thead>
<tbody><tr>
<td><b>annotations</b></td>
<td>map[string]string</td>
<td>
<br/>
</td>
<td>false</td>
</tr><tr>
<td><b>labels</b></td>
<td>map[string]string</td>
<td>
<br/>
</td>
<td>false</td>
</tr></tbody>
</table>


### Tenant.spec.priorityClasses


Expand Down
Loading
Loading