Skip to content

Commit b19773e

Browse files
Merge pull request #2497 from 2uasimojo/HIVE-1793/rfe-manifest-patch
RFE: Patch Installer Manifests
2 parents 770cea8 + 377805f commit b19773e

File tree

1 file changed

+203
-0
lines changed

1 file changed

+203
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
# Patching Installer Manifests
2+
3+
- [Overview](#overview)
4+
- [Groundwork](#groundwork)
5+
- [Existing Manifest Patching](#existing-manifest-patching)
6+
- [`ClusterDeploymentCustomization`](#clusterdeploymentcustomization)
7+
- [API](#api)
8+
- [`ClusterDeploymentCustomization.Spec.InstallerManifestPatches`](#clusterdeploymentcustomizationspecinstallermanifestpatches)
9+
- [`ClusterDeployment.Spec.Provisioning.CustomizationRef`](#clusterdeploymentspecprovisioningcustomizationref)
10+
- [`ClusterDeployment.Spec.ClusterPoolRef.CustomizationRef`](#clusterdeploymentspecclusterpoolrefcustomizationref)
11+
- [`ClusterPool.Spec.CustomizationRef`](#clusterpoolspeccustomizationref)
12+
- [`ClusterDeployment.Status`](#clusterdeploymentstatus)
13+
- [`ClusterPool.Status`](#clusterpoolstatus)
14+
- [What About Ignition Configs?](#what-about-ignition-configs)
15+
- [Failure Modes](#failure-modes)
16+
17+
## Overview
18+
Installer accepts spoke configuration via the
19+
[install-config.yaml](https://github.com/openshift/hive/blob/c392ca38fb489267cd7bfbdb3c5a76cc36163686/vendor/github.com/openshift/installer/pkg/types/installconfig.go#L93)
20+
file.
21+
However, some options are only available by editing the OpenShift object manifests generated by
22+
`openshift-install create manifests` prior to feeding them to `openshift-install create cluster`.
23+
Moving forward, installer frequently prefers new features to be enabled via this latter mechanism.
24+
25+
Up to this point, hive has incorporated some piecemeal patching of these manifests at the behest of
26+
specific values passed through via hive APIs.
27+
Example: Extra worker security groups for AWS.
28+
However, this model is awkward, brittle, and not extensible.
29+
What is needed is a mechanism for the hive *user* to specify arbitrary modifications to OpenShift
30+
manifests generated by the installer.
31+
32+
## Groundwork
33+
34+
### Existing Manifest Patching
35+
The code that invokes `openshift-install` lives in installmanager.go.
36+
[This block](https://github.com/openshift/hive/blob/9615c77cd786dd82079a9dacd60b64c882f56327/pkg/installmanager/installmanager.go#L882-L921)
37+
contains logic to:
38+
- Walk the `openshift/` directory (one of two created by `openshift-install create manifests`, the
39+
other being `manifests/`).
40+
- Load yaml files.
41+
- Parse the files as JSON.
42+
- Patch the JSON.
43+
- Convert the JSON back to YAML.
44+
- Write the files back to storage.
45+
46+
### `ClusterDeploymentCustomization`
47+
An earlier feature, [ClusterPool Inventory](clusterpool-inventory.md), invented the concept of
48+
[`ClusterDeploymentCustomization`](https://github.com/openshift/hive/blob/8c79fedae7011e434e8a7ff0fef40994ef92deb2/vendor/github.com/openshift/hive/apis/hive/v1/clusterdeploymentcustomization_types.go),
49+
a hive API used by ClusterPools when specific discrete values (such as reserved IP addresses) must
50+
be injected into the install-config.yaml for each cluster in the pool.
51+
As such, ClusterDeploymentCustomization originally contained only one member,
52+
[`InstallConfigPatches`](https://github.com/openshift/hive/blob/4eaf5fd7858e8def3b2ba3229801090610246243/vendor/github.com/openshift/hive/apis/hive/v1/clusterdeploymentcustomization_types.go#L45),
53+
a list of patches to apply to the install-config.yaml generated by the ClusterPool controller based
54+
on the user-provided template.
55+
Each entry in `InstallConfigPatches` is a
56+
[`PatchEntity`](https://github.com/openshift/hive/blob/4eaf5fd7858e8def3b2ba3229801090610246243/vendor/github.com/openshift/hive/apis/hive/v1/clusterdeploymentcustomization_types.go#L49),
57+
a hive-owned CRD corresponding to an
58+
[RFC 6902 JSON patch](https://datatracker.ietf.org/doc/html/rfc6902).
59+
60+
## API
61+
62+
### `ClusterDeploymentCustomization.Spec.InstallerManifestPatches`
63+
64+
**Example:**
65+
```yaml
66+
apiVersion: v1
67+
kind: ClusterDeploymentCustomization
68+
metadata:
69+
name: foo-cluster-deployment-customization
70+
namespace: my-project
71+
spec:
72+
installerManifestPatches:
73+
# Add custom labels to all master machine manifests
74+
- manifestSelector:
75+
glob: openshift/99_openshift-cluster-api_master-machines-*.yaml
76+
patches:
77+
- op: add
78+
path: /metadata/labels/a-custom-label
79+
value: foo
80+
- op: add
81+
path: /metadata/labels/b-custom-label
82+
value: bar
83+
- ...
84+
...
85+
```
86+
87+
- Build on the existing [groundwork](#groundwork), extending `ClusterDeploymentCustomization` to
88+
expose a new field, `InstallerManifestPatches`.
89+
- Because installer generates multiple manifests, and RFC 6902 syntax can only reference a single
90+
document at a time, we can't directly use `PatchEntity`; we must use an intervening type to
91+
identify which file(s) a patch is to be applied to.
92+
We'll call this `InstallerManifestPatch`.
93+
- Each `InstallerManifestPatch`:
94+
- Identifies one or more files via subfield `ManifestSelector`.
95+
- Lists `PatchEntity`s to apply to files thus identified.
96+
This is the same `PatchEntity` used for `InstallConfigPatches`.
97+
- `ManifestSelector` supports one subfield, `Glob`, which accepts a path matching string as
98+
supported by golang's [filepath.Glob](https://pkg.go.dev/path/filepath#Glob).
99+
- We will execute the glob relative to the working directory in which manifests were generated.
100+
- For security, we will attempt to detect and raise an error if any paths attempt to point outside of the working directory.
101+
- `ManifestSelector` is extensible.
102+
E.g. in the future we may wish to match manifests based on the GVK of the object therein.
103+
104+
### `ClusterDeployment.Spec.Provisioning.CustomizationRef`
105+
106+
**Example:**
107+
```yaml
108+
apiVersion: v1
109+
kind: ClusterDeployment
110+
metadata:
111+
name: foo-cluster-deployment
112+
namespace: my-project
113+
spec:
114+
provisioning:
115+
installConfigSecretRef: ic-secret
116+
customizationRef:
117+
name: foo-cluster-deployment-customization
118+
...
119+
...
120+
```
121+
122+
- Extend `ClusterDeployment`, adding a `CustomizationRef` field at the same level as
123+
`InstallConfigSecretRef`, i.e. under `Spec.Provisioning`.
124+
- `CustomizationRef` is a `LocalObjectReference` to the name of a `ClusterDeploymentCustomization`
125+
in the same namespace as the `ClusterDeployment`.
126+
127+
### `ClusterDeployment.Spec.ClusterPoolRef.CustomizationRef`
128+
129+
This field already existed.
130+
It was used by the ClusterPool Inventory feature to help track which CDC from the pool's inventory
131+
was assigned to this CD.
132+
133+
**We will add support for inventory CDCs to contain `InstallerManifestPatch`.**
134+
135+
That is, in addition to supporting per-pool-CD patching of the install-config, we will now also
136+
support per-pool-CD patching of generated manifests.
137+
138+
**NOTE:** This support will require copying the referenced CDC into the pool CD's (generated)
139+
namespace so it can be applied by the provisioner pod.
140+
(This was not previously necessary, as the install-config patches were used to customize the
141+
install-config Secret in memory before it was created in the target namespace.)
142+
143+
### `ClusterPool.Spec.CustomizationRef`
144+
145+
**Example:**
146+
```yaml
147+
apiVersion: v1
148+
kind: ClusterPool
149+
metadata:
150+
name: foo-cluster-pool
151+
namespace: my-project
152+
spec:
153+
customizationRef:
154+
name: foo-cluster-deployment-customization
155+
...
156+
...
157+
```
158+
159+
- Extend `ClusterPool`, adding a `CustomizationRef` field at the top `Spec` level.
160+
- `CustomizationRef` is a `LocalObjectReference` to the name of a `ClusterDeploymentCustomization`
161+
in the same namespace as the `ClusterPool`.
162+
- In contrast to the inventory CDC, this can (should) be used when the same manifest patches are to
163+
be applied to *all* CDs in the pool.
164+
- This can be used with or without Inventory.
165+
- As with `ClusterDeployment.Spec.ClusterPoolRef.CustomizationRef`, the CDC referenced by this field will be copied into
166+
the pool CD's namespace so it can be applied by the provisioner.
167+
- If both references exist, we will apply `Spec.CustomizationRef` first so that the CD-specific
168+
patches in `ClusterDeployment.Spec.ClusterPoolRef.CustomizationRef` "win" in case of conflicts.
169+
- Note that, unlike inventory CDCs, these pool-wide CDCs will not be reserved/assigned to a single
170+
CD, or even a single ClusterPool (though they will be limited to ClusterPools in the same
171+
namespace).
172+
173+
### `ClusterDeployment.Status`
174+
175+
When a referenced CDC does not exist, we will update the `RequirementsMet` condition accordingly.
176+
177+
For eventual consistency, if the CDC is subsequently created, we will "immediately" clear the
178+
condition (i.e. we need a `Watch()` on CDC that enqueues any ClusterDeployments that reference it).
179+
180+
### `ClusterPool.Status`
181+
182+
When the CDC referenced by `ClusterPool.Spec.CustomizationRef` does not exist, we'll set the
183+
`MissingDependencies` condition.
184+
185+
For eventual consistency, if the CDC is subsequently created, we will "immediately" clear the
186+
condition (i.e. we need a `Watch()` on CDC that enqueues any ClusterPools that reference it).
187+
188+
## What About Ignition Configs?
189+
Between `create manifests` and `create cluster` we `create ignition-configs`.
190+
- This feature will *not* support patching ignition configs.
191+
If we decide to add that support at some point in the future, it will be via a new CDC subfield, e.g. `IgnitionConfigPatches`.
192+
- Because `create ignition-configs` consumes and deletes generated manifests, we must apply manifest patches _between_ these two phases.
193+
194+
## Failure Modes
195+
Other than the status conditions mentioned above, all new failure modes will occur within the
196+
provision pod, causing that pod to log an error message and fail.
197+
198+
Error conditions:
199+
- A `ManifestSelector` matches zero manifests.
200+
- A `ManifestSelector.Glob` resolves to a path outside of the installer's working directory.
201+
- A patch has invalid syntax.
202+
- Applying a patch fails.
203+
- The usual things like files unexpectedly not being readable/writable/parseable (not expected).

0 commit comments

Comments
 (0)