Skip to content
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

proposal: Change application identifier to allow app names longer than 63 chars #6425

Merged
merged 6 commits into from
Sep 13, 2021
Merged
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
273 changes: 273 additions & 0 deletions docs/proposals/application-name-identifier.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
---
title: Change the way application resources are identified
authors:
- "@jannfis"
sponsors:
- TBD
reviewers:
- TBD
approvers:
- TBD

creation-date: 2021-06-07
last-updated: 2021-06-07
---

# Change the way application resources are identified

This is a proposal to introduce the tracking method settings that allows using
an annotation as the application identifier instead of the application instance label.
This will allow application names longer than 63 characters and solve issues caused by
copying `app.kubernetes.io/instance` label. As an additional goal, we propose to introduce an
installation ID that will allow multiple Argo CD instances to manage resources
on the same cluster.


## Summary

Argo CD identifies resources it manages by setting the _application instance
label_ to the name of the managing `Application` on all resources that are
managed (i.e. reconciled from Git). The default label used is the well-known
label `app.kubernetes.io/instance`.

This proposal suggests to introduce the `trackingMethod` setting that allows
controlling how applicaton resources are identified and allows switching to
using the annotation instead of `app.kubernetes.io/instance` label.

## Motivation

The main motivation behind this change is to solve the following known issues:

* The Kubernetes label value cannot be longer than 63 characters. In large scale
installations, in order to build up an easy to understand and
well-formed naming schemes for applications managed by Argo CD, people often
hit the 63 character limit and need to define the naming scheme around this
unnecessary limit.

* Popular off-the-shelf Helm charts often add the `app.kubernetes.io/instance` label
to the generated resource manifests. This label confuses Argo CD and makes it think the
resource is managed by the application.

* Kubernetes operators often create additional resources without creating owner reference
and copy the `app.kubernetes.io/instance` label from the application resource. This is
also confusing Argo CD and makes it think the resource is managed by the application.

An additional motivation - while we're at touching at application instance
label - is to improve the way how multiple Argo CD instances could manage
applications on the same cluster, without requiring the user to actually
perform instance specific configuration.

### Goals

* Allow application names of more than 63 characters

* Prevent confusion caused by copied/generated `app.kubernetes.io/instance` label

* Keep having a human-readable way to identify resources that belong to a
given Argo CD application

* As a stretch-goal, allow multiple Argo CD instances to manage resources on
the same cluster without the need for configuring application instance label
key (usually `app.kubernetes.io/instance`)

### Non-Goals

* Change the default name of the application instance label

## Proposal

We propose introducing a new setting `trackingMethod` that allows to control
how application resources are identified. The `trackingMethod` setting takes
one of the following values:

* `label` (default) - Argo CD keep using the `app.kubernetes.io/instance` label.
* `annotation+label` - Argo CD keep adding `app.kubernetes.io/instance` but only
for informational purposes: label is not used for tracking, value is truncated if
longer than 63 characters. The `app.kubernetes.io/instance` annotation is used
to track application resources.
* `annotation` - Argo CD uses the `app.kubernetes.io/instance` annotation to track
application resources.

The `app.kubernetes.io/instance` attribute values includes the application name,
resources identifier it is applied to, and optionally the Argo CD installation ID:

The application name allows to identify the application that manages the resource. The
resource identifier prevents confusion if an operation copies the
`app.kubernetes.io/instance` annotation to another resource. Finally optional
installation ID allows separate two Argo CD instances that manages resources in the same cluster.

The `trackingMethod` setting should be available at the system level and the application level to
allow the smooth transition from the old `app.kubernetes.io/instance` label to the new tracking method.
Using the app leverl settings users will be able to first switch applications one by one to the new tracking method
and prepare for the migration. Next system level setting can be changed to `annotation` or `annotation+label`
and not-migrated applications can be configured to use `labels` using application level setting.


### Use cases

Add a list of detailed use cases this enhancement intends to take care of.

#### Use case 1: Allow for more than 63 characters in application name

