diff --git a/openapi/generated_openapi/zz_generated.openapi.go b/openapi/generated_openapi/zz_generated.openapi.go index 967c191d6be..662a6424f9d 100644 --- a/openapi/generated_openapi/zz_generated.openapi.go +++ b/openapi/generated_openapi/zz_generated.openapi.go @@ -1227,6 +1227,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/openshift/api/operator/v1alpha1.ClusterAPIInstallerComponent": schema_openshift_api_operator_v1alpha1_ClusterAPIInstallerComponent(ref), "github.com/openshift/api/operator/v1alpha1.ClusterAPIInstallerComponentImage": schema_openshift_api_operator_v1alpha1_ClusterAPIInstallerComponentImage(ref), "github.com/openshift/api/operator/v1alpha1.ClusterAPIInstallerRevision": schema_openshift_api_operator_v1alpha1_ClusterAPIInstallerRevision(ref), + "github.com/openshift/api/operator/v1alpha1.ClusterAPIInstallerRevisionManifestSubstitution": schema_openshift_api_operator_v1alpha1_ClusterAPIInstallerRevisionManifestSubstitution(ref), "github.com/openshift/api/operator/v1alpha1.ClusterAPIList": schema_openshift_api_operator_v1alpha1_ClusterAPIList(ref), "github.com/openshift/api/operator/v1alpha1.ClusterAPISpec": schema_openshift_api_operator_v1alpha1_ClusterAPISpec(ref), "github.com/openshift/api/operator/v1alpha1.ClusterAPIStatus": schema_openshift_api_operator_v1alpha1_ClusterAPIStatus(ref), @@ -62669,6 +62670,13 @@ func schema_openshift_api_operator_v1alpha1_ClusterAPIInstallerComponent(ref com Description: "ClusterAPIInstallerComponent defines a component which will be installed by this revision.", Type: []string{"object"}, Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "name is the human-readable name of the component. It is only used to make api revisions easier to understand. It must consist of alphanumeric characters, or '-'.", + Type: []string{"string"}, + Format: "", + }, + }, "type": { SchemaProps: spec.SchemaProps{ Description: "type is the source type of the component. The only valid value is Image. When set to Image, the image field must be set and will define an image source for the component.\n\nPossible enum values:\n - `\"Image\"` is an image source for a component.", @@ -62694,6 +62702,7 @@ func schema_openshift_api_operator_v1alpha1_ClusterAPIInstallerComponent(ref com "discriminator": "type", "fields-to-discriminateBy": map[string]interface{}{ "image": "Image", + "name": "Name", }, }, }, @@ -62780,6 +62789,25 @@ func schema_openshift_api_operator_v1alpha1_ClusterAPIInstallerRevision(ref comm }, }, }, + "manifestSubstitutions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "manifestSubstitutions is a list of envsubst style substitutions which will be applied to manifests in the revision during rendering.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/openshift/api/operator/v1alpha1.ClusterAPIInstallerRevisionManifestSubstitution"), + }, + }, + }, + }, + }, "components": { VendorExtensible: spec.VendorExtensible{ Extensions: spec.Extensions{ @@ -62809,7 +62837,35 @@ func schema_openshift_api_operator_v1alpha1_ClusterAPIInstallerRevision(ref comm }, }, Dependencies: []string{ - "github.com/openshift/api/operator/v1alpha1.ClusterAPIInstallerComponent"}, + "github.com/openshift/api/operator/v1alpha1.ClusterAPIInstallerComponent", "github.com/openshift/api/operator/v1alpha1.ClusterAPIInstallerRevisionManifestSubstitution"}, + } +} + +func schema_openshift_api_operator_v1alpha1_ClusterAPIInstallerRevisionManifestSubstitution(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ClusterAPIInstallerRevisionManifestSubstitution defines an envsubst style substitution which will be applied to manifests in a revision during rendering.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "key": { + SchemaProps: spec.SchemaProps{ + Description: "key is the name of the envsubst variable to substitute. It must be a valid envsubst variable name, consisting of letters, digits, and underscores, and must start with a letter or underscore. The key must not be empty, and must not exceed 255 characters.", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Description: "value is the value to substitute for the envsubst variable. It may be empty, in which case the variable will be substituted with an empty string. The value must not exceed 255 characters.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"key", "value"}, + }, + }, } } @@ -62937,6 +62993,13 @@ func schema_openshift_api_operator_v1alpha1_ClusterAPIStatus(ref common.Referenc }, }, }, + "observedRevisionGeneration": { + SchemaProps: spec.SchemaProps{ + Description: "observedRevisionGeneration is the generation of the ClusterAPI object that was last observed by the revision controller. If specified it must be greater than or equal to 1. It may not decrease or be unset once set.", + Type: []string{"integer"}, + Format: "int64", + }, + }, }, Required: []string{"desiredRevision", "revisions"}, }, diff --git a/operator/v1alpha1/tests/clusterapis.operator.openshift.io/ClusterAPIMachineManagement.yaml b/operator/v1alpha1/tests/clusterapis.operator.openshift.io/ClusterAPIMachineManagement.yaml index 7b279a6322d..15da7e66436 100644 --- a/operator/v1alpha1/tests/clusterapis.operator.openshift.io/ClusterAPIMachineManagement.yaml +++ b/operator/v1alpha1/tests/clusterapis.operator.openshift.io/ClusterAPIMachineManagement.yaml @@ -649,3 +649,642 @@ tests: image: ref: quay.io/openshift/cluster-api@sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb profile: default + + # ManifestSubstitution key validation tests + + - name: Should accept valid envsubst key in manifestSubstitutions + initial: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + updated: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + manifestSubstitutions: + - key: EXP_BOOTSTRAP_FORMAT_IGNITION + value: "true" + components: + - type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + expected: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + manifestSubstitutions: + - key: EXP_BOOTSTRAP_FORMAT_IGNITION + value: "true" + components: + - type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + + - name: Should accept envsubst key starting with underscore + initial: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + updated: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + manifestSubstitutions: + - key: _FOO + value: bar + components: + - type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + expected: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + manifestSubstitutions: + - key: _FOO + value: bar + components: + - type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + + - name: Should accept lowercase envsubst key + initial: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + updated: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + manifestSubstitutions: + - key: my_var + value: something + components: + - type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + expected: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + manifestSubstitutions: + - key: my_var + value: something + components: + - type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + + - name: Should reject envsubst key starting with digit + initial: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + updated: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + manifestSubstitutions: + - key: 1BAD + value: something + components: + - type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + expectedStatusError: "key must start with a letter or underscore" + + - name: Should reject envsubst key with hyphens + initial: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + updated: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + manifestSubstitutions: + - key: my-key + value: something + components: + - type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + expectedStatusError: "key must start with a letter or underscore" + + - name: Should accept empty value in manifestSubstitutions + initial: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + updated: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + manifestSubstitutions: + - key: MY_VAR + value: "" + components: + - type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + expected: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + manifestSubstitutions: + - key: MY_VAR + value: "" + components: + - type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + + # Component name validation tests + + - name: Should accept component name with hyphens + initial: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + updated: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + components: + - name: core-provider + type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + expected: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + components: + - name: core-provider + type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + + - name: Should accept component name with uppercase letters + initial: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + updated: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + components: + - name: CoreProvider + type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + expected: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + components: + - name: CoreProvider + type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + + - name: Should reject component name with underscores + initial: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + updated: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + components: + - name: core_provider + type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + expectedStatusError: "name must consist of alphanumeric characters or '-'" + + - name: Should reject component name with dots + initial: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + updated: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + components: + - name: core.provider + type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + expectedStatusError: "name must consist of alphanumeric characters or '-'" + + # Component union discriminator tests + + - name: Should reject component with type Image but no image field + initial: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + updated: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + components: + - type: Image + expectedStatusError: "image is required when type is Image, and forbidden otherwise" + + # observedRevisionGeneration validation tests + + - name: Should reject observedGeneration less than 1 + initial: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + updated: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + observedRevisionGeneration: 0 + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + components: + - type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + expectedStatusError: "should be greater than or equal to 1" + + - name: Should accept increasing observedGeneration + initial: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + observedRevisionGeneration: 1 + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + components: + - type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + updated: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + observedRevisionGeneration: 2 + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + components: + - type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + expected: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + observedRevisionGeneration: 2 + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + components: + - type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + + - name: Should accept keeping observedGeneration the same + initial: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + observedRevisionGeneration: 1 + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + components: + - type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + updated: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + observedRevisionGeneration: 1 + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + components: + - type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + expected: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + observedRevisionGeneration: 1 + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + components: + - type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + + - name: Should reject decreasing observedGeneration + initial: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + observedRevisionGeneration: 5 + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + components: + - type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + updated: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + observedRevisionGeneration: 3 + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + components: + - type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + expectedStatusError: "observedRevisionGeneration may not decrease" + + - name: Should reject unsetting observedGeneration once set + initial: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + observedRevisionGeneration: 1 + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + components: + - type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + updated: | + apiVersion: operator.openshift.io/v1alpha1 + kind: ClusterAPI + metadata: + name: cluster + spec: {} + status: + desiredRevision: rev-1 + revisions: + - name: rev-1 + revision: 1 + contentID: content-1 + components: + - type: Image + image: + ref: quay.io/openshift/cluster-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + profile: default + expectedStatusError: "observedRevisionGeneration may not be unset once set" diff --git a/operator/v1alpha1/types_clusterapi.go b/operator/v1alpha1/types_clusterapi.go index 5816f936283..a793e6576a2 100644 --- a/operator/v1alpha1/types_clusterapi.go +++ b/operator/v1alpha1/types_clusterapi.go @@ -78,6 +78,7 @@ type RevisionName string // ClusterAPIStatus describes the current state of the capi-operator. // +kubebuilder:validation:XValidation:rule="self.revisions.exists(r, r.name == self.desiredRevision && self.revisions.all(s, s.revision <= r.revision))",message="desiredRevision must be the name of the revision with the highest revision number" // +kubebuilder:validation:XValidation:rule="!has(self.currentRevision) || self.revisions.exists(r, r.name == self.currentRevision)",message="currentRevision must correspond to an entry in the revisions list" +// +kubebuilder:validation:XValidation:rule="!has(oldSelf.observedRevisionGeneration) || has(self.observedRevisionGeneration)",message="observedRevisionGeneration may not be unset once set" type ClusterAPIStatus struct { // currentRevision is the name of the most recently fully applied revision. // It is written by the installer controller. If it is absent, it indicates @@ -111,6 +112,14 @@ type ClusterAPIStatus struct { // +kubebuilder:validation:XValidation:rule="self.all(new, oldSelf.exists(old, old.name == new.name) || oldSelf.all(old, new.revision > old.revision))",message="new revisions must have a revision number greater than all existing revisions" // +kubebuilder:validation:XValidation:rule="oldSelf.all(old, !self.exists(new, new.name == old.name) || self.exists(new, new == old))",message="existing revisions are immutable, but may be removed" Revisions []ClusterAPIInstallerRevision `json:"revisions,omitempty"` + + // observedRevisionGeneration is the generation of the ClusterAPI object that was last observed by the revision controller. + // If specified it must be greater than or equal to 1. It may not decrease or be unset once set. + // + // +optional + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:XValidation:rule="self >= oldSelf",message="observedRevisionGeneration may not decrease" + ObservedRevisionGeneration int64 `json:"observedRevisionGeneration,omitempty"` } // +structType=atomic @@ -144,6 +153,14 @@ type ClusterAPIInstallerRevision struct { // +optional UnmanagedCustomResourceDefinitions []string `json:"unmanagedCustomResourceDefinitions,omitempty"` + // manifestSubstitutions is a list of envsubst style substitutions which + // will be applied to manifests in the revision during rendering. + // +optional + // +listType=atomic + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=32 + ManifestSubstitutions []ClusterAPIInstallerRevisionManifestSubstitution `json:"manifestSubstitutions,omitempty"` + // components is a list of components which will be installed by this // revision. Components will be installed in the order they are listed. If // omitted no components will be installed. @@ -157,6 +174,29 @@ type ClusterAPIInstallerRevision struct { Components []ClusterAPIInstallerComponent `json:"components,omitempty"` } +// ClusterAPIInstallerRevisionManifestSubstitution defines an envsubst style +// substitution which will be applied to manifests in a revision during +// rendering. +type ClusterAPIInstallerRevisionManifestSubstitution struct { + // key is the name of the envsubst variable to substitute. It must be a + // valid envsubst variable name, consisting of letters, digits, and + // underscores, and must start with a letter or underscore. The key must + // not be empty, and must not exceed 255 characters. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=255 + // +kubebuilder:validation:XValidation:rule="self.matches('^[A-Za-z_][A-Za-z0-9_]*$')",message="key must start with a letter or underscore, followed by letters, digits, or underscores" + Key string `json:"key,omitempty"` + + // value is the value to substitute for the envsubst variable. It may be + // empty, in which case the variable will be substituted with an empty + // string. The value must not exceed 255 characters. + // +required + // +kubebuilder:validation:MinLength=0 + // +kubebuilder:validation:MaxLength=255 + Value *string `json:"value,omitempty"` +} + // InstallerComponentType is the type of component to install. // +kubebuilder:validation:Enum=Image // +enum @@ -171,6 +211,15 @@ const ( // +union // +kubebuilder:validation:XValidation:rule="self.type == 'Image' ? has(self.image) : !has(self.image)",message="image is required when type is Image, and forbidden otherwise" type ClusterAPIInstallerComponent struct { + // name is the human-readable name of the component. It is only used to make + // api revisions easier to understand. It must consist of alphanumeric + // characters, or '-'. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=255 + // +kubebuilder:validation:XValidation:rule="self.matches('^[A-Za-z0-9-]+$')",message="name must consist of alphanumeric characters or '-'" + Name string `json:"name,omitempty"` + // type is the source type of the component. // The only valid value is Image. // When set to Image, the image field must be set and will define an image source for the component. diff --git a/operator/v1alpha1/zz_generated.crd-manifests/0000_30_cluster-api_01_clusterapis.crd.yaml b/operator/v1alpha1/zz_generated.crd-manifests/0000_30_cluster-api_01_clusterapis.crd.yaml index 6516a3ef0dd..57752690f1e 100644 --- a/operator/v1alpha1/zz_generated.crd-manifests/0000_30_cluster-api_01_clusterapis.crd.yaml +++ b/operator/v1alpha1/zz_generated.crd-manifests/0000_30_cluster-api_01_clusterapis.crd.yaml @@ -106,6 +106,16 @@ spec: maxLength: 255 minLength: 1 type: string + observedRevisionGeneration: + description: |- + observedRevisionGeneration is the generation of the ClusterAPI object that was last observed by the revision controller. + If specified it must be greater than or equal to 1. It may not decrease or be unset once set. + format: int64 + minimum: 1 + type: integer + x-kubernetes-validations: + - message: observedRevisionGeneration may not decrease + rule: self >= oldSelf revisions: description: |- revisions is a list of all currently active revisions. A revision is @@ -168,6 +178,18 @@ spec: - profile - ref type: object + name: + description: |- + name is the human-readable name of the component. It is only used to make + api revisions easier to understand. It must consist of alphanumeric + characters, or '-'. + maxLength: 255 + minLength: 1 + type: string + x-kubernetes-validations: + - message: name must consist of alphanumeric characters + or '-' + rule: self.matches('^[A-Za-z0-9-]+$') type: description: |- type is the source type of the component. @@ -194,6 +216,45 @@ spec: maxLength: 255 minLength: 1 type: string + manifestSubstitutions: + description: |- + manifestSubstitutions is a list of envsubst style substitutions which + will be applied to manifests in the revision during rendering. + items: + description: |- + ClusterAPIInstallerRevisionManifestSubstitution defines an envsubst style + substitution which will be applied to manifests in a revision during + rendering. + properties: + key: + description: |- + key is the name of the envsubst variable to substitute. It must be a + valid envsubst variable name, consisting of letters, digits, and + underscores, and must start with a letter or underscore. The key must + not be empty, and must not exceed 255 characters. + maxLength: 255 + minLength: 1 + type: string + x-kubernetes-validations: + - message: key must start with a letter or underscore, + followed by letters, digits, or underscores + rule: self.matches('^[A-Za-z_][A-Za-z0-9_]*$') + value: + description: |- + value is the value to substitute for the envsubst variable. It may be + empty, in which case the variable will be substituted with an empty + string. The value must not exceed 255 characters. + maxLength: 255 + minLength: 0 + type: string + required: + - key + - value + type: object + maxItems: 32 + minItems: 1 + type: array + x-kubernetes-list-type: atomic name: description: name is the name of a revision. maxLength: 255 @@ -259,6 +320,8 @@ spec: list rule: '!has(self.currentRevision) || self.revisions.exists(r, r.name == self.currentRevision)' + - message: observedRevisionGeneration may not be unset once set + rule: '!has(oldSelf.observedRevisionGeneration) || has(self.observedRevisionGeneration)' required: - metadata - spec diff --git a/operator/v1alpha1/zz_generated.deepcopy.go b/operator/v1alpha1/zz_generated.deepcopy.go index 1f3fd281e15..63a9e16fa6f 100644 --- a/operator/v1alpha1/zz_generated.deepcopy.go +++ b/operator/v1alpha1/zz_generated.deepcopy.go @@ -99,6 +99,13 @@ func (in *ClusterAPIInstallerRevision) DeepCopyInto(out *ClusterAPIInstallerRevi *out = make([]string, len(*in)) copy(*out, *in) } + if in.ManifestSubstitutions != nil { + in, out := &in.ManifestSubstitutions, &out.ManifestSubstitutions + *out = make([]ClusterAPIInstallerRevisionManifestSubstitution, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } if in.Components != nil { in, out := &in.Components, &out.Components *out = make([]ClusterAPIInstallerComponent, len(*in)) @@ -117,6 +124,27 @@ func (in *ClusterAPIInstallerRevision) DeepCopy() *ClusterAPIInstallerRevision { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterAPIInstallerRevisionManifestSubstitution) DeepCopyInto(out *ClusterAPIInstallerRevisionManifestSubstitution) { + *out = *in + if in.Value != nil { + in, out := &in.Value, &out.Value + *out = new(string) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterAPIInstallerRevisionManifestSubstitution. +func (in *ClusterAPIInstallerRevisionManifestSubstitution) DeepCopy() *ClusterAPIInstallerRevisionManifestSubstitution { + if in == nil { + return nil + } + out := new(ClusterAPIInstallerRevisionManifestSubstitution) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterAPIList) DeepCopyInto(out *ClusterAPIList) { *out = *in diff --git a/operator/v1alpha1/zz_generated.featuregated-crd-manifests/clusterapis.operator.openshift.io/ClusterAPIMachineManagement.yaml b/operator/v1alpha1/zz_generated.featuregated-crd-manifests/clusterapis.operator.openshift.io/ClusterAPIMachineManagement.yaml index 1b2bd420876..8bde6d108a5 100644 --- a/operator/v1alpha1/zz_generated.featuregated-crd-manifests/clusterapis.operator.openshift.io/ClusterAPIMachineManagement.yaml +++ b/operator/v1alpha1/zz_generated.featuregated-crd-manifests/clusterapis.operator.openshift.io/ClusterAPIMachineManagement.yaml @@ -107,6 +107,16 @@ spec: maxLength: 255 minLength: 1 type: string + observedRevisionGeneration: + description: |- + observedRevisionGeneration is the generation of the ClusterAPI object that was last observed by the revision controller. + If specified it must be greater than or equal to 1. It may not decrease or be unset once set. + format: int64 + minimum: 1 + type: integer + x-kubernetes-validations: + - message: observedRevisionGeneration may not decrease + rule: self >= oldSelf revisions: description: |- revisions is a list of all currently active revisions. A revision is @@ -169,6 +179,18 @@ spec: - profile - ref type: object + name: + description: |- + name is the human-readable name of the component. It is only used to make + api revisions easier to understand. It must consist of alphanumeric + characters, or '-'. + maxLength: 255 + minLength: 1 + type: string + x-kubernetes-validations: + - message: name must consist of alphanumeric characters + or '-' + rule: self.matches('^[A-Za-z0-9-]+$') type: description: |- type is the source type of the component. @@ -195,6 +217,45 @@ spec: maxLength: 255 minLength: 1 type: string + manifestSubstitutions: + description: |- + manifestSubstitutions is a list of envsubst style substitutions which + will be applied to manifests in the revision during rendering. + items: + description: |- + ClusterAPIInstallerRevisionManifestSubstitution defines an envsubst style + substitution which will be applied to manifests in a revision during + rendering. + properties: + key: + description: |- + key is the name of the envsubst variable to substitute. It must be a + valid envsubst variable name, consisting of letters, digits, and + underscores, and must start with a letter or underscore. The key must + not be empty, and must not exceed 255 characters. + maxLength: 255 + minLength: 1 + type: string + x-kubernetes-validations: + - message: key must start with a letter or underscore, + followed by letters, digits, or underscores + rule: self.matches('^[A-Za-z_][A-Za-z0-9_]*$') + value: + description: |- + value is the value to substitute for the envsubst variable. It may be + empty, in which case the variable will be substituted with an empty + string. The value must not exceed 255 characters. + maxLength: 255 + minLength: 0 + type: string + required: + - key + - value + type: object + maxItems: 32 + minItems: 1 + type: array + x-kubernetes-list-type: atomic name: description: name is the name of a revision. maxLength: 255 @@ -260,6 +321,8 @@ spec: list rule: '!has(self.currentRevision) || self.revisions.exists(r, r.name == self.currentRevision)' + - message: observedRevisionGeneration may not be unset once set + rule: '!has(oldSelf.observedRevisionGeneration) || has(self.observedRevisionGeneration)' required: - metadata - spec diff --git a/operator/v1alpha1/zz_generated.swagger_doc_generated.go b/operator/v1alpha1/zz_generated.swagger_doc_generated.go index 92cef1421a6..2f3f128f9bb 100644 --- a/operator/v1alpha1/zz_generated.swagger_doc_generated.go +++ b/operator/v1alpha1/zz_generated.swagger_doc_generated.go @@ -148,6 +148,7 @@ func (ClusterAPI) SwaggerDoc() map[string]string { var map_ClusterAPIInstallerComponent = map[string]string{ "": "ClusterAPIInstallerComponent defines a component which will be installed by this revision.", + "name": "name is the human-readable name of the component. It is only used to make api revisions easier to understand. It must consist of alphanumeric characters, or '-'.", "type": "type is the source type of the component. The only valid value is Image. When set to Image, the image field must be set and will define an image source for the component.", "image": "image defines an image source for a component. The image must contain a /capi-operator-installer directory containing the component manifests.", } @@ -171,6 +172,7 @@ var map_ClusterAPIInstallerRevision = map[string]string{ "revision": "revision is a monotonically increasing number that is assigned to a revision.", "contentID": "contentID uniquely identifies the content of this revision. The contentID must be between 1 and 255 characters long.", "unmanagedCustomResourceDefinitions": "unmanagedCustomResourceDefinitions is a list of the names of ClusterResourceDefinition (CRD) objects which are included in this revision, but which should not be installed or updated. If not set, all CRDs in the revision will be managed by the CAPI operator.", + "manifestSubstitutions": "manifestSubstitutions is a list of envsubst style substitutions which will be applied to manifests in the revision during rendering.", "components": "components is a list of components which will be installed by this revision. Components will be installed in the order they are listed. If omitted no components will be installed.\n\nThe maximum number of components is 32.", } @@ -178,6 +180,16 @@ func (ClusterAPIInstallerRevision) SwaggerDoc() map[string]string { return map_ClusterAPIInstallerRevision } +var map_ClusterAPIInstallerRevisionManifestSubstitution = map[string]string{ + "": "ClusterAPIInstallerRevisionManifestSubstitution defines an envsubst style substitution which will be applied to manifests in a revision during rendering.", + "key": "key is the name of the envsubst variable to substitute. It must be a valid envsubst variable name, consisting of letters, digits, and underscores, and must start with a letter or underscore. The key must not be empty, and must not exceed 255 characters.", + "value": "value is the value to substitute for the envsubst variable. It may be empty, in which case the variable will be substituted with an empty string. The value must not exceed 255 characters.", +} + +func (ClusterAPIInstallerRevisionManifestSubstitution) SwaggerDoc() map[string]string { + return map_ClusterAPIInstallerRevisionManifestSubstitution +} + var map_ClusterAPIList = map[string]string{ "": "ClusterAPIList contains a list of ClusterAPI configurations\n\nCompatibility level 4: No compatibility is provided, the API can change at any point for any reason. These capabilities should not be used by applications needing long term support.", "metadata": "metadata is the standard list's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", @@ -198,10 +210,11 @@ func (ClusterAPISpec) SwaggerDoc() map[string]string { } var map_ClusterAPIStatus = map[string]string{ - "": "ClusterAPIStatus describes the current state of the capi-operator.", - "currentRevision": "currentRevision is the name of the most recently fully applied revision. It is written by the installer controller. If it is absent, it indicates that no revision has been fully applied yet. If set, currentRevision must correspond to an entry in the revisions list.", - "desiredRevision": "desiredRevision is the name of the desired revision. It is written by the revision controller. It must be set to the name of the entry in the revisions list with the highest revision number.", - "revisions": "revisions is a list of all currently active revisions. A revision is active until the installer controller updates currentRevision to a later revision. It is written by the revision controller.\n\nThe maximum number of revisions is 16. All revisions must have a unique name. All revisions must have a unique revision number. When adding a revision, the revision number must be greater than the highest revision number in the list. Revisions are immutable, although they can be deleted.", + "": "ClusterAPIStatus describes the current state of the capi-operator.", + "currentRevision": "currentRevision is the name of the most recently fully applied revision. It is written by the installer controller. If it is absent, it indicates that no revision has been fully applied yet. If set, currentRevision must correspond to an entry in the revisions list.", + "desiredRevision": "desiredRevision is the name of the desired revision. It is written by the revision controller. It must be set to the name of the entry in the revisions list with the highest revision number.", + "revisions": "revisions is a list of all currently active revisions. A revision is active until the installer controller updates currentRevision to a later revision. It is written by the revision controller.\n\nThe maximum number of revisions is 16. All revisions must have a unique name. All revisions must have a unique revision number. When adding a revision, the revision number must be greater than the highest revision number in the list. Revisions are immutable, although they can be deleted.", + "observedRevisionGeneration": "observedRevisionGeneration is the generation of the ClusterAPI object that was last observed by the revision controller. If specified it must be greater than or equal to 1. It may not decrease or be unset once set.", } func (ClusterAPIStatus) SwaggerDoc() map[string]string { diff --git a/payload-manifests/crds/0000_30_cluster-api_01_clusterapis.crd.yaml b/payload-manifests/crds/0000_30_cluster-api_01_clusterapis.crd.yaml index 6516a3ef0dd..57752690f1e 100644 --- a/payload-manifests/crds/0000_30_cluster-api_01_clusterapis.crd.yaml +++ b/payload-manifests/crds/0000_30_cluster-api_01_clusterapis.crd.yaml @@ -106,6 +106,16 @@ spec: maxLength: 255 minLength: 1 type: string + observedRevisionGeneration: + description: |- + observedRevisionGeneration is the generation of the ClusterAPI object that was last observed by the revision controller. + If specified it must be greater than or equal to 1. It may not decrease or be unset once set. + format: int64 + minimum: 1 + type: integer + x-kubernetes-validations: + - message: observedRevisionGeneration may not decrease + rule: self >= oldSelf revisions: description: |- revisions is a list of all currently active revisions. A revision is @@ -168,6 +178,18 @@ spec: - profile - ref type: object + name: + description: |- + name is the human-readable name of the component. It is only used to make + api revisions easier to understand. It must consist of alphanumeric + characters, or '-'. + maxLength: 255 + minLength: 1 + type: string + x-kubernetes-validations: + - message: name must consist of alphanumeric characters + or '-' + rule: self.matches('^[A-Za-z0-9-]+$') type: description: |- type is the source type of the component. @@ -194,6 +216,45 @@ spec: maxLength: 255 minLength: 1 type: string + manifestSubstitutions: + description: |- + manifestSubstitutions is a list of envsubst style substitutions which + will be applied to manifests in the revision during rendering. + items: + description: |- + ClusterAPIInstallerRevisionManifestSubstitution defines an envsubst style + substitution which will be applied to manifests in a revision during + rendering. + properties: + key: + description: |- + key is the name of the envsubst variable to substitute. It must be a + valid envsubst variable name, consisting of letters, digits, and + underscores, and must start with a letter or underscore. The key must + not be empty, and must not exceed 255 characters. + maxLength: 255 + minLength: 1 + type: string + x-kubernetes-validations: + - message: key must start with a letter or underscore, + followed by letters, digits, or underscores + rule: self.matches('^[A-Za-z_][A-Za-z0-9_]*$') + value: + description: |- + value is the value to substitute for the envsubst variable. It may be + empty, in which case the variable will be substituted with an empty + string. The value must not exceed 255 characters. + maxLength: 255 + minLength: 0 + type: string + required: + - key + - value + type: object + maxItems: 32 + minItems: 1 + type: array + x-kubernetes-list-type: atomic name: description: name is the name of a revision. maxLength: 255 @@ -259,6 +320,8 @@ spec: list rule: '!has(self.currentRevision) || self.revisions.exists(r, r.name == self.currentRevision)' + - message: observedRevisionGeneration may not be unset once set + rule: '!has(oldSelf.observedRevisionGeneration) || has(self.observedRevisionGeneration)' required: - metadata - spec