Adding Owner References for Existing Resources

Owner references are automatically injected only during creation of resources. Enabling owner reference injection will not update objects created while owner reference injection is disabled

This guide will demonstrate how to retroactively set owner references for existing resources.

A GET request to the owning resource will provide the necessary data to construct an ownerReference or an annotation.

$ kubectl get memcacheds.cache.example.com -o yaml

Example Response (Abbreviated):

apiVersion: cache.example.com/v1alpha1
kind: Memcached
metadata:
  name: example-memcached
  namespace: default
  uid: 2a94ff2b-84e0-40ce-8b5e-2b7e4d2bc0e2

kubectl edit can be used to update the resources by hand. See below for example ownerReference and annotations.

For objects in the same namespace as the Owner (CR)

Dependent resources within the same namespace as the owning CR are tracked with the ownerReference field.

ownerReference structure:

  • apiVersion: {group}/{version}
  • kind: {kind}
  • name: {metadata.name}
  • uid: {metadata.uid}

Example ownerReference:

metadata:
  ...(snip)
  ownerReferences:
    - apiVersion: cache.example.com/v1alpha1
      kind: Memcached
      name: example-memcached
      uid: ad834522-d9a5-4841-beac-991ff3798c00

For objects which are NOT in the same namespace as the Owner (CR)

An annotation is used instead of an ownerReference if the dependent resource is in a different namespace than the CR, or the dependent resource is a cluster level resource.

annotation structure:

  • operator-sdk/primary-resource: {metadata.namespace}/{metadata.name}
  • operator-sdk/primary-resource-type: {kind}.{group}

NOTE: The {group} can be found by splitting the apiVersion metadata of the CR, into group and version. As an example, apiVersion: cache.example.com/v1alpha1 in the config/samples directory gives us the group cache.example.com.

Example Annotation:

metadata:
  ...(snip)
  annotations:
    operator-sdk/primary-resource: default/example-memcached
    operator-sdk/primary-resource-type: Memcached.cache.example.com

Migration using Ansible assets

If you have many resources to update, it may be easier to use the following Ansible assets, which should be considered an example rather than an officially supported workflow.

To use these assets, create a vars.yml as specified below and copy playbook.yml and each_resource.yml into the same directory. Execute the playbook with:

$ ansible-playbook -i localhost playbook.yml

vars.yml

This file should be created by the user to configure the playbook, and needs to contain:

  • owning_resource
    • apiVersion
    • kind
    • name
    • namespace
  • resources_to_own (list): For each resource, specify:
    • name
    • namespace (if applicable)
    • apiVersion
    • kind
owning_resource:
  apiVersion: cache.example.com/v1alpha1
  kind: Memcached
  name: example-memcached
  namespace: default

resources_to_own:
  - name: example-memcached-memcached
    namespace: default
    apiVersion: apps/v1
    kind: Deployment
  - name: example-memcached
    apiVersion: v1
    kind: Namespace

playbook.yml

This file can be used as-is without user adjustments.

- hosts: localhost

  tasks:
    - name: Import user variables
      include_vars: vars.yml
    - name: Retrieve owning resource
      community.kubernetes.k8s_info:
        api_version: "{{ owning_resource.apiVersion }}"
        kind: "{{ owning_resource.kind }}"
        name: "{{ owning_resource.name }}"
        namespace: "{{ owning_resource.namespace }}"
      register: extra_owner_data

    - name: Ensure resources are owned
      include_tasks: each_resource.yml
      loop: "{{ resources_to_own }}"
      vars:
        to_be_owned: '{{ q("community.kubernetes.k8s",
          api_version=item.apiVersion,
          kind=item.kind,
          resource_name=item.name,
          namespace=item.namespace
        ).0 }}'
        owner_reference:
          apiVersion: "{{ owning_resource.apiVersion }}"
          kind: "{{ owning_resource.kind }}"
          name: "{{ owning_resource.name }}"
          uid: "{{ extra_owner_data.resources[0].metadata.uid }}"

each_resource.yml

This file can be used as-is without user adjustments.

- name: Patch resource with owner reference
  when:
    - to_be_owned.metadata.namespace is defined
    - to_be_owned.metadata.namespace == owning_resource.namespace
    - (to_be_owned.metadata.ownerReferences is not defined) or
      (owner_reference not in to_be_owned.metadata.ownerReferences)
  community.kubernetes.k8s:
    state: present
    resource_definition:
      apiVersion: "{{ to_be_owned.apiVersion }}"
      kind: "{{ to_be_owned.kind }}"
      metadata:
        name: "{{ to_be_owned.metadata.name }}"
        namespace: "{{ to_be_owned.metadata.namespace }}"
        ownerReferences: "{{ (to_be_owned.metadata.ownerReferences | default([])) + [owner_reference] }}"

- name: Patch resource with owner annotation
  when: to_be_owned.namespace is not defined or to_be_owned.namespace != owning_resource.namespace
  community.kubernetes.k8s:
    state: present
    resource_definition:
      apiVersion: "{{ to_be_owned.apiVersion }}"
      kind: "{{ to_be_owned.kind }}"
      metadata:
        name: "{{ to_be_owned.metadata.name }}"
        namespace: "{{ to_be_owned.metadata.namespace | default(omit)}}"
        annotations:
          operator-sdk/primary-resource: "{{ owning_resource.namespace }}/{{ owning_resource.name }}"
          operator-sdk/primary-resource-type: "{{ owning_resource.kind }}.{{ owning_resource.apiVersion.split('/')[0] }}"