As a user, I would like to be able to give my applications names with arbitrary
length, because I want to include identifiers like target regions and possibly
availability zones, the environment and possibly other identifiers (e.g. a team
name) in the application names. The current restriction of 63 characters is not
sufficient for my naming requirements.

#### Use case 2: Allow for retrieving all resources using Kubernetes

As an administrator, I want to enable my users to use more than 63 characters
in their application names, but I still want to be able to retrieve all of the
resources managed by that particular application using Kubernetes mechanisms,
e.g. a label selector as in the following example:

```
kubectl get deployments -l app.kubernetes.io/instance=<application> --all-namespaces
```

#### Use case 3: Multiple Argo CD instances managing apps on same cluster

I also want to be able to see which application and Argo CD instance is the
one in charge of a given resource.

### Implementation Details/Notes/Constraints [optional]

#### Include resource identifies in the `app.kubernetes.io/instance` annotation

The `app.kubernetes.io/instance` annotation might be accidently added or copied
same as label. To prevent Argo CD confusion the annotation value should include
the identifier of the resource annotation was applied to. The resource identifier
includes the group, kind, namespace and name of the resource. It is proposed to use `;`
to separate identifier from the application name.

```yaml
annotations:
app.kubernetes.io/instance: <application-name>;<group>/<kind>/<namespace>/<name>
```

