Skip to content

MutatingAdmissionPolicy KEP: Drop dependency on SSA unsetting fields #5182

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
108 changes: 4 additions & 104 deletions keps/sig-api-machinery/3962-mutating-admission-policies/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -465,105 +465,7 @@ Optimizations like caching or lazy sub-schema resolution can be candidates of be
#### Unsetting values

Since there is no field manager used for the merge, the server side apply merge
algorithm will only add and replace values. This is because server side apply is
designed to only unset values that were previously owned by the field manager
but excluded from the apply configuration.

To work around this limitation, we will take advantage of CEL's optional type
feature to make it possible to express that a value should be unset. For example:

```cel
Object{
spec: Object.spec{
?fieldToRemoveIfPresent: optional.none()
}
}
```
The policy declaratively describes that, in the final object, if fieldToRemoveIfPresent presents it should be removed or no-op otherwise.

The optional.none() function creates a CEL object defined as optional(T) where T is the type of the receiving field.
In conjunction with standard CEL macros, simple expressions can perform more complicated and precise operations.

For example, to remove the env of a sidecar container, filter by its name.

```yaml
mutations:
- patchType: "ApplyConfiguration"
expression: >
Object{
spec: Object.spec{
containers: object.spec.containers{
object.spec.containers.filter(c, c.name == "sidecar")
.map(c, Object.spec.containers.item{
?env: optional.none()
})
}
}
}

```

We will track which fields are unset in this way and remove them after the
server side apply merge algorithm is run.

This solves the vast majority of value removal needs. Specifically:

| Schema type | Merge type | Example of how to unset a value |
|-------------| -----------|----------------------------------------------------------------------------------|
| struct | atomic | ```Object{ spec: Object.spec{ structField: {?fieldToRemove: optional.none()}}}``` |
| struct | granular | `?fieldToRemove: optional.none()` |
| map | atomic | `mapField: object.spec.mapField.filter(k, k != "keyToRemove")` |
| map | granular | `mapField: {?"keyToRemove": optional.none()}` |
| list | atomic | Use `JSONPatch` |
| list | set | `setField: object.spec.setField.filter(e, e != "itemToRemove")` |
| list | map | See below |
| list | granular | See below |


List with "map" merge type:
- Filter `objects.filter(<list>, <keys-to-remove>)` could be used for the deletion
- For associatedList with multiple keys like example above, a directive field added could be used to indicate the deletion.
```yaml
mutations:
- patchType: "ApplyConfiguration"
expression: >
Object{
spec: Object.spec{
assocListField: [Object.spec.assocListField{
keyField1: "key1",
keyField2: "key2",
_: optional.none()
}]
}
}

```

For examples of removing item from List with Map filtered by a subfield:
```yaml
mutations:
- patchType: "ApplyConfiguration"
expression: >
Object{
spec: Object.spec{
containers: object.spec.containers.filter(c, c.envvar != "remove-this-container")
}
}
}

```

For granular list removal, a use case would be removing an item with a sub field named `remove-this-item`.
```yaml
mutations:
- patchType: "ApplyConfiguration"
expression: >
Object{
spec: Object.spec{
granularList: object.spec.granularList.filter(c, c.subField != "remove-this-item")
}
}
```
algorithm will only add and replace values. To unset values, JSON Patch mutations must be used.

##### Safety

Expand Down Expand Up @@ -840,11 +742,9 @@ Object{
#### Use case: Remove an annotation

```cel
Object{
metadata: Object.metadata{
annotations:
?"annotation-to-unset": optional.none()
}
JSONPatch{
op: "remove",
path: "/metadata/annotations/annotation-to-unset"
}
```

Expand Down