Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
57ae12f
chore: correct metrics
oliverbaehler May 21, 2025
6b4c184
Merge branch 'main' of github.com:projectcapsule/capsule into feat/im…
oliverbaehler May 21, 2025
cde0bd4
chore: correct metrics
oliverbaehler May 22, 2025
6277369
feat: add serviceAccount impersonation
oliverbaehler May 28, 2025
11b12d2
chore: implement impersonation
oliverbaehler Jun 6, 2025
06321fc
chore: implement impersonation
oliverbaehler Jun 11, 2025
e51b8fe
chore: update helm-schema version
oliverbaehler Jun 16, 2025
501847d
chore: progress
oliverbaehler Jul 10, 2025
15c6977
feat: add error interval for secrets
oliverbaehler Aug 14, 2025
2fa7194
feat(config): add ignore user groups property
oliverbaehler Aug 14, 2025
2bad174
fix: regenerate manifests
oliverbaehler Aug 18, 2025
18fe577
little progress
oliverbaehler Aug 20, 2025
316d886
feat(docs): improve setup and ecosystem
oliverbaehler Aug 25, 2025
b8369a4
feat(docs): improve setup and ecosystem
oliverbaehler Aug 25, 2025
8370900
feat(docs): improve setup and ecosystem
oliverbaehler Aug 25, 2025
25e7473
feat(docs): improve setup and ecosystem
oliverbaehler Aug 26, 2025
5d39275
Merge branch 'main' of github.com:projectcapsule/capsule into feat/im…
oliverbaehler Aug 26, 2025
5fd0b1c
fix: correct base64 translation
oliverbaehler Aug 30, 2025
5536c9e
feat(controller): allow owners to promote serviceaccounts within tena…
oliverbaehler Sep 13, 2025
43ed31d
chore: only consider 0.10
oliverbaehler Oct 7, 2025
fb34953
chore: only consider 0.10
oliverbaehler Oct 8, 2025
a06956f
chore: only consider 0.10
oliverbaehler Oct 8, 2025
7dea2f8
chore: fix testing ci
oliverbaehler Oct 20, 2025
1bfe2b1
feat: migrate admission policies
oliverbaehler Oct 23, 2025
692072b
feat: migrate to resources api
oliverbaehler Nov 3, 2025
64f3c72
somewaht functional
oliverbaehler Nov 5, 2025
55ec198
fix(tenants): functional
oliverbaehler Nov 12, 2025
248dc8a
fix(tenants): functional
oliverbaehler Nov 12, 2025
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
10 changes: 6 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,9 @@ dev-setup:
--create-namespace \
--set 'crds.install=true' \
--set 'crds.exclusive=true'\
--set 'crds.createConfig=true'\
--set 'crds.createConfig=true'\
--set 'tls.enableController=false'\
--set "webhooks.exclusive=true"\
--set "webhooks.exclusive=true"\
--set "webhooks.service.url=$${WEBHOOK_URL}" \
--set "webhooks.service.caBundle=$${CA_BUNDLE}" \
Expand Down Expand Up @@ -242,10 +244,10 @@ API_GW_LOOKUP := kubernetes-sigs/gateway-api/
e2e-install-deps:
@$(KUBECTL) apply --force-conflicts --server-side=true -f https://github.com/$(API_GW_LOOKUP)/releases/download/$(API_GW_VERSION)/standard-install.yaml

e2e-build: kind
e2e-build: e2e-build-cluster e2e-install-deps e2e-install

e2e-build-cluster: kind
$(KIND) create cluster --wait=60s --name $(CLUSTER_NAME) --image kindest/node:$(KUBERNETES_SUPPORTED_VERSION)
$(MAKE) e2e-install-deps
$(MAKE) e2e-install

.PHONY: e2e-install
e2e-install: ko-build-all
Expand Down
2 changes: 2 additions & 0 deletions api/v1beta2/capsuleconfiguration_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ type CapsuleConfigurationSpec struct {
// when not using an already provided CA and certificate, or when these are managed externally with Vault, or cert-manager.
// +kubebuilder:default=true
EnableTLSReconciler bool `json:"enableTLSReconciler"` //nolint:tagliatelle
// Define Kubernetes-Client Configurations
ServiceAccountClient *api.ServiceAccountClient `json:"serviceAccountClient,omitempty"`
}

type NodeMetadata struct {
Expand Down
31 changes: 15 additions & 16 deletions api/v1beta2/tenantresource_global.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,21 @@ package v1beta2

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"

"github.com/projectcapsule/capsule/pkg/api"
"github.com/projectcapsule/capsule/pkg/meta"
)