Example:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
namespace: default
annotations:
app.kubernetes.io/instance: my-application;apps/Deployment/default/my-deployment
```

#### Allow multiple Argo CD instances manage applications on same cluster

As of today, to allow two or more Argo CD instances with a similar set of
permissions (e.g. cluster-wide read access to resources) manage applications
on the same cluster, users would have to configure the _application instance
label key_ in the Argo CD configuration to a unique value. Otherwise, if an
application with the same name exists in two different Argo CD installations,
both would claim ownership of the resources of that application.

We do see the need for preventing such scenarios out-of-the-box in Argo CD.
For this, we do suggest the introduction of an _installation ID_ in the
form of a standard _GUID_.

This GUID would be generated once by Argo CD upon startup, and is persisted in
the Argo CD configuration, e.g. by storing it as `installationID` in the
`argocd-cm` ConfigMap. The GUID of the installation would need to be encoded
in some way in the resources managed by that Argo CD instance.

We suggest using a dedicated annotation to store the GUID and modify Argo CD so that it matches _both_, the app
instance key and the GUID to determine whether a resource is managed by
this Argo CD instance. Given above mentioned GUID, this may look like the
following on a resource:

```yaml
apiVersion: v1
Kind: Secret
metadata:
name: some-secret
namespace: some-namespace
annotations:
app.kubernetes.io/instance: my-application;/Secret/some-namespace/some-secret
argo-cd.argoproj.io/installation-id: 61199294-412c-4e78-a237-3ebba6784fcd
```

The user should be able to opt-out of this feature by setting the `installationID` to an empty string.

### Security Considerations

We think this change will not have a direct impact on the security of Argo CD
or the applications it manages.

### Risks and Mitigations

The proposal assumes that user can keep adding `app.kubernetes.io/instance` label
to be able to retrieve resources using `kubectl get -l app.kubernetes.io/instance=<application>` command.
However, Argo CD is going to truncate the value of the label if it is longer than 63 characters. There is
a small possibility that there are several applications with the same first 63 characters in the name. This
should be clearly stated in documentation.

### Upgrade / Downgrade Strategy

Upgrading to a version that implements this proposal should be seamless, as
previously injected labels will not be removed and additional annotations will
be applied to the resource. E.g. consider following resource in Git, that will
be synced as part of an application named `some-application`. In Git, the
resource looks like follows:

```yaml
apiVersion: v1
Kind: Secret
metadata:
name: some-secret
namespace: some-namespace
```

When synced with the current incarnation of Argo CD, Argo CD would inject the
application instance label and once the resource is applied in the cluster, it
would look like follows:

```yaml
apiVersion: v1
Kind: Secret
metadata:
name: some-secret
namespace: some-namespace
labels:
app.kubernetes.io/instance: some-application
```

Once Argo CD is updated to a version implementing this proposal, the resource
would be rewritten to look like the following:

```yaml
apiVersion: v1
Kind: Secret
metadata:
name: some-secret
namespace: some-namespace
labels:
app.kubernetes.io/instance: some-application
annotations:
app.kubernetes.io/instance: my-application;/Secret/some-namespace/some-secret
argo-cd.argoproj.io/installation-id: 61199294-412c-4e78-a237-3ebba6784fcd
```

On a rollback to a previous Argo CD version, this change would be reverted
and the resource would look like the first shown example above.

Copy link
Member

Choose a reason for hiding this comment

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

How would Argo CD know whether a resource's app.kubernetes.io/instance was applied using the old system or the new system (the one described in this proposal)?

For example:

  1. A cluster is running with the old version of Argo CD
  2. All the application resources are labelled using the old system
  3. Argo CD is upgraded
  4. The new Argo CD looks at all the existing app.kubernetes.io/instance fields of the existing resources, discovers that they don't match any expected hash (and thus don't belong to any Application), and ignores them (recreating new Application resources).

I think we need the ability to look at the app.kubernetes.io/instance field of a resource and know which system it was created with.

One option we could use is look at the app.kubernetes.io/instance value and determine if it is a 32 character hexadecimal value, but this is not foolproof (users may be using Applications with names that are 32-byte hexadecimal values).

Copy link
Member Author

Choose a reason for hiding this comment

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

That's a good point you raise there.

I haven't yet verified, but I think it would be similar to the behavior if you rename an application, e.g. by deleting an Application and re-creating it with a different name, but same parameters (source + destination).

Argo CD would just go and overwrite what's in app.kubernetes.io/instance - at least as long as another application with the previous name does not exist, in which case Argo CD would issue a Shared Resource Warning and prevent the overwrite.

Copy link
Member

Choose a reason for hiding this comment

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

I think if an Argo CD instance encounters a resource with a app.kubernetes.io/instance value that it doesn't recognize (eg it does not correspond to a hash of a known application name+install id value), then Argo CD must assume that that resource is owned by another Argo CD instance (the goal of 'allow multiple Argo CD instances to manage resources on the same cluster').

So, presuming that Argo CD encounters a resource it thinks is owned by someone else, and it has the same name/namespace as a resouce managed by an Application managed by the current Argo CD instance, do we:

  1. Take ownership of it with the current Argo CD instance
  2. Leave it alone

Probably 2), and report an error ('error: Argo CD Application would overwrite a resource that may be owned by another Argo CD instance').

It might be worthwhile to document the algorithm that Argo CD will use to move existing resources from the old style to the new style, in the upgrade scenario.

I'm guessing it would be:

  1. If we encounter a resource that contains a app.kubernetes.io/instance that is not a 32/64 character hexadecimal value, AND it matches the name field of an existing Argo CD Application, then convert it to the new system (ensure the app.kubernetes.io/instance field is updated)
    • As you said, this might already be handled by the rename-style behaviour.
    • Another option would be to delete the resource, and hope that Argo CD recreates it.
  2. If we encounter a resource that contains a app.kubernetes.io/instance that is a 32/64 character hexadecimal value, leave it alone. (Assume it's a valid instance id from another Argo CD instance).
    • Unless it has the same Kubernetes resource name as a resource that this Argo CD instance is trying to create, in which case report the above error ('error: would overwrite a resource that may be owned...')
  3. Else, either delete or leave it alone (orphan it).
    (in very rare cases this might orphan or delete old resources, but it's probably fine.)

## Drawbacks

We do see some drawbacks to this implementation:

* This change would trigger a re-sync of each and every managed resource, which
may result in unexpected heavy load on Argo CD and the cluster at upgrade
time. The workaround is an ability to opt-out of this as a default and enable it
on application basis.

## Alternatives

* Enabling application names longer than 63 characters could also be done
by using the hashed value of the application name and additional metadata as a label.
The disadvantage of this approach is that hash value is not human friendly. In particular,
it is difficult to retrieve application manifests using `kubectl get -l app.kubernetes.io/instance=<application>`.