Global Owner
This is an example controller implementation using metacontroller for a Custom Resource called GlobalOwner, which is responsible for setting ownership on any kubernetes native or custom resources across the cluster.
Examples
This example presents the functionality by adopting ConfigMaps matching label selector expression and setting ownership on those resources across the cluster, but the same process could be applied to any resource kind existing in the cluster. For each resource type the permissions to own the object will be granularly adjusted.
Steps
To create an example GlobalOwner and a ConfigMap:
kubectl apply -f example
A GlobalOwner resource will be created and will adopt all resources matching the given GVKs provided in the spec.childResources field assuming they match the label selector spec.selector value.
This example shows how a GlobalOwner resource could be used to adopt all ConfigMaps that have an adopt label specified.
Example ConfigMap and a Secret resource located in example/example-resource-set.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
  name: test
  namespace: default
  labels:
    adopt: "true"
data:
  some: "value"
  other: "value"
---
apiVersion: v1
kind: Secret
metadata:
  name: test
  namespace: default
  labels:
    adopt: "true"
data: {}
After these resources are applied in the cluster, the ownership reference should be set on the resource.
$ kubectl get globalowner -o yaml
returns
apiVersion: globalowner.metacontroller.io/v1alpha1
kind: GlobalOwner
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"globalowner.metacontroller.io/v1alpha1","kind":"GlobalOwner","metadata":{"annotations":{},"name":"global-owner"},"spec":{"selector":{"matchExpressions":[{"key":"skip","operator":"DoesNotExist"}]}}}
  creationTimestamp: "2023-09-11T11:18:25Z"
  generation: 1
  name: global-owner
  resourceVersion: "3436"
  uid: 53f365d2-4c8e-469b-b09a-62994a968f8f
spec:
  childResources:
  - apiVersion: v1
    resource: secrets
    namespace: default
    names:
    - test
  - apiVersion: v1
    resource: configmaps
status:
  observedGeneration: 1
$ kubectl get cm test -n default -o yaml
will show that a ConfigMap has an OwnershipReference pointing to the GlobalOwner resource.
apiVersion: v1
data:
  other: value
  some: value
kind: ConfigMap
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","data":{"other":"value","some":"value"},"kind":"ConfigMap","metadata":{"annotations":{},"labels":{"adopt":"true"},"name":"test","namespace":"default"}}
  creationTimestamp: "2023-09-11T12:23:41Z"
  labels:
    adopt: "true"
  name: test
  namespace: default
  ownerReferences:
  - apiVersion: globalowner.metacontroller.io/v1alpha1
    blockOwnerDeletion: true
    controller: true
    kind: GlobalOwner
    name: global-owner
    uid: 6a3269db-f713-41cb-8111-6155f2c2b4b7
  resourceVersion: "1602"
  uid: e3aa2bc8-7615-44de-a788-e6a5296b47bb