// GlobalTenantResourceSpec defines the desired state of GlobalTenantResource.
type GlobalTenantResourceSpec struct {
TenantResourceSpec `json:",inline"`

// Resource Scope, Can either be
// - Tenant: Create Resources for each tenant in selected Tenants
// - Namespace: Create Resources for each namespace in selected Tenants
// +kubebuilder:default:=Namespace
Scope api.ResourceScope `json:"scope"`
// Defines the Tenant selector used target the tenants on which resources must be propagated.
TenantSelector metav1.LabelSelector `json:"tenantSelector,omitempty"`
TenantSelector metav1.LabelSelector `json:"tenantSelector,omitempty"`
TenantResourceSpec `json:",inline"`
}

// GlobalTenantResourceStatus defines the observed state of GlobalTenantResource.
Expand All @@ -22,23 +28,16 @@ type GlobalTenantResourceStatus struct {
SelectedTenants []string `json:"selectedTenants"`
// List of the replicated resources for the given TenantResource.
ProcessedItems ProcessedItems `json:"processedItems"`
}

type ProcessedItems []ObjectReferenceStatus

func (p *ProcessedItems) AsSet() sets.Set[string] {
set := sets.New[string]()

for _, i := range *p {
set.Insert(i.String())
}

return set
// Condition of the GlobalTenantResource.
Conditions meta.ConditionList `json:"conditions,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:scope=Cluster
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description="Reconcile Status for the tenant"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description="Reconcile Message for the tenant"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Age"

// GlobalTenantResource allows to propagate resource replications to a specific subset of Tenant resources.
type GlobalTenantResource struct {
Expand Down
32 changes: 32 additions & 0 deletions api/v1beta2/tenantresource_namespaced.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"

"github.com/projectcapsule/capsule/pkg/api"
"github.com/projectcapsule/capsule/pkg/meta"
)

// TenantResourceSpec defines the desired state of TenantResource.
Expand All @@ -20,8 +21,22 @@ type TenantResourceSpec struct {
// Disable this to keep replicated resources although the deletion of the replication manifest.
// +kubebuilder:default=true
PruningOnDelete *bool `json:"pruningOnDelete,omitempty"`
// When cordoning a replication it will no longer execute any applies or deletions (paused).
// This is useful for maintenances
// +kubebuilder:default=false
Cordoned *bool `json:"cordoned,omitempty"`
// Defines the rules to select targeting Namespace, along with the objects that must be replicated.
Resources []ResourceSpec `json:"resources"`
// Local ServiceAccount which will perform all the actions defined in the TenantResource
// You must provide permissions accordingly to that ServiceAccount
ServiceAccount *api.ServiceAccountReference `json:"serviceAccount,omitempty"`
// Enabling this allows TenanResources to interact with objects which were not created by a TenantResource. In this case on prune no deletion of the entire object is made.
// +kubebuilder:default=false
Adopt *bool `json:"adopt,omitempty"`
// Force indicates that in case of conflicts with server-side apply, the client should acquire ownership of the conflicting field.
// You may create collisions with this.
// +kubebuilder:default=false
Force *bool `json:"force,omitempty"`
}

type ResourceSpec struct {
Expand All @@ -35,6 +50,16 @@ type ResourceSpec struct {
// Besides the Capsule metadata required by TenantResource controller, defines additional metadata that must be
// added to the replicated resources.
AdditionalMetadata *api.AdditionalMetadataSpec `json:"additionalMetadata,omitempty"`
// Generators for advanced use cases
Generators []GeneratorItemSpec `json:"generators,omitempty"`
// Provide additional template context, which can be used throughout all
// the declared items for the replication
// +optional
Context *api.TemplateContext `json:"context,omitempty"`
// Automatically adds a label to all resources being patched by a tenantresource blocking any interactions from tenant users via admission webhook
// The label added is called
// +kubebuilder:default=true
Managed *bool `json:"managed,omitempty"`
}

// +kubebuilder:validation:XEmbeddedResource
Expand All @@ -45,12 +70,19 @@ type RawExtension struct {

// TenantResourceStatus defines the observed state of TenantResource.
type TenantResourceStatus struct {
// How items are processed by this instance
Size uint `json:"size"`
// List of the replicated resources for the given TenantResource.
ProcessedItems ProcessedItems `json:"processedItems"`
// Conditions of the TenantResource.
Conditions meta.ConditionList `json:"conditions,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description="Reconcile Status for the tenant"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description="Reconcile Message for the tenant"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Age"

// TenantResource allows a Tenant Owner, if enabled with proper RBAC, to propagate resources in its Namespace.
// The object must be deployed in a Tenant Namespace, and cannot reference object living in non-Tenant namespaces.
Expand Down
145 changes: 102 additions & 43 deletions api/v1beta2/tenantresource_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,70 +5,129 @@ package v1beta2

import (
"fmt"
"strings"

"github.com/projectcapsule/capsule/pkg/api"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8stypes "k8s.io/apimachinery/pkg/types"
)

type ResourceOptions struct {
// Template contains any amount of yaml which is applied to Kubernetes.
// This can be a single resource or multiple resources
Template string `json:"template,omitempty"`
// Missing Key Option for templating
// +kubebuilder:default=default
MissingKey MissingKeyOption `json:"missingKey,omitempty"`
}

// +kubebuilder:validation:Enum=default;zero;error
type MissingKeyOption string

func (p MissingKeyOption) String() string {
return string(p)
}

const (
MissingKeyDefault MissingKeyOption = "default"
MissingKeyZero MissingKeyOption = "zero"
MissingKeyError MissingKeyOption = "error"
)

type GeneratorItemSpec struct {
// Template contains any amount of yaml which is applied to Kubernetes.
// This can be a single resource or multiple resources
Template string `json:"template,omitempty"`
// Missing Key Option for templating
// +kubebuilder:default=default
MissingKey MissingKeyOption `json:"missingKey,omitempty"`
}

type ProcessedItems []ObjectReferenceStatus

// Adds a condition by type.
func (p *ProcessedItems) UpdateItem(item ObjectReferenceStatus) {
for i, stat := range *p {
if p.isEqual(stat, item) {
(*p)[i].ObjectReferenceStatusCondition = item.ObjectReferenceStatusCondition

return
}
}

*p = append(*p, item)
}

// Removes a condition by type.
func (p *ProcessedItems) RemoveItem(item ObjectReferenceStatus) {
filtered := make(ProcessedItems, 0, len(*p))

for _, stat := range *p {
if !p.isEqual(stat, item) {
filtered = append(filtered, stat)
}
}

*p = filtered
}

func (p *ProcessedItems) isEqual(a, b ObjectReferenceStatus) bool {
return a.ResourceID == b.ResourceID
}

type ObjectReferenceAbstract struct {
// Kind of the referent.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
Kind string `json:"kind"`
// Namespace of the referent.
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
Namespace string `json:"namespace"`
Namespace string `json:"namespace,omitempty"`
// API version of the referent.
APIVersion string `json:"apiVersion,omitempty"`
}

type ObjectReferenceStatus struct {
ObjectReferenceAbstract `json:",inline"`

// Name of the referent.
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
Name string `json:"name"`
}

type ObjectReference struct {
ObjectReferenceAbstract `json:",inline"`

// Label selector used to select the given resources in the given Namespace.
Selector metav1.LabelSelector `json:"selector"`
api.ResourceID `json:",inline"`
ObjectReferenceStatusCondition `json:",inline"`
}

func (in *ObjectReferenceStatus) String() string {
return fmt.Sprintf("Kind=%s,APIVersion=%s,Namespace=%s,Name=%s", in.Kind, in.APIVersion, in.Namespace, in.Name)
return fmt.Sprintf("Kind=%s,Group=%s,APIVersion=%s,Namespace=%s,Name=%s", in.Kind, in.Group, in.Version, in.Namespace, in.Name)
}

func (in *ObjectReferenceStatus) ParseFromString(value string) error {
rawParts := strings.Split(value, ",")

if len(rawParts) != 4 {
return fmt.Errorf("unexpected raw parts")
}

for _, i := range rawParts {
parts := strings.Split(i, "=")
type ObjectReferenceStatusOwner struct {
// Name of the owning object.
Name string `json:"name,omitempty"`
// UID of the owning object.
k8stypes.UID `json:"uid,omitempty" protobuf:"bytes,5,opt,name=uid"`
// Scope of the owning object.
Scope api.ResourceScope `json:"scope,omitempty"`
}

if len(parts) != 2 {
return fmt.Errorf("unrecognized separator")
}
type ObjectReferenceStatusCondition struct {
// status of the condition, one of True, False, Unknown.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:Enum=True;False;Unknown
Status metav1.ConditionStatus `json:"status" protobuf:"bytes,2,opt,name=status"`
// message is a human readable message indicating details about the transition.
// This may be an empty string.
// +kubebuilder:validation:MaxLength=32768
Message string `json:"message,omitempty" protobuf:"bytes,6,opt,name=message"`
// type of condition in CamelCase or in foo.example.com/CamelCase.
// ---
// Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be
// useful (see .node.status.conditions), the ability to deconflict is important.
// The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:Pattern=`^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$`
// +kubebuilder:validation:MaxLength=316
Type string `json:"type" protobuf:"bytes,1,opt,name=type"`
}

k, v := parts[0], parts[1]

switch k {
case "Kind":
in.Kind = v
case "APIVersion":
in.APIVersion = v
case "Namespace":
in.Namespace = v
case "Name":
in.Name = v
default:
return fmt.Errorf("unrecognized marker: %s", k)
}
}
type ObjectReference struct {
ObjectReferenceAbstract `json:",inline"`

return nil
// Label selector used to select the given resources in the given Namespace.
Selector metav1.LabelSelector `json:"selector"`
}
Loading
Loading