mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-02-14 09:59:57 +00:00
fix(controller): decode old object for delete requests (#1787)
* fix(controller): decode old object for delete requests Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: modernize golang Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: modernize golang Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: modernize golang Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> --------- Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
This commit is contained in:
3
Makefile
3
Makefile
@@ -150,6 +150,7 @@ dev-setup:
|
||||
--set 'crds.install=true' \
|
||||
--set 'crds.exclusive=true'\
|
||||
--set 'crds.createConfig=true'\
|
||||
--set "tls.enableController=false"\
|
||||
--set "webhooks.exclusive=true"\
|
||||
--set "webhooks.hooks.nodes.enabled=true"\
|
||||
--set "webhooks.service.url=$${WEBHOOK_URL}" \
|
||||
@@ -415,7 +416,7 @@ nwa:
|
||||
$(call go-install-tool,$(NWA),github.com/$(NWA_LOOKUP)@$(NWA_VERSION))
|
||||
|
||||
GOLANGCI_LINT := $(LOCALBIN)/golangci-lint
|
||||
GOLANGCI_LINT_VERSION := v2.5.0
|
||||
GOLANGCI_LINT_VERSION := v2.7.2
|
||||
GOLANGCI_LINT_LOOKUP := golangci/golangci-lint
|
||||
golangci-lint: ## Download golangci-lint locally if necessary.
|
||||
@test -s $(GOLANGCI_LINT) && $(GOLANGCI_LINT) -h | grep -q $(GOLANGCI_LINT_VERSION) || \
|
||||
|
||||
@@ -20,17 +20,21 @@ type TenantSpec struct {
|
||||
// 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. Optional.
|
||||
StorageClasses *api.AllowedListSpec `json:"storageClasses,omitempty"`
|
||||
// Specifies options for the Ingress resources, such as allowed hostnames and IngressClass. Optional.
|
||||
IngressOptions IngressOptions `json:"ingressOptions,omitempty"`
|
||||
// +optional
|
||||
IngressOptions IngressOptions `json:"ingressOptions,omitzero"`
|
||||
// Specifies the trusted Image Registries assigned to the Tenant. Capsule assures that all Pods resources created in the Tenant can use only one of the allowed trusted registries. Optional.
|
||||
ContainerRegistries *api.AllowedListSpec `json:"containerRegistries,omitempty"`
|
||||
// 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.
|
||||
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
|
||||
// Specifies the NetworkPolicies assigned to the Tenant. The assigned NetworkPolicies are inherited by any namespace created in the Tenant. Optional.
|
||||
NetworkPolicies api.NetworkPolicySpec `json:"networkPolicies,omitempty"`
|
||||
// +optional
|
||||
NetworkPolicies api.NetworkPolicySpec `json:"networkPolicies,omitzero"`
|
||||
// Specifies the resource min/max usage restrictions to the Tenant. The assigned values are inherited by any namespace created in the Tenant. Optional.
|
||||
LimitRanges api.LimitRangesSpec `json:"limitRanges,omitempty"`
|
||||
// +optional
|
||||
LimitRanges api.LimitRangesSpec `json:"limitRanges,omitzero"`
|
||||
// Specifies a list of ResourceQuota resources assigned to the Tenant. The assigned values are inherited by any namespace created in the Tenant. The Capsule operator aggregates ResourceQuota at Tenant level, so that the hard quota is never crossed for the given Tenant. This permits the Tenant owner to consume resources in the Tenant regardless of the namespace. Optional.
|
||||
ResourceQuota api.ResourceQuotaSpec `json:"resourceQuotas,omitempty"`
|
||||
// +optional
|
||||
ResourceQuota api.ResourceQuotaSpec `json:"resourceQuotas,omitzero"`
|
||||
// Specifies additional RoleBindings assigned to the Tenant. Capsule will ensure that all namespaces in the Tenant always contain the RoleBinding for the given ClusterRole. Optional.
|
||||
AdditionalRoleBindings []api.AdditionalRoleBindingsSpec `json:"additionalRoleBindings,omitempty"`
|
||||
// Specify the allowed values for the imagePullPolicies option in Pod resources. Capsule assures that all Pod resources created in the Tenant can use only one of the allowed policy. Optional.
|
||||
@@ -50,11 +54,13 @@ type TenantSpec struct {
|
||||
|
||||
// Tenant is the Schema for the tenants API.
|
||||
type Tenant struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitzero"`
|
||||
|
||||
Spec TenantSpec `json:"spec,omitempty"`
|
||||
Status TenantStatus `json:"status,omitempty"`
|
||||
Spec TenantSpec `json:"spec"`
|
||||
// +optional
|
||||
Status TenantStatus `json:"status,omitzero"`
|
||||
}
|
||||
|
||||
func (in *Tenant) Hub() {}
|
||||
@@ -64,7 +70,8 @@ func (in *Tenant) Hub() {}
|
||||
// TenantList contains a list of Tenant.
|
||||
type TenantList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
// +optional
|
||||
metav1.ListMeta `json:"metadata,omitzero"`
|
||||
|
||||
Items []Tenant `json:"items"`
|
||||
}
|
||||
|
||||
@@ -40,13 +40,14 @@ type CapsuleConfigurationSpec struct {
|
||||
// Allows to set different name rather than the canonical one for the Capsule configuration objects,
|
||||
// such as webhook secret or configurations.
|
||||
// +kubebuilder:default={TLSSecretName:"capsule-tls",mutatingWebhookConfigurationName:"capsule-mutating-webhook-configuration",validatingWebhookConfigurationName:"capsule-validating-webhook-configuration"}
|
||||
CapsuleResources CapsuleResources `json:"overrides,omitempty"`
|
||||
// +optional
|
||||
CapsuleResources CapsuleResources `json:"overrides,omitzero"`
|
||||
// Allows to set the forbidden metadata for the worker nodes that could be patched by a Tenant.
|
||||
// This applies only if the Tenant has an active NodeSelector, and the Owner have right to patch their nodes.
|
||||
NodeMetadata *NodeMetadata `json:"nodeMetadata,omitempty"`
|
||||
// Toggles the TLS reconciler, the controller that is able to generate CA and certificates for the webhooks
|
||||
// when not using an already provided CA and certificate, or when these are managed externally with Vault, or cert-manager.
|
||||
// +kubebuilder:default=true
|
||||
// +kubebuilder:default=false
|
||||
EnableTLSReconciler bool `json:"enableTLSReconciler"` //nolint:tagliatelle
|
||||
// Define entities which can act as Administrators in the capsule construct
|
||||
// These entities are automatically owners for all existing tenants. Meaning they can add namespaces to any tenant. However they must be specific by using the capsule label
|
||||
@@ -57,9 +58,11 @@ type CapsuleConfigurationSpec struct {
|
||||
|
||||
type NodeMetadata struct {
|
||||
// Define the labels that a Tenant Owner cannot set for their nodes.
|
||||
ForbiddenLabels api.ForbiddenListSpec `json:"forbiddenLabels"`
|
||||
// +optional
|
||||
ForbiddenLabels api.ForbiddenListSpec `json:"forbiddenLabels,omitzero"`
|
||||
// Define the annotations that a Tenant Owner cannot set for their nodes.
|
||||
ForbiddenAnnotations api.ForbiddenListSpec `json:"forbiddenAnnotations"`
|
||||
// +optional
|
||||
ForbiddenAnnotations api.ForbiddenListSpec `json:"forbiddenAnnotations,omitzero"`
|
||||
}
|
||||
|
||||
type CapsuleResources struct {
|
||||
@@ -81,10 +84,12 @@ type CapsuleResources struct {
|
||||
|
||||
// CapsuleConfiguration is the Schema for the Capsule configuration API.
|
||||
type CapsuleConfiguration struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
Spec CapsuleConfigurationSpec `json:"spec,omitempty"`
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitzero"`
|
||||
|
||||
Spec CapsuleConfigurationSpec `json:"spec"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
@@ -92,7 +97,7 @@ type CapsuleConfiguration struct {
|
||||
// CapsuleConfigurationList contains a list of CapsuleConfiguration.
|
||||
type CapsuleConfigurationList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
metav1.ListMeta `json:"metadata,omitzero"`
|
||||
|
||||
Items []CapsuleConfiguration `json:"items"`
|
||||
}
|
||||
|
||||
@@ -18,9 +18,11 @@ type NamespaceOptions struct {
|
||||
// Specifies additional labels and annotations the Capsule operator places on any Namespace resource in the Tenant via a list. Optional.
|
||||
AdditionalMetadataList []api.AdditionalMetadataSelectorSpec `json:"additionalMetadataList,omitempty"`
|
||||
// Define the labels that a Tenant Owner cannot set for their Namespace resources.
|
||||
ForbiddenLabels api.ForbiddenListSpec `json:"forbiddenLabels,omitempty"`
|
||||
// +optional
|
||||
ForbiddenLabels api.ForbiddenListSpec `json:"forbiddenLabels,omitzero"`
|
||||
// Define the annotations that a Tenant Owner cannot set for their Namespace resources.
|
||||
ForbiddenAnnotations api.ForbiddenListSpec `json:"forbiddenAnnotations,omitempty"`
|
||||
// +optional
|
||||
ForbiddenAnnotations api.ForbiddenListSpec `json:"forbiddenAnnotations,omitzero"`
|
||||
// If enabled only metadata from additionalMetadata is reconciled to the namespaces.
|
||||
//+kubebuilder:default:=false
|
||||
ManagedMetadataOnly bool `json:"managedMetadataOnly,omitempty"`
|
||||
|
||||
@@ -21,9 +21,11 @@ type ResourcePoolStatus struct {
|
||||
// Namespaces which are considered for claims
|
||||
Namespaces []string `json:"namespaces,omitempty"`
|
||||
// Tracks the quotas for the Resource.
|
||||
Claims ResourcePoolNamespaceClaimsStatus `json:"claims,omitempty"`
|
||||
// +optional
|
||||
Claims ResourcePoolNamespaceClaimsStatus `json:"claims,omitzero"`
|
||||
// Tracks the Usage from Claimed against what has been granted from the pool
|
||||
Allocation ResourcePoolQuotaStatus `json:"allocation,omitempty"`
|
||||
// +optional
|
||||
Allocation ResourcePoolQuotaStatus `json:"allocation,omitzero"`
|
||||
// Exhaustions from claims associated with the pool
|
||||
Exhaustions map[string]api.PoolExhaustionResource `json:"exhaustions,omitempty"`
|
||||
}
|
||||
|
||||
@@ -18,10 +18,12 @@ type ResourcePoolSpec struct {
|
||||
Quota corev1.ResourceQuotaSpec `json:"quota"`
|
||||
// The Defaults given for each namespace, the default is not counted towards the total allocation
|
||||
// When you use claims it's recommended to provision Defaults as the prevent the scheduling of any resources
|
||||
Defaults corev1.ResourceList `json:"defaults,omitempty"`
|
||||
// +optional
|
||||
Defaults corev1.ResourceList `json:"defaults,omitzero"`
|
||||
// Additional Configuration
|
||||
//+kubebuilder:default:={}
|
||||
Config ResourcePoolSpecConfiguration `json:"config,omitempty"`
|
||||
// +optional
|
||||
Config ResourcePoolSpecConfiguration `json:"config,omitzero"`
|
||||
}
|
||||
|
||||
type ResourcePoolSpecConfiguration struct {
|
||||
@@ -55,11 +57,15 @@ type ResourcePoolSpecConfiguration struct {
|
||||
// it's up the group of users within these namespaces, to manage the resources they consume per namespace. Each Resourcepool provisions a ResourceQuotainto all the selected namespaces. Then essentially the ResourcePoolClaims, when they can be assigned to the ResourcePool stack resources on top of that
|
||||
// ResourceQuota based on the namspace, where the ResourcePoolClaim was made from.
|
||||
type ResourcePool struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
Spec ResourcePoolSpec `json:"spec,omitempty"`
|
||||
Status ResourcePoolStatus `json:"status,omitempty"`
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitzero"`
|
||||
|
||||
Spec ResourcePoolSpec `json:"spec"`
|
||||
|
||||
// +optional
|
||||
Status ResourcePoolStatus `json:"status,omitzero"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
@@ -67,7 +73,9 @@ type ResourcePool struct {
|
||||
// ResourcePoolList contains a list of ResourcePool.
|
||||
type ResourcePoolList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
// +optional
|
||||
metav1.ListMeta `json:"metadata,omitzero"`
|
||||
|
||||
Items []ResourcePool `json:"items"`
|
||||
}
|
||||
|
||||
@@ -22,9 +22,11 @@ type ResourcePoolClaimSpec struct {
|
||||
// ResourceQuotaClaimStatus defines the observed state of ResourceQuotaClaim.
|
||||
type ResourcePoolClaimStatus struct {
|
||||
// Reference to the GlobalQuota being claimed from
|
||||
Pool api.StatusNameUID `json:"pool,omitempty"`
|
||||
// +optional
|
||||
Pool api.StatusNameUID `json:"pool,omitzero"`
|
||||
// Condtion for this resource claim
|
||||
Condition metav1.Condition `json:"condition,omitempty"`
|
||||
// +optional
|
||||
Condition metav1.Condition `json:"condition,omitzero"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
@@ -37,11 +39,15 @@ type ResourcePoolClaimStatus struct {
|
||||
|
||||
// ResourcePoolClaim is the Schema for the resourcepoolclaims API.
|
||||
type ResourcePoolClaim struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
Spec ResourcePoolClaimSpec `json:"spec,omitempty"`
|
||||
Status ResourcePoolClaimStatus `json:"status,omitempty"`
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitzero"`
|
||||
|
||||
Spec ResourcePoolClaimSpec `json:"spec"`
|
||||
|
||||
// +optional
|
||||
Status ResourcePoolClaimStatus `json:"status,omitzero"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
@@ -49,7 +55,9 @@ type ResourcePoolClaim struct {
|
||||
// ResourceQuotaClaimList contains a list of ResourceQuotaClaim.
|
||||
type ResourcePoolClaimList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
// +optional
|
||||
metav1.ListMeta `json:"metadata,omitzero"`
|
||||
|
||||
Items []ResourcePoolClaim `json:"items"`
|
||||
}
|
||||
|
||||
@@ -58,7 +58,8 @@ type TenantStatusNamespaceMetadata struct {
|
||||
|
||||
type TenantAvailableStatus struct {
|
||||
// Available Class Types within Tenant
|
||||
Classes TenantAvailableClassesStatus `json:"classes,omitempty"`
|
||||
// +optional
|
||||
Classes TenantAvailableClassesStatus `json:"classes,omitzero"`
|
||||
}
|
||||
|
||||
type TenantAvailableClassesStatus struct {
|
||||
|
||||
@@ -16,7 +16,8 @@ import (
|
||||
// TenantSpec defines the desired state of Tenant.
|
||||
type TenantSpec struct {
|
||||
// Specify Permissions for the Tenant.
|
||||
Permissions Permissions `json:"permissions,omitempty"`
|
||||
// +optional
|
||||
Permissions Permissions `json:"permissions,omitzero"`
|
||||
// Specifies the owners of the Tenant.
|
||||
// Optional
|
||||
Owners api.OwnerListSpec `json:"owners,omitempty"`
|
||||
@@ -32,7 +33,8 @@ type TenantSpec struct {
|
||||
// Optional.
|
||||
StorageClasses *api.DefaultAllowedListSpec `json:"storageClasses,omitempty"`
|
||||
// Specifies options for the Ingress resources, such as allowed hostnames and IngressClass. Optional.
|
||||
IngressOptions IngressOptions `json:"ingressOptions,omitempty"`
|
||||
// +optional
|
||||
IngressOptions IngressOptions `json:"ingressOptions,omitzero"`
|
||||
// Specifies the trusted Image Registries assigned to the Tenant. Capsule assures that all Pods resources created in the Tenant can use only one of the allowed trusted registries. Optional.
|
||||
ContainerRegistries *api.AllowedListSpec `json:"containerRegistries,omitempty"`
|
||||
// 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.
|
||||
@@ -40,13 +42,16 @@ type TenantSpec struct {
|
||||
// Deprecated: Use Tenant Replications instead (https://projectcapsule.dev/docs/replications/)
|
||||
//
|
||||
// Specifies the NetworkPolicies assigned to the Tenant. The assigned NetworkPolicies are inherited by any namespace created in the Tenant. Optional.
|
||||
NetworkPolicies api.NetworkPolicySpec `json:"networkPolicies,omitempty"`
|
||||
// +optional
|
||||
NetworkPolicies api.NetworkPolicySpec `json:"networkPolicies,omitzero"`
|
||||
// Deprecated: Use Tenant Replications instead (https://projectcapsule.dev/docs/replications/)
|
||||
//
|
||||
// Specifies the resource min/max usage restrictions to the Tenant. The assigned values are inherited by any namespace created in the Tenant. Optional.
|
||||
LimitRanges api.LimitRangesSpec `json:"limitRanges,omitempty"`
|
||||
// +optional
|
||||
LimitRanges api.LimitRangesSpec `json:"limitRanges,omitzero"`
|
||||
// Specifies a list of ResourceQuota resources assigned to the Tenant. The assigned values are inherited by any namespace created in the Tenant. The Capsule operator aggregates ResourceQuota at Tenant level, so that the hard quota is never crossed for the given Tenant. This permits the Tenant owner to consume resources in the Tenant regardless of the namespace. Optional.
|
||||
ResourceQuota api.ResourceQuotaSpec `json:"resourceQuotas,omitempty"`
|
||||
// +optional
|
||||
ResourceQuota api.ResourceQuotaSpec `json:"resourceQuotas,omitzero"`
|
||||
// Specifies additional RoleBindings assigned to the Tenant. Capsule will ensure that all namespaces in the Tenant always contain the RoleBinding for the given ClusterRole. Optional.
|
||||
AdditionalRoleBindings []api.AdditionalRoleBindingsSpec `json:"additionalRoleBindings,omitempty"`
|
||||
// Specify the allowed values for the imagePullPolicies option in Pod resources. Capsule assures that all Pod resources created in the Tenant can use only one of the allowed policy. Optional.
|
||||
@@ -63,7 +68,8 @@ type TenantSpec struct {
|
||||
// Specifies options for the DeviceClass resources.
|
||||
DeviceClasses *api.SelectorAllowedListSpec `json:"deviceClasses,omitempty"`
|
||||
// Specifies options for the GatewayClass resources.
|
||||
GatewayOptions GatewayOptions `json:"gatewayOptions,omitempty"`
|
||||
// +optional
|
||||
GatewayOptions GatewayOptions `json:"gatewayOptions,omitzero"`
|
||||
// Toggling the Tenant resources cordoning, when enable resources cannot be deleted.
|
||||
//+kubebuilder:default:=false
|
||||
Cordoned bool `json:"cordoned,omitempty"`
|
||||
@@ -110,11 +116,15 @@ func (p *Permissions) ListMatchingOwners(
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Age"
|
||||
// Tenant is the Schema for the tenants API.
|
||||
type Tenant struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
Spec TenantSpec `json:"spec,omitempty"`
|
||||
Status TenantStatus `json:"status,omitempty"`
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitzero"`
|
||||
|
||||
Spec TenantSpec `json:"spec"`
|
||||
|
||||
// +optional
|
||||
Status TenantStatus `json:"status,omitzero"`
|
||||
}
|
||||
|
||||
func (in *Tenant) GetNamespaces() (res []string) {
|
||||
@@ -130,7 +140,7 @@ func (in *Tenant) GetNamespaces() (res []string) {
|
||||
// TenantList contains a list of Tenant.
|
||||
type TenantList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
metav1.ListMeta `json:"metadata,omitzero"`
|
||||
|
||||
Items []Tenant `json:"items"`
|
||||
}
|
||||
|
||||
@@ -13,7 +13,8 @@ type GlobalTenantResourceSpec struct {
|
||||
TenantResourceSpec `json:",inline"`
|
||||
|
||||
// Defines the Tenant selector used target the tenants on which resources must be propagated.
|
||||
TenantSelector metav1.LabelSelector `json:"tenantSelector,omitempty"`
|
||||
// +optional
|
||||
TenantSelector metav1.LabelSelector `json:"tenantSelector,omitzero"`
|
||||
}
|
||||
|
||||
// GlobalTenantResourceStatus defines the observed state of GlobalTenantResource.
|
||||
@@ -21,7 +22,7 @@ type GlobalTenantResourceStatus struct {
|
||||
// List of Tenants addressed by the GlobalTenantResource.
|
||||
SelectedTenants []string `json:"selectedTenants"`
|
||||
// List of the replicated resources for the given TenantResource.
|
||||
ProcessedItems ProcessedItems `json:"processedItems"`
|
||||
ProcessedItems ProcessedItems `json:"processedItems,omitzero"`
|
||||
}
|
||||
|
||||
type ProcessedItems []ObjectReferenceStatus
|
||||
@@ -42,11 +43,15 @@ func (p *ProcessedItems) AsSet() sets.Set[string] {
|
||||
|
||||
// GlobalTenantResource allows to propagate resource replications to a specific subset of Tenant resources.
|
||||
type GlobalTenantResource struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
Spec GlobalTenantResourceSpec `json:"spec,omitempty"`
|
||||
Status GlobalTenantResourceStatus `json:"status,omitempty"`
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitzero"`
|
||||
|
||||
Spec GlobalTenantResourceSpec `json:"spec"`
|
||||
|
||||
// +optional
|
||||
Status GlobalTenantResourceStatus `json:"status,omitzero"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
@@ -54,7 +59,7 @@ type GlobalTenantResource struct {
|
||||
// GlobalTenantResourceList contains a list of GlobalTenantResource.
|
||||
type GlobalTenantResourceList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
metav1.ListMeta `json:"metadata,omitzero"`
|
||||
|
||||
Items []GlobalTenantResource `json:"items"`
|
||||
}
|
||||
|
||||
@@ -56,11 +56,15 @@ type TenantResourceStatus struct {
|
||||
// The object must be deployed in a Tenant Namespace, and cannot reference object living in non-Tenant namespaces.
|
||||
// For such cases, the GlobalTenantResource must be used.
|
||||
type TenantResource struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
Spec TenantResourceSpec `json:"spec,omitempty"`
|
||||
Status TenantResourceStatus `json:"status,omitempty"`
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitzero"`
|
||||
|
||||
Spec TenantResourceSpec `json:"spec"`
|
||||
|
||||
// +optional
|
||||
Status TenantResourceStatus `json:"status,omitzero"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
@@ -68,7 +72,7 @@ type TenantResource struct {
|
||||
// TenantResourceList contains a list of TenantResource.
|
||||
type TenantResourceList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
metav1.ListMeta `json:"metadata,omitzero"`
|
||||
|
||||
Items []TenantResource `json:"items"`
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ The following Values have changed key or Value:
|
||||
| manager.options.generateCertificates | bool | `true` | Specifies whether capsule webhooks certificates should be generated by capsule operator |
|
||||
| manager.options.ignoreUserWithGroups | list | `[]` | Define groups which when found in the request of a user will be ignored by the Capsule this might be useful if you have one group where all the users are in, but you want to separate administrators from normal users with additional groups. |
|
||||
| manager.options.labels | object | `{}` | Additional labels to add to the CapsuleConfiguration resource |
|
||||
| manager.options.logLevel | string | `"3"` | Set the log verbosity of the capsule with a value from 1 to 5 |
|
||||
| manager.options.logLevel | string | `"info"` | Set the log verbosity of the capsule with a value from 1 to 5 |
|
||||
| manager.options.nodeMetadata | object | `{"forbiddenAnnotations":{"denied":[],"deniedRegex":""},"forbiddenLabels":{"denied":[],"deniedRegex":""}}` | Allows to set the forbidden metadata for the worker nodes that could be patched by a Tenant |
|
||||
| manager.options.protectedNamespaceRegex | string | `""` | If specified, disallows creation of namespaces matching the passed regexp |
|
||||
| manager.options.userNames | list | `[]` | DEPRECATED: use users properties. Names of the users considered as Capsule users. |
|
||||
|
||||
@@ -72,7 +72,7 @@ spec:
|
||||
However ServiceAccounts which have been promoted to owner can not promote further serviceAccounts.
|
||||
type: boolean
|
||||
enableTLSReconciler:
|
||||
default: true
|
||||
default: false
|
||||
description: |-
|
||||
Toggles the TLS reconciler, the controller that is able to generate CA and certificates for the webhooks
|
||||
when not using an already provided CA and certificate, or when these are managed externally with Vault, or cert-manager.
|
||||
@@ -117,9 +117,6 @@ spec:
|
||||
deniedRegex:
|
||||
type: string
|
||||
type: object
|
||||
required:
|
||||
- forbiddenAnnotations
|
||||
- forbiddenLabels
|
||||
type: object
|
||||
overrides:
|
||||
default:
|
||||
@@ -198,6 +195,8 @@ spec:
|
||||
required:
|
||||
- enableTLSReconciler
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
|
||||
@@ -291,6 +291,8 @@ spec:
|
||||
- processedItems
|
||||
- selectedTenants
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
|
||||
@@ -151,6 +151,8 @@ spec:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
|
||||
@@ -321,6 +321,8 @@ spec:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
|
||||
@@ -239,6 +239,8 @@ spec:
|
||||
required:
|
||||
- processedItems
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
|
||||
@@ -1074,6 +1074,8 @@ spec:
|
||||
- size
|
||||
- state
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: false
|
||||
@@ -2890,6 +2892,8 @@ spec:
|
||||
- size
|
||||
- state
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
|
||||
@@ -177,7 +177,7 @@ manager:
|
||||
# -- Workers (MaxConcurrentReconciles) is the maximum number of concurrent Reconciles which can be run (ALPHA).
|
||||
workers: 1
|
||||
# -- Set the log verbosity of the capsule with a value from 1 to 5
|
||||
logLevel: '3'
|
||||
logLevel: "info"
|
||||
# -- Define entities which are considered part of the Capsule construct.
|
||||
# Users not mentioned here will be ignored by Capsule
|
||||
users:
|
||||
|
||||
@@ -91,15 +91,26 @@ var _ = Describe("creating namespace with status lifecycle", Label("namespace",
|
||||
})
|
||||
|
||||
By("removing first namespace", func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), ns1)).Should(Succeed())
|
||||
cs := impersonationClient(tnt.Spec.Owners[0].UserSpec.Name, withDefaultGroups(nil))
|
||||
Expect(cs.Delete(context.TODO(), ns1)).Should(Succeed())
|
||||
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.GetName()}, t)).Should(Succeed())
|
||||
Eventually(func(g Gomega) {
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
|
||||
Expect(t.Status.Size).To(Equal(uint(1)))
|
||||
err := k8sClient.Get(
|
||||
context.TODO(),
|
||||
types.NamespacedName{Name: tnt.GetName()},
|
||||
t,
|
||||
)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
g.Expect(t.Status.Size).To(Equal(uint(1)))
|
||||
|
||||
instance := t.Status.GetInstance(&capsulev1beta2.TenantStatusNamespaceItem{Name: ns1.GetName(), UID: ns1.GetUID()})
|
||||
Expect(instance).To(BeNil(), "Namespace instance should be nil")
|
||||
instance := t.Status.GetInstance(&capsulev1beta2.TenantStatusNamespaceItem{
|
||||
Name: ns1.GetName(),
|
||||
UID: ns1.GetUID(),
|
||||
})
|
||||
g.Expect(instance).To(BeNil(), "Namespace instance should be nil")
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
})
|
||||
|
||||
By("removing second namespace", func() {
|
||||
|
||||
@@ -104,9 +104,9 @@ var _ = Describe("Promoting ServiceAccounts to Owners", Label("config"), Label("
|
||||
client client.Client
|
||||
matcher otypes.GomegaMatcher
|
||||
}{
|
||||
"owner": {client: impersonationClient(tnt.Spec.Owners[0].Name, withDefaultGroups(make([]string, 0)), k8sClient.Scheme()), matcher: Not(Succeed())},
|
||||
"rb-user": {client: impersonationClient("bob", withDefaultGroups(make([]string, 0)), k8sClient.Scheme()), matcher: Not(Succeed())},
|
||||
"rb-sa": {client: impersonationClient("system:serviceaccount:"+sa.GetNamespace()+":default", withDefaultGroups(make([]string, 0)), k8sClient.Scheme()), matcher: Not(Succeed())},
|
||||
"owner": {client: impersonationClient(tnt.Spec.Owners[0].Name, withDefaultGroups(make([]string, 0))), matcher: Not(Succeed())},
|
||||
"rb-user": {client: impersonationClient("bob", withDefaultGroups(make([]string, 0))), matcher: Not(Succeed())},
|
||||
"rb-sa": {client: impersonationClient("system:serviceaccount:"+sa.GetNamespace()+":default", withDefaultGroups(make([]string, 0))), matcher: Not(Succeed())},
|
||||
}
|
||||
|
||||
for name, tc := range personas {
|
||||
@@ -197,9 +197,9 @@ var _ = Describe("Promoting ServiceAccounts to Owners", Label("config"), Label("
|
||||
client client.Client
|
||||
matcher otypes.GomegaMatcher
|
||||
}{
|
||||
"rb-user": {client: impersonationClient("bob", withDefaultGroups(make([]string, 0)), k8sClient.Scheme()), matcher: Not(Succeed())},
|
||||
"rb-sa": {client: impersonationClient("system:serviceaccount:"+sa.GetNamespace()+":default", withDefaultGroups(make([]string, 0)), k8sClient.Scheme()), matcher: Not(Succeed())},
|
||||
"owner": {client: impersonationClient(tnt.Spec.Owners[0].Name, withDefaultGroups(make([]string, 0)), k8sClient.Scheme()), matcher: Succeed()},
|
||||
"rb-user": {client: impersonationClient("bob", withDefaultGroups(make([]string, 0))), matcher: Not(Succeed())},
|
||||
"rb-sa": {client: impersonationClient("system:serviceaccount:"+sa.GetNamespace()+":default", withDefaultGroups(make([]string, 0))), matcher: Not(Succeed())},
|
||||
"owner": {client: impersonationClient(tnt.Spec.Owners[0].Name, withDefaultGroups(make([]string, 0))), matcher: Succeed()},
|
||||
}
|
||||
|
||||
for name, tc := range personas {
|
||||
@@ -258,7 +258,7 @@ var _ = Describe("Promoting ServiceAccounts to Owners", Label("config"), Label("
|
||||
client client.Client
|
||||
matcher otypes.GomegaMatcher
|
||||
}{
|
||||
"owner": {client: impersonationClient(tnt.Spec.Owners[0].Name, withDefaultGroups(make([]string, 0)), k8sClient.Scheme()), matcher: Succeed()},
|
||||
"owner": {client: impersonationClient(tnt.Spec.Owners[0].Name, withDefaultGroups(make([]string, 0))), matcher: Succeed()},
|
||||
}
|
||||
|
||||
for name, tc := range personas {
|
||||
@@ -294,14 +294,21 @@ var _ = Describe("Promoting ServiceAccounts to Owners", Label("config"), Label("
|
||||
saClient := impersonationClient(
|
||||
fmt.Sprintf("system:serviceaccount:%s:%s", ns.Name, sa.Name),
|
||||
nil,
|
||||
k8sClient.Scheme(),
|
||||
)
|
||||
|
||||
newNs := NewNamespace("")
|
||||
Expect(saClient.Create(context.TODO(), newNs)).To(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElements(ns.GetName()))
|
||||
By("preventing the service account from deleting the namespace", func() {
|
||||
newNs := NewNamespace("")
|
||||
Expect(saClient.Create(context.TODO(), newNs)).To(Succeed())
|
||||
|
||||
Expect(saClient.Delete(context.TODO(), newNs)).To(Not(Succeed()))
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).
|
||||
Should(ContainElements(ns.GetName(), newNs.GetName()))
|
||||
|
||||
Eventually(func(g Gomega) {
|
||||
// Deletion should eventually be forbidden / fail
|
||||
g.Expect(saClient.Delete(context.TODO(), newNs)).
|
||||
ToNot(Succeed())
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
})
|
||||
|
||||
for name, tc := range personas {
|
||||
By(fmt.Sprintf("trying to promote SA as %s", name))
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
@@ -115,16 +114,17 @@ func ownerClient(owner api.UserSpec) (cs kubernetes.Interface) {
|
||||
return cs
|
||||
}
|
||||
|
||||
func impersonationClient(user string, groups []string, scheme *runtime.Scheme) client.Client {
|
||||
c, err := config.GetConfig()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
c.Impersonate = rest.ImpersonationConfig{
|
||||
func impersonationClient(user string, groups []string) client.Client {
|
||||
impersonatedCfg := rest.CopyConfig(cfg)
|
||||
impersonatedCfg.Impersonate = rest.ImpersonationConfig{
|
||||
UserName: user,
|
||||
Groups: groups,
|
||||
}
|
||||
cl, err := client.New(c, client.Options{Scheme: scheme})
|
||||
|
||||
c, err := client.New(impersonatedCfg, client.Options{Scheme: k8sClient.Scheme()})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
return cl
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func withDefaultGroups(groups []string) []string {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"sync"
|
||||
|
||||
"github.com/valyala/fasttemplate"
|
||||
@@ -40,13 +41,8 @@ func prepareAdditionalMetadata(m map[string]string) map[string]string {
|
||||
return make(map[string]string)
|
||||
}
|
||||
|
||||
// we need to create a new map to avoid modifying the original one
|
||||
copied := make(map[string]string, len(m))
|
||||
for k, v := range m {
|
||||
copied[k] = v
|
||||
}
|
||||
|
||||
return copied
|
||||
// clone without mutating the original
|
||||
return maps.Clone(m)
|
||||
}
|
||||
|
||||
func (r *Processor) HandlePruning(ctx context.Context, current, desired sets.Set[string]) (updateStatus bool) {
|
||||
@@ -249,12 +245,12 @@ func (r *Processor) HandleSection(ctx context.Context, tnt capsulev1beta2.Tenant
|
||||
|
||||
t := fasttemplate.New(template, "{{ ", " }}")
|
||||
|
||||
tmplString := t.ExecuteString(map[string]interface{}{
|
||||
tmplString := t.ExecuteString(map[string]any{
|
||||
"tenant.name": tnt.Name,
|
||||
"namespace": ns.Name,
|
||||
})
|
||||
|
||||
obj, keysAndValues := unstructured.Unstructured{}, []interface{}{"index", rawIndex}
|
||||
obj, keysAndValues := unstructured.Unstructured{}, []any{"index", rawIndex}
|
||||
|
||||
if _, _, decodeErr := codecFactory.UniversalDeserializer().Decode([]byte(tmplString), nil, &obj); decodeErr != nil {
|
||||
log.Error(decodeErr, "unable to deserialize rawItem", keysAndValues...)
|
||||
@@ -304,27 +300,18 @@ func (r *Processor) createOrUpdate(ctx context.Context, obj *unstructured.Unstru
|
||||
rv := actual.GetResourceVersion()
|
||||
actual.SetUnstructuredContent(desired.Object)
|
||||
|
||||
combinedLabels := obj.GetLabels()
|
||||
if combinedLabels == nil {
|
||||
combinedLabels = make(map[string]string)
|
||||
}
|
||||
|
||||
for key, value := range labels {
|
||||
combinedLabels[key] = value
|
||||
}
|
||||
combinedLabels := map[string]string{}
|
||||
maps.Copy(combinedLabels, obj.GetLabels())
|
||||
maps.Copy(combinedLabels, labels)
|
||||
|
||||
actual.SetLabels(combinedLabels)
|
||||
|
||||
combinedAnnotations := obj.GetAnnotations()
|
||||
if combinedAnnotations == nil {
|
||||
combinedAnnotations = make(map[string]string)
|
||||
}
|
||||
|
||||
for key, value := range annotations {
|
||||
combinedAnnotations[key] = value
|
||||
}
|
||||
combinedAnnotations := map[string]string{}
|
||||
maps.Copy(combinedAnnotations, obj.GetAnnotations())
|
||||
maps.Copy(combinedAnnotations, annotations)
|
||||
|
||||
actual.SetAnnotations(combinedAnnotations)
|
||||
|
||||
actual.SetResourceVersion(rv)
|
||||
actual.SetUID(UID)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ package tenant
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -155,13 +156,7 @@ func (r *Manager) SetupWithManager(mgr ctrl.Manager, ctrlConfig utils.Controller
|
||||
q workqueue.TypedRateLimitingInterface[reconcile.Request],
|
||||
) {
|
||||
r.enqueueForTenantsWithCondition(ctx, e.Object, q, func(tnt *capsulev1beta2.Tenant, c client.Object) bool {
|
||||
for _, n := range tnt.Status.Namespaces {
|
||||
if n == c.GetNamespace() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return slices.Contains(tnt.Status.Namespaces, c.GetNamespace())
|
||||
})
|
||||
},
|
||||
UpdateFunc: func(
|
||||
@@ -170,13 +165,7 @@ func (r *Manager) SetupWithManager(mgr ctrl.Manager, ctrlConfig utils.Controller
|
||||
q workqueue.TypedRateLimitingInterface[reconcile.Request],
|
||||
) {
|
||||
r.enqueueForTenantsWithCondition(ctx, e.ObjectNew, q, func(tnt *capsulev1beta2.Tenant, c client.Object) bool {
|
||||
for _, n := range tnt.Status.Namespaces {
|
||||
if n == c.GetNamespace() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return slices.Contains(tnt.Status.Namespaces, c.GetNamespace())
|
||||
})
|
||||
},
|
||||
DeleteFunc: func(
|
||||
@@ -241,6 +230,7 @@ func (r *Manager) SetupWithManager(mgr ctrl.Manager, ctrlConfig utils.Controller
|
||||
|
||||
func (r Manager) Reconcile(ctx context.Context, request ctrl.Request) (result ctrl.Result, err error) {
|
||||
r.Log = r.Log.WithValues("Request.Name", request.Name)
|
||||
|
||||
// Fetch the Tenant instance
|
||||
instance := &capsulev1beta2.Tenant{}
|
||||
if err = r.Get(ctx, request.NamespacedName, instance); err != nil {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -213,7 +214,12 @@ func (r *Manager) collectNamespaces(ctx context.Context, tenant *capsulev1beta2.
|
||||
return err
|
||||
}
|
||||
|
||||
tenant.AssignNamespaces(list.Items)
|
||||
// Drop namespaces that are currently being deleted (DeletionTimestamp != nil)
|
||||
activeNamespaces := slices.DeleteFunc(list.Items, func(ns corev1.Namespace) bool {
|
||||
return ns.DeletionTimestamp != nil
|
||||
})
|
||||
|
||||
tenant.AssignNamespaces(activeNamespaces)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
@@ -98,12 +100,6 @@ func LabelsChanged(keys []string, oldLabels, newLabels map[string]string) bool {
|
||||
|
||||
func NamesMatchingPredicate(names ...string) builder.Predicates {
|
||||
return builder.WithPredicates(predicate.NewPredicateFuncs(func(object client.Object) bool {
|
||||
for _, name := range names {
|
||||
if object.GetName() == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return slices.Contains(names, object.GetName())
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ type handler struct {
|
||||
handlers []webhook.TypedHandler[*corev1.Namespace]
|
||||
}
|
||||
|
||||
//nolint:dupl
|
||||
func (h *handler) OnCreate(c client.Client, decoder admission.Decoder, recorder record.EventRecorder) webhook.Func {
|
||||
return func(ctx context.Context, req admission.Request) *admission.Response {
|
||||
userIsAdmin := users.IsAdminUser(req, h.cfg.Administrators())
|
||||
@@ -63,35 +62,8 @@ func (h *handler) OnCreate(c client.Client, decoder admission.Decoder, recorder
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:dupl
|
||||
func (h *handler) OnDelete(c client.Client, decoder admission.Decoder, recorder record.EventRecorder) webhook.Func {
|
||||
return func(ctx context.Context, req admission.Request) *admission.Response {
|
||||
userIsAdmin := users.IsAdminUser(req, h.cfg.Administrators())
|
||||
|
||||
if !userIsAdmin && !users.IsCapsuleUser(ctx, c, h.cfg, req.UserInfo.Username, req.UserInfo.Groups) {
|
||||
return nil
|
||||
}
|
||||
|
||||
ns := &corev1.Namespace{}
|
||||
if err := decoder.Decode(req, ns); err != nil {
|
||||
return utils.ErroredResponse(err)
|
||||
}
|
||||
|
||||
tnt, err := tenant.GetTenantByLabels(ctx, c, ns)
|
||||
if err != nil {
|
||||
return utils.ErroredResponse(err)
|
||||
}
|
||||
|
||||
if tnt == nil && userIsAdmin {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, hndl := range h.handlers {
|
||||
if response := hndl.OnDelete(c, ns, decoder, recorder)(ctx, req); response != nil {
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
return func(context.Context, admission.Request) *admission.Response {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package mutation
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"maps"
|
||||
"net/http"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -134,12 +135,10 @@ func mergeStringMap(dst, src map[string]string) map[string]string {
|
||||
}
|
||||
|
||||
if dst == nil {
|
||||
dst = make(map[string]string, len(src))
|
||||
return maps.Clone(src)
|
||||
}
|
||||
|
||||
for k, v := range src {
|
||||
dst[k] = v
|
||||
}
|
||||
maps.Copy(dst, src)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ type handler struct {
|
||||
handlers []webhook.TypedHandlerWithTenant[*corev1.Namespace]
|
||||
}
|
||||
|
||||
//nolint:dupl
|
||||
func (h *handler) OnCreate(c client.Client, decoder admission.Decoder, recorder record.EventRecorder) webhook.Func {
|
||||
return func(ctx context.Context, req admission.Request) *admission.Response {
|
||||
userIsAdmin := users.IsAdminUser(req, h.cfg.Administrators())
|
||||
@@ -66,7 +65,6 @@ func (h *handler) OnCreate(c client.Client, decoder admission.Decoder, recorder
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:dupl
|
||||
func (h *handler) OnDelete(c client.Client, decoder admission.Decoder, recorder record.EventRecorder) webhook.Func {
|
||||
return func(ctx context.Context, req admission.Request) *admission.Response {
|
||||
userIsAdmin := users.IsAdminUser(req, h.cfg.Administrators())
|
||||
@@ -76,7 +74,7 @@ func (h *handler) OnDelete(c client.Client, decoder admission.Decoder, recorder
|
||||
}
|
||||
|
||||
ns := &corev1.Namespace{}
|
||||
if err := decoder.Decode(req, ns); err != nil {
|
||||
if err := decoder.DecodeRaw(req.OldObject, ns); err != nil {
|
||||
return utils.ErroredResponse(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,9 @@ import (
|
||||
// +kubebuilder:object:generate=true
|
||||
type PoolExhaustionResource struct {
|
||||
// Available Resources to be claimed
|
||||
Available resource.Quantity `json:"available,omitempty"`
|
||||
// +optional
|
||||
Available resource.Quantity `json:"available,omitzero"`
|
||||
// Requesting Resources
|
||||
Requesting resource.Quantity `json:"requesting,omitempty"`
|
||||
// +optional
|
||||
Requesting resource.Quantity `json:"requesting,omitzero"`
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"sort"
|
||||
)
|
||||
|
||||
@@ -19,10 +20,8 @@ func (o OwnerListSpec) IsOwner(name string, groups []string) bool {
|
||||
return true
|
||||
}
|
||||
case GroupOwner:
|
||||
for _, group := range groups {
|
||||
if group == owner.Name {
|
||||
return true
|
||||
}
|
||||
if slices.Contains(groups, owner.Name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@ type ServiceOptions struct {
|
||||
// Specifies the external IPs that can be used in Services with type ClusterIP. An empty list means no IPs are allowed. Optional.
|
||||
ExternalServiceIPs *ExternalServiceIPsSpec `json:"externalIPs,omitempty"`
|
||||
// Define the labels that a Tenant Owner cannot set for their Service resources.
|
||||
ForbiddenLabels ForbiddenListSpec `json:"forbiddenLabels,omitempty"`
|
||||
// +optional
|
||||
ForbiddenLabels ForbiddenListSpec `json:"forbiddenLabels,omitzero"`
|
||||
// Define the annotations that a Tenant Owner cannot set for their Service resources.
|
||||
ForbiddenAnnotations ForbiddenListSpec `json:"forbiddenAnnotations,omitempty"`
|
||||
// +optional
|
||||
ForbiddenAnnotations ForbiddenListSpec `json:"forbiddenAnnotations,omitzero"`
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ func TemplateForTenantAndNamespace(m map[string]string, tnt *capsulev1beta2.Tena
|
||||
}
|
||||
|
||||
t := fasttemplate.New(v, "{{ ", " }}")
|
||||
tmplString := t.ExecuteString(map[string]interface{}{
|
||||
tmplString := t.ExecuteString(map[string]any{
|
||||
"tenant.name": tnt.Name,
|
||||
"namespace": ns.Name,
|
||||
})
|
||||
|
||||
@@ -50,6 +50,7 @@ func IsCapsuleUser(
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:modernize
|
||||
for _, group := range cfg.UserGroups() {
|
||||
if groupList.Find(group) {
|
||||
if len(cfg.IgnoreUserWithGroups()) > 0 {
|
||||
|
||||
Reference in New Issue
Block a user