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: Enable purging resources in kubectl apply #29551

102 changes: 102 additions & 0 deletions docs/proposals/kubectl-apply-purge-from-namespace.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Enable purging resources in `kubectl apply`

## Motivation

See [#19805](https://github.com/kubernetes/kubernetes/issues/19805).

Currently `kubectl apply` will create any new resources that exist in the resource file(s) it is supplied with, but it has no way of deleting anything that has been removed from such files.

This makes it difficult to use `kubectl apply` to update reality to match the declarative definitions of resources when that definition evolves over time, since changes can include deletions.
Copy link
Member

Choose a reason for hiding this comment

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

We have refereed to this process as "configuration reconciliation" in the past. #1702


The primary driver for this use-case is to be able to manage cluster add-ons using `kubectl apply`.

## Proposed Solution

In a "simplest possible idea first" approach, this proposal suggests that we reuse namespaces as a de-facto way of grouping resources for the purpose of _purging_ (removing) resources which no longer exist.
Copy link
Member

Choose a reason for hiding this comment

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

Prune seems like a more appropriate than purge for this feature.

Copy link
Member

Choose a reason for hiding this comment

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

I agree with @mikedanese


Most add-ons reside in `kube-system` namespace, therefore for add-ons, an implementation based on namespaces would be reasonable.
The benefit of this is that no new abstraction (such as a new way of grouping resources) is required for this use-case.

Therefore, the proposal is to introduce an optional `--purge-from-namespace` flag to `kubectl apply`, which will allow a user to automatically purge resources from the given namespace, if they haven't been explicitly declared in files that are arguments to the same `kubectl apply` invocation.

All resources referred to must be explicitly labelled with a non-default namespace in order for the `--purge-from-namespace` argument to succeed.
Copy link
Contributor

Choose a reason for hiding this comment

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

s/a non-default/one non-default/


This flag will require `--namespace` not to be set.
Copy link
Contributor

Choose a reason for hiding this comment

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

I am not a big fan of hardcoding namespaces in manifests. This makes workloads less portable. E.g. in continuous deployment environments I might need to deploy/apply a workloads in a different namespace, depending on prod/testing/staging.

Hence, my immediate thought was to enforce --namespace and forbid any hard-coded namespace in the manifests. This is kind of the opposite of the proposed solution.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks, I think this is quite reasonable. The intention was that namespace must be explicit to ensure good user experience of this feature. There is wasn't a particularly big reason for doing that way, it's just that we had to pick something. I'm happy to change this over, unless @lukemarsden has something to add.

Copy link
Member

Choose a reason for hiding this comment

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

I've changed this.

All referenced resources must be explicitly defined as being in the same namespace, set in each of the resource definitions.

The goal here is to avoid a negative user experience where the user accidentally deletes all the resources in some other namespace just because they accidentally refer to (e.g.) a single resource in some other namespace.

## Managing Add-ons

Let's first consider the add-ons use case.

There exists a `/etc/kubernetes/addons` directory with some number of files. All resources declared in those files belong to the same namespace (`kube-system`), which is set explicitly in each of the resources.

When `kubectl apply --purge-from-namespace -f /etc/kubernetes/addons` runs for the first time all the resources will be created.
Copy link
Member

Choose a reason for hiding this comment

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

This section should be updated when we agree on the syntax.


Eventually some new files appear in `/etc/kubernetes/addons` and some are modified.

Subsequent invocations of `kubectl apply --purge-from-namespace -f /etc/kubernetes/addons` result in new resources being created and modified resources being updated.

When user deletes some of the files or resources within files from `/etc/kubernetes/addons`, `kubectl apply --purge-from-namespace` would ensure the resources that were referenced in the deleted files are deleted, based on the assumption that `/etc/kubernetes/addons` represents all resources that are supposed to be present in `kube-system` namespace.
Copy link
Member

Choose a reason for hiding this comment

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

There are other resources in kube-system.



## Managing Any Other Application
Copy link
Member

Choose a reason for hiding this comment

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

This section needs to be updated with the label-selector proposal.


It should be possible to apply the same approach to any other applications, with the assumption that single application occupies a namespace of its own.

The usage may look like this:

```
kubectl apply --purge-from-namespace -f https://example.com/myapp1.json
```

Given resources in `myapp1.json` set their namespaces explicitly, resource that are no longer in this file would be purged, if no longer defined.

```
kubectl apply --purge-from-namespace -f https://example.com/myapp-part1.json -f https://example.com/myapp-part2.json
```

If `myapp1.json` and `myapp2.json` are in the same namespaces and it is explicitly defined, removing either of the URLs from the invocations arguments will result in all the resources that URL has defined to be purged.
Copy link
Member

Choose a reason for hiding this comment

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

insert -part here, to reflect what's above


## Flag Discovery User Experience

As stated above, the `--purge-from-namespace` flag shouldn't be enabled by default, however it should be discoverable.

So, if a `kubectl apply` command is run without `--purge-from-namespace`, and the referenced files are all in the same namespace, then the command could output something like:
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure how keen I am to do this. It will make apply significantly more expensive (to discover all available resources within the namespace) and make the user experience significantly different than other forms of updates.

--dry-run, to simulate what changes would be done, would be useful.

Mentioning this in the short help snippet for kubectl apply would be useful.

A tutorial that shows how to manage resources using apply would be useful.


```
$ kubectl apply -f resources.yaml
Updating ServiceA...
Copy link
Member

Choose a reason for hiding this comment

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

Currently the output doesn't even reflect whether something is being created or updated, so we will need to investigate that...

Copy link
Member

Choose a reason for hiding this comment

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

That would be useful.

The precise syntax should follow the output convention of other commands, including both the resource type and resource name in a form that could be copy+pasted to other commands verbatim, and should support -o name for scripting use.

Updating ServiceB...
Updating DeploymentA...
The following resources in namespace `foo` are not referenced in the files you provided:

DeploymentB
ServiceC

You can automatically delete them, if you wish, using:
Copy link
Member

Choose a reason for hiding this comment

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

We could suggest that on first use of apply to perform an update.

See also #19736 (suggest next commands), #10693 (more/better preferences)


kubectl apply --purge-from-namespace -f resources.yaml

$ kubectl apply --purge-from-namespace -f resources.yaml
Updating ServiceA...
Updating ServiceB...
Updating DeploymentA...
Deleting DeploymentB...
Deleting ServiceC...
Done!
```

If there are > 5 resources which match, we could just list the first 5.
Copy link
Member

Choose a reason for hiding this comment

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

Kubectl displays all resources modified.

Copy link
Member

Choose a reason for hiding this comment

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

It doesn't cover everything, but take a look at kubectl conventions:
https://github.com/kubernetes/kubernetes/blob/master/docs/devel/kubectl-conventions.md


If no namespace, or conflicting namespaces are provided in the resource file(s), this output would not be shown.

# Alternatives Considered
Copy link
Member

Choose a reason for hiding this comment

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

I would just remove these.


A new Kubernetes API-level concept "Versioned ApplySets" could record all the `kubectl apply` commands that were ever executed against a cluster.
This would allow deletions to be automatically detected and applied between invocations of `kubectl apply` without the onus of having to put resources into namespaces.

This adds complexity, since a new Kubernetes API object type would need to be invented.

It's not obvious how to make this concept work correctly in the case where one user runs `kubectl apply X` and later `kubectl apply X'` and a different user runs `kubectl apply Y` where `X'` is intended as an update to `X`
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is that a problem? The same issue exists already with a simple kubectl apply command if object names are re-used. If different users have different baselines for their changes, this asks for conflicts.

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 @lukemarsden added this, I'm not sure what exactly was he trying to say. As this is a kind of non-critical background information, I'd vote for removing this paragraph.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was in context of the alternative considered. Feel free to kill it, it was only ever a half-thought anyway.