API
This section describes APIs used to interact with the Global Owner controller.
Global Owner
This document describes the Global Owner resource.
Group Owner
This document describes the internal Group Owner resource role, resource fields and behaviour in the cluster.
Global Owner resource
This is an API specification for the GlobalOwner resource.
Resources
This is a list of structures representing the GlobalOwner resource API.
GlobalOwner
type GlobalOwner struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata"`
	Spec   GlobalOwnerSpec   `json:"spec"`
	Status GlobalOwnerStatus `json:"status,omitempty"`
}
GlobalOwner spec
type GlobalOwnerSpec struct {
	// Global label selector value. Will be applied to all child resources
	// if resource does not have its own.
	Selector       *metav1.LabelSelector `json:"selector,omitempty"`
	// A list of child resources which global owner should adopt.
	// Each child resource is passed to generated Group Owner object spec.
	//
	// The order of the resources in the list specifies removal ordering,
	// top to bottom.
	ChildResources []ChildResource       `json:"childResources"`
}
GlobalOwner resource rule
type ResourceRule struct {
	// Resource api version, for example: v1
	APIVersion string `json:"apiVersion"`
	// Resource plural name, for example: secrets
	Resource   string `json:"resource"`
}
GlobalOwner child resource
type ChildResource struct {
	ResourceRule `json:",inline"`
	// A list of unique names for the resources to adopt
	// Mutually exclusive with the selector value.
	Names     []string              `json:"names,omitempty"`
	// A namespace, where the resources should be looked up.
	Namespace string                `json:"namespace,omitempty"`
	// Label selector value for resource group.
	// Mutually exclusive with the names and namespace values.
	// Has precedence on Names/Namespaces if specified.
	Selector  *metav1.LabelSelector `json:"selector,omitempty"`
}
GlobalOwner status
type GlobalOwnerStatus struct {
	ObservedGeneration int `json:"observedGeneration,omitempty"`
}
Group Owner resource
This is an API specification for the GroupOwner resource.
Resources
This is a list of structures representing the GroupOwner resource API.
GroupOwner resource
type GroupOwner struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata"`
	Spec   GroupOwnerSpec   `json:"spec"`
	Status GroupOwnerStatus `json:"status,omitempty"`
}
GroupOwner spec
type GroupOwnerSpec struct {
	Selector      *metav1.LabelSelector `json:"selector,omitempty"`
	ChildResource ChildResource         `json:"childResource"`
}
GroupOwner status
type GroupOwnerStatus struct {
	OwnedResources     []OwnedResource `json:"ownedResources,omitempty"`
	ObservedGeneration int             `json:"observedGeneration,omitempty"`
}
OwnedResource
type OwnedResource struct {
	metav1.TypeMeta `json:",inline"`
	Name      string `json:"name,omitempty"`
	Namespace string `json:"namespace,omitempty"`
}
Concepts
This section contains the concepts and the architecture of individual components.
Terms
Bootstrap
This page describes the bootstrap phase of the controller.
Adopt
This page descibes the adoption process for the selected resources
Ownership chain
This page descibes the ownership chain process between individual group owner resources
Deletion
This page describes how the deletion process is being performed
Bootstrap
This process involves creating infrastructure resources, bound to the parent GlobalOwner object by ownership references, and allowing the child objects to be adopted by the GlobalOwner resource later.
Steps
Bootstrap process is carried by a single DecoratorController replica created during installation phase.
The process consists of 2 parts:
- For each child group in the GlobalOwnerresource, create aCompositeControllerreplica.
- Create an aggregated ClusterRolewith permissions required to access every resource from child groups. The rules in this role will aggregate to themetacontrollerreplica installed with the controller.
A ClusterRole per each group has only these permissions:
- get
- list
- watch
- update- required for the created- CompositeControllerto apply ownership reference on the resource.
Cleanup
Upon removal of the parent GlobalOwner resource, every bootstrapped component will be deleted using kubernetes garbage collection. This allows for the aggregated permissions to be scaled down upon removal, as the ClusterRole is getting removed.
Adopt
This process involves listing and watching resource groups specified under the ChildResources field in the GlobalOwner resource, and setting ownership references on each observed resource, using metacontroller adoption rules.
Process
Using collected objects from the related list collected using the customize hook semantics, an adopt CompositeController is setting a list of children resources in the sync hook response matching the namespace and name of each related resource. Returned list of children may look like:
[
    {
        "kind": "Secret",
        "apiVersion": "v1",
        "name": "owned-secret",
        "namespace": "default",
    }, {
        "kind": "ConfigMap",
        "apiVersion": "v1",
        "name": "owned-config-map",
        "namespace": "default",
    },
]
As every object in the list is already created in the cluster, the only change the metacontroller replica will do, is to apply the ownership references on the object, pointing to the parent GroupOwner resource instance.
Cleanup
Upon removal of the parent GlobalOwner resource, each GroupOwner resource and subsequentially all adopted resources will be removed in the order specified by the ChildResources content. Resource groups will be removed from top to bottom of the list.
Ownership chain
This process creates a GroupOwner resource per each resource group specified in the parent GlobalOwner resource spec.
Process
The process of setting up ownership chain consists of 2 parts.
When the bootstrap process finishes, each created CompositeController is creating a GroupOwner resource replica from the parents GlobalOwner children resource group.
Each of the created GroupOwner resources have a label matching the CompositeController label selector, for narrowing down the adopted resources to the target GroupOwner object. Secrets owned by the Secret group owner.
Once all of the GroupOwner resources are created, the controller is setting ownership references between the GroupOwner resources. This (in the best case scenario - see the issue) allows the removal process to fully rely on the kubernetes garbage collection by using foreground cascading deletion finalizer on the GroupOwner resource. More in the design document.
Deletion
This process specifies how the ordered deletion is handled, when the GlobalOwner resource is removed.
Deletion
TODO
Opt-out
As the resource is adopted using owner reference to the parent object, when the parent object is removed using foreground or background policy, the adopted resource will be removed as well. To opt out of this behavior, the metacontroller instance should be scaled down, the finalizer metacontroller.io/compositecontroller-<global-owner-name> should be removed from the resource, and then the GlobalOwner resource can be deleted with the orphan deletion policy. Adopted resources will stay untouched, however, the CompositeController and the ClusterRole created by the GlobalOwner resource will stay in the cluster, so the permission scope will not be reduced. Those will have to be cleaned up manually.
Guide
This section contains configuration and installation tips for Global Owner resource controller
Installation
This page describes how to install Global Owner controller using kustomize.
Helm installation
This page describes how to install Global Owner controller using Helm.
Configuration
This page described how a Global Owner resource could be configured to apply ownership on your resources.
Configuration
This page describes how to configure the Global Owner resource.
Helm values
| Parameter | Description | Default | 
|---|---|---|
| namespaceOverride | Namespace override value for installed global owner controller instance | "" | 
| nameOverride | Name override value for installed global owner controller instance | "" | 
| image | Jsonnet image to use for controller functionality | ghcr.io/danil-grigorev/jsonnetd:v0.3.1 | 
| aggregationLabel | ClusterRole aggregation label to provide granular permission scaling to the owned set of resources. Matches the metacontroller ClusterRoleaggregation label value. | rbac.metacontroller.k8s.io/aggregate-to-metacontroller | 
Install Global Owner using Helm
This page describes how to install Global Owner controller using helm.
Building the chart from source code
The chart can be built from repository source:
git clone https://github.com/Danil-Grigorev/global-owner.git
cd global-owner
make release-chart
Installing the chart from package
helm install globalowner out/package/global-owner-v*.tgz --wait
Installing chart from ghcr.io
Charts are published as packages on ghcr.io
To install it from registry:
helm install globalowner -n metacontroller --create-namespace oci://ghcr.io/danil-grigorev/global-owner --version=v0.5.1 --wait
Installation
This page describes how to install Global Owner resource controller using kustomize.
Prerequisites
- Install Metacontroller
Deploy the controller
kubectl apply -k https://github.com/danil-grigorev/global-owner/v1
or locally from the cloned repo:
kubectl apply -k v1
Design
Ordered Deletion
This is a design document for ordered resource deletion using only ownership references and native kubernetes garbage collection.
Ordered Deletion
Resources deletion is processed in the order specified on the GlobalOwner resource.

Blockers
Unfortunately this design does not work, because of an issue in the garbage collector implementation: https://github.com/kubernetes/kubernetes/issues/121113. Until it is fixed, this is just a concept.
Deletion
As the resource is adopted using owner reference to the parent object, when the parent object is removed using foreground or background policy, the adopted resource will be removed as well. To opt out of this behavior, the metacontroller instance should be scaled down, the finalizer metacontroller.io/compositecontroller-<global-owner-name> should be removed from the resource, and then the GlobalOwner resource can be deleted with the orphan deletion policy. Adopted resources will stay untouched, however, the CompositeController and the ClusterRole created by the GlobalOwner resource will stay in the cluster, so the permission scope will not be reduced. Those will have to be cleaned up manually.