Skip to content

doc: Update existing resources with owner refs #2013

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

Merged
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
5 changes: 5 additions & 0 deletions doc/ansible/dev/advanced_options.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,12 @@ The ansible runner will keep information about the ansible run in the container.
You may want to manage what your operator watches and the owner references. This means that your operator will need to understand how to clean up after itself when your CR is deleted. To disable these features you will need to edit your `build/Dockerfile` to include the line below.

**NOTE**: That if you use this feature there will be a warning that dependent watches is turned off but there will be no error.
**WARNING**: Once a CR is deployed without owner reference injection, there is no automatic way to add those references.

```
ENTRYPOINT ["/usr/local/bin/entrypoint", "--inject-owner-ref=false"]
```

If you have created resources without owner reference injection, it is
possible to manually to update resources following [this
guide.](./retroactively-owned-resources.md)
2 changes: 1 addition & 1 deletion doc/ansible/dev/dependent_watches.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ This is an example of a watches file with the `watchDependentResources` field se
manageStatus: False
watchDependentResources: True

```
```
194 changes: 194 additions & 0 deletions doc/ansible/dev/retroactively-owned-resources.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
# 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](./advanced_options.md#turning-off-dependent-watches-and-owner-reference-injection)

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):**

```yaml
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 (CRD)

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:**

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

@camilamacedo86 camilamacedo86 Oct 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @fabianvf,

Is required to add the UID? If I would like to do it for a pod for example and the pod be re-created automatically by the API platform which is the default behaviour would not it assume a new UID? So, should not we be able to do that without informing a UID?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the UID field is required to construct an ownerReference. This is to correctly garbage collect if the owner is deleted and then re-created

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WDYT about we add a note with this info @asmacdo?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me, the delete/recreate edge case seems like an implementation detail which we should leave out to keep the guide simple. I'm happy to add it if it would be helpful though.

```

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WDYT?

Suggested change
**NOTE**: The UID field is required to construct an ownerReference. Then, this is to correctly garbage collect if the owner is deleted and then re-created. So, the owner reference is not lost if the resource needs to be re-created by the API.

Is my understand correctly @fabianvf ?

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

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,
[this apiVersion field](https://github.com/operator-framework/operator-sdk-samples/blob/master/ansible/memcached-operator/deploy/crds/cache_v1alpha1_memcached_cr.yaml#L1)
gives us the group `cache.example.com`.

**Example Annotation:**

```yaml
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:

``` bash
$ 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

```yaml
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.

```yaml
- hosts: localhost

tasks:
- name: Import user variables
include_vars: vars.yml
- name: Retrieve owning resource
k8s_facts:
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("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.

``` yaml
- 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)
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
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] }}"
```