Skip to content

Commit

Permalink
Update godoc and validation for MOSC/MOSB
Browse files Browse the repository at this point in the history
Mostly fixups, with some minor changes to the v1alpha1 API:

 - Removed Version and ConfigGeneration from MOSB as they were unused
 - Updated relatedobjects list
 - Changed all optional,omitempty structs to pointers
 - Removed default for ImageBuilderType, but keeping default build arch
   to noarch as we don’t foresee changing that.
 - Fixed RenderedImagePushspec validators to match description
  • Loading branch information
yuqi-zhang committed Nov 15, 2024
1 parent 8b77960 commit a663367
Show file tree
Hide file tree
Showing 14 changed files with 412 additions and 248 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
apiVersion: apiextensions.k8s.io/v1 # Hack because controller-gen complains if we don't have this
name: "[TechPreview] MachineOSBuild"
name: "MachineOSBuild"
crdName: machineosbuilds.machineconfiguration.openshift.io
featureGate: OnClusterBuild
tests:
onCreate:
- name: Should be able to create a minimal MachineOSBuild
initial: |
apiVersion: machineconfiguration.openshift.io/v1alpha1
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineOSBuild
metadata:
name: foobar
Expand All @@ -19,7 +19,7 @@ tests:
name: worker
renderedImagePushspec: quay.io/cdoern/mco:latest
expected: |
apiVersion: machineconfiguration.openshift.io/v1alpha1
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineOSBuild
metadata:
name: foobar
Expand All @@ -33,7 +33,7 @@ tests:
renderedImagePushspec: quay.io/cdoern/mco:latest
- name: fail on invalid version
initial: |
apiVersion: machineconfiguration.openshift.io/v1alpha1
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineOSBuild
metadata:
name: foobar
Expand All @@ -48,7 +48,7 @@ tests:
expectedError: "Invalid value: 0: spec.version in body should be greater than or equal to 1"
- name: fail on invalid configGeneration
initial: |
apiVersion: machineconfiguration.openshift.io/v1alpha1
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineOSBuild
metadata:
name: foobar
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
apiVersion: apiextensions.k8s.io/v1 # Hack because controller-gen complains if we don't have this
name: "[TechPreview] MachineOSConfig"
name: "MachineOSConfig"
crdName: machineosconfigs.machineconfiguration.openshift.io
featureGate: OnClusterBuild
tests:
onCreate:
- name: Should be able to create a minimal MachineOSConfig
initial: |
apiVersion: machineconfiguration.openshift.io/v1alpha1
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineOSConfig
metadata:
name: foobar
Expand All @@ -27,7 +27,7 @@ tests:
currentImagePullSecret:
name: foo
expected: |
apiVersion: machineconfiguration.openshift.io/v1alpha1
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineOSConfig
metadata:
name: foobar
Expand All @@ -47,9 +47,53 @@ tests:
buildOutputs:
currentImagePullSecret:
name: foo
onCreate:
- name: Should be able to create a MachineOSConfig with a renderedImagePushspec that contains a port
initial: |
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineOSConfig
metadata:
name: foobar
spec:
machineConfigPool:
name: worker
buildInputs:
imageBuilder:
imageBuilderType: PodImageBuilder
baseOSImagePullspec: example.io/my-project/image-v1.0_23@sha256:2c3ea52ac3a41c6d58e85977c3149413e3fa4b70eb2397426456863adbf43306
baseImagePullSecret:
name: foo
baseOSExtensionsImagePullspec: example.io/my-project/image-v1.0_23@sha256:2c3ea52ac3a41c6d58e85977c3149413e3fa4b70eb2397426456863adbf43306
renderedImagePushSecret:
name: foo
renderedImagePushspec: registry.test.example.local:5000/test/custom-os-image:v0.1
buildOutputs:
currentImagePullSecret:
name: foo
expected: |
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineOSConfig
metadata:
name: foobar
spec:
machineConfigPool:
name: worker
buildInputs:
imageBuilder:
imageBuilderType: PodImageBuilder
baseOSImagePullspec: example.io/my-project/image-v1.0_23@sha256:2c3ea52ac3a41c6d58e85977c3149413e3fa4b70eb2397426456863adbf43306
baseImagePullSecret:
name: foo
baseOSExtensionsImagePullspec: example.io/my-project/image-v1.0_23@sha256:2c3ea52ac3a41c6d58e85977c3149413e3fa4b70eb2397426456863adbf43306
renderedImagePushSecret:
name: foo
renderedImagePushspec: registry.test.example.local:5000/test/custom-os-image:v0.1
buildOutputs:
currentImagePullSecret:
name: foo
- name: Fail on invalid rendered image pushspec
initial: |
apiVersion: machineconfiguration.openshift.io/v1alpha1
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineOSConfig
metadata:
name: foobar
Expand All @@ -72,7 +116,7 @@ tests:
expectedError: "Invalid value: \"string\": the OCI Image name should follow the host[:port][/namespace]/name format, resembling a valid URL without the scheme"
- name: Fail on invalid base image pullspec
initial: |
apiVersion: machineconfiguration.openshift.io/v1alpha1
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineOSConfig
metadata:
name: foobar
Expand All @@ -95,7 +139,7 @@ tests:
expectedError: "Invalid value: \"string\": the OCI Image name should follow the host[:port][/namespace]/name format, resembling a valid URL without the scheme"
- name: Allows for an empty pull secret
initial: |
apiVersion: machineconfiguration.openshift.io/v1alpha1
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineOSConfig
metadata:
name: foobar
Expand All @@ -114,7 +158,7 @@ tests:
currentImagePullSecret:
name: foo
expected: |
apiVersion: machineconfiguration.openshift.io/v1alpha1
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineOSConfig
metadata:
name: foobar
Expand Down
47 changes: 29 additions & 18 deletions machineconfiguration/v1/types_machineosbuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,27 +52,18 @@ type MachineOSBuildList struct {

// MachineOSBuildSpec describes information about a build process primarily populated from a MachineOSConfig object.
type MachineOSBuildSpec struct {
// configGeneration tracks which version of MachineOSConfig this build is based off of
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Required
ConfigGeneration int64 `json:"configGeneration"`
// desiredConfig is the desired config we want to build an image for.
// +kubebuilder:validation:Required
DesiredConfig RenderedMachineConfigReference `json:"desiredConfig"`
// machineOSConfig is the config object which the build is based off of
// +kubebuilder:validation:Required
MachineOSConfig MachineOSConfigReference `json:"machineOSConfig"`
// version tracks the newest MachineOSBuild for each MachineOSConfig
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Required
Version int64 `json:"version"`
// renderedImagePushspec is set from the MachineOSConfig
// The format of the image pullspec is:
// host[:port][/namespace]/name:<tag> or svc_name.namespace.svc[:port]/repository/name:<tag>
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=447
// +kubebuilder:validation:XValidation:rule=`((self.split(':').size() == 2 && self.split(':')[1].matches('^([a-zA-Z0-9-./:])+$')) || self.matches('^[^.]+\\.[^.]+\\.svc:\\d+\\/[^\\/]+\\/[^\\/]+:[^\\/]+$'))`,message="the OCI Image reference must end with a valid :<tag>, where '<digest>' is 64 characters long and '<tag>' is any valid string Or it must be a valid .svc followed by a port, repository, image name, and tag."
// +kubebuilder:validation:XValidation:rule=`((self.split(':').size() == 2 && self.split(':')[0].matches('^([a-zA-Z0-9-]+\\.)+[a-zA-Z0-9-]+(:[0-9]{2,5})?/([a-zA-Z0-9-_]{0,61}/)?[a-zA-Z0-9-_.]*?$')) || self.matches('^[^.]+\\.[^.]+\\.svc:\\d+\\/[^\\/]+\\/[^\\/]+:[^\\/]+$'))`,message="the OCI Image name should follow the host[:port][/namespace]/name format, resembling a valid URL without the scheme. Or it must be a valid .svc followed by a port, repository, image name, and tag."
// +kubebuilder:validation:XValidation:rule=`self.matches('^([a-zA-Z0-9-]+\\.)+[a-zA-Z0-9-]+(:[0-9]{2,5})?(/[a-zA-Z0-9-_]{1,61})*/[a-zA-Z0-9-_.]+:[a-zA-Z0-9._-]+$') || self.matches('^[^.]+\\.[^.]+\\.svc:\\d+\\/[^\\/]+\\/[^\\/]+:[^\\/]+$')`,message="the OCI Image name should follow the host[:port][/namespace]/name format, resembling a valid URL without the scheme. Or it must be a valid .svc followed by a port, repository, image name, and tag."
// +kubebuilder:validation:Required
RenderedImagePushspec string `json:"renderedImagePushspec"`
}
Expand All @@ -88,35 +79,40 @@ type MachineOSBuildStatus struct {
// +listMapKey=type
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
// ImageBuilderType describes the image builder set in the MachineOSConfig
// BuilderReference describes which ImageBuilder backend to use for this build
// +optional
BuilderReference *MachineOSBuilderReference `json:"builderReference"`
// relatedObjects is a list of objects that are related to the build process.
// +kubebuilder:validation:MaxItems=10
// +listType=map
// +listMapKey=name
// +listMapKey=resource
RelatedObjects []ObjectReference `json:"relatedObjects,omitempty"`
// buildStart describes when the build started.
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="buildStart is immutable once set"
// +kubebuilder:validation:Required
BuildStart *metav1.Time `json:"buildStart"`
BuildStart metav1.Time `json:"buildStart"`
// buildEnd describes when the build ended.
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="buildEnd is immutable once set"
//+optional
// +optional
BuildEnd *metav1.Time `json:"buildEnd,omitempty"`
// finalImagePushSpec describes the fully qualified pushspec produced by this build that the final image can be. Must be in sha format.
// +kubebuilder:validation:XValidation:rule=`((self.split('@').size() == 2 && self.split('@')[1].matches('^sha256:[a-f0-9]{64}$')))`,message="the OCI Image reference must end with a valid '@sha256:<digest>' suffix, where '<digest>' is 64 characters long"
// finalImagePushSpec describes the fully qualified pushspec produced by this build that the final image can be. Must end with a valid '@sha256:<digest>' suffix, where '<digest>' is 64 hexadecimal characters long
// +kubebuilder:validation:XValidation:rule=`((self.split('@').size() == 2 && self.split('@')[1].matches('^sha256:[a-f0-9]{64}$')))`,message="the OCI Image reference must end with a valid '@sha256:<digest>' suffix, where '<digest>' is 64 hexadecimal characters long"
// +optional
FinalImagePushspec string `json:"finalImagePullspec,omitempty"`
}

// MachineOSBuilderReference describes which ImageBuilder backend to use for this build/
// MachineOSBuilderReference describes which ImageBuilder backend to use for this build
// +union
// +kubebuilder:validation:XValidation:rule="has(self.imageBuilderType) && self.imageBuilderType == 'PodImageBuilder' ? true : !has(self.buildPod)",message="buildPod is required when imageBuilderType is PodImageBuilder, and forbidden otherwise"
// +kubebuilder:validation:XValidation:rule="has(self.imageBuilderType) && self.imageBuilderType == 'PodImageBuilder' ? has(self.buildPod) : !has(self.buildPod)",message="buildPod is required when imageBuilderType is PodImageBuilder, and forbidden otherwise"
type MachineOSBuilderReference struct {
// ImageBuilderType describes the image builder set in the MachineOSConfig
// +unionDiscriminator
ImageBuilderType MachineOSImageBuilderType `json:"imageBuilderType"`

// relatedObjects is a list of objects that are related to the build process.
// +unionMember,optional
// +unionMember
// +optional
PodImageBuilder *ObjectReference `json:"buildPod,omitempty"`
}

Expand Down Expand Up @@ -150,22 +146,37 @@ type RenderedMachineConfigReference struct {
// ObjectReference contains enough information to let you inspect or modify the referred object.
type ObjectReference struct {
// group of the referent.
// This value should consist of only lowercase alphanumeric characters, hyphens and periods.
// Example: "", "apps", "build.openshift.io", etc.
// +kubebuilder:validation:Pattern:="^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$"
// +kubebuilder:validation:Required
Group string `json:"group"`
// resource of the referent.
// This value should consist of only lowercase alphanumeric characters and hyphens.
// Example: "deployments", "deploymentconfigs", "pods", etc.
// +kubebuilder:validation:Required
// +kubebuilder:validation:Pattern:="^[a-z0-9]([-a-z0-9]*[a-z0-9])?$"
Resource string `json:"resource"`
// namespace of the referent.
// +kubebuilder:validation:Pattern:="^[a-z0-9]([-a-z0-9]*[a-z0-9])?$"
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=63
// +optional
Namespace string `json:"namespace,omitempty"`
// name of the referent.
// +kubebuilder:validation:Required
// +kubebuilder:validation:Pattern:="^[a-z0-9]([-a-z0-9]*[a-z0-9])?$"
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=256
Name string `json:"name"`
}

// MachineOSConfigReference refers to the MachineOSConfig this build is based off of
type MachineOSConfigReference struct {
// name of the MachineOSConfig
// +kubebuilder:validation:Required
// +kubebuilder:validation:Pattern:="^[a-z0-9]([-a-z0-9]*[a-z0-9])?$"
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=256
Name string `json:"name"`
}
28 changes: 17 additions & 11 deletions machineconfiguration/v1/types_machineosconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type MachineOSConfig struct {

// status describes the status of the machineosconfig
// +optional
Status MachineOSConfigStatus `json:"status,omitempty"`
Status *MachineOSConfigStatus `json:"status,omitempty"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
Expand All @@ -52,9 +52,9 @@ type MachineOSConfigSpec struct {
// buildInputs is where user input options for the build live
// +kubebuilder:validation:Required
BuildInputs BuildInputs `json:"buildInputs"`
// buildOutputs is where user input options for the build live
// buildOutputs holds all information needed to handle booting the image after a build
// +optional
BuildOutputs BuildOutputs `json:"buildOutputs,omitempty"`
BuildOutputs *BuildOutputs `json:"buildOutputs,omitempty"`
}

// MachineOSConfigStatus describes the status this config object and relates it to the builds associated with this MachineOSConfig
Expand All @@ -66,8 +66,7 @@ type MachineOSConfigStatus struct {
// +listMapKey=type
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
// observedGeneration represents the generation observed by the controller.
// this field is updated when the user changes the configuration in BuildSettings or the MCP this object is associated with.
// observedGeneration represents the generation observed by the Machine Config Operator's build controller.
// +kubebuilder:validation:Required
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
// currentImagePullspec is the fully qualified image pull spec used by the MCO to pull down the new OSImage. This must include sha256.
Expand Down Expand Up @@ -107,7 +106,7 @@ type BuildInputs struct {
// must live in the openshift-machine-config-operator namespace if provided.
// defaults to using the cluster-wide pull secret if not specified.
// +optional
BaseImagePullSecret ImageSecretObjectReference `json:"baseImagePullSecret,omitempty"`
BaseImagePullSecret *ImageSecretObjectReference `json:"baseImagePullSecret,omitempty"`
// machineOSImageBuilder describes which image builder will be used in each build triggered by this MachineOSConfig
// +kubebuilder:validation:Required
ImageBuilder *MachineOSImageBuilder `json:"imageBuilder"`
Expand All @@ -126,18 +125,20 @@ type BuildInputs struct {
// host[:port][/namespace]/name:<tag> or svc_name.namespace.svc[:port]/repository/name:<tag>
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=447
// +kubebuilder:validation:XValidation:rule=`((self.split(':').size() == 2 && self.split(':')[1].matches('^([a-zA-Z0-9-./:])+$')) || self.matches('^[^.]+\\.[^.]+\\.svc:\\d+\\/[^\\/]+\\/[^\\/]+:[^\\/]+$'))`,message="the OCI Image reference must end with a valid :<tag>, where '<digest>' is 64 characters long and '<tag>' is any valid string Or it must be a valid .svc followed by a port, repository, image name, and tag."
// +kubebuilder:validation:XValidation:rule=`((self.split(':').size() == 2 && self.split(':')[0].matches('^([a-zA-Z0-9-]+\\.)+[a-zA-Z0-9-]+(:[0-9]{2,5})?/([a-zA-Z0-9-_]{0,61}/)?[a-zA-Z0-9-_.]*?$')) || self.matches('^[^.]+\\.[^.]+\\.svc:\\d+\\/[^\\/]+\\/[^\\/]+:[^\\/]+$'))`,message="the OCI Image name should follow the host[:port][/namespace]/name format, resembling a valid URL without the scheme. Or it must be a valid .svc followed by a port, repository, image name, and tag."
// +kubebuilder:validation:XValidation:rule=`self.matches('^([a-zA-Z0-9-]+\\.)+[a-zA-Z0-9-]+(:[0-9]{2,5})?(/[a-zA-Z0-9-_]{1,61})*/[a-zA-Z0-9-_.]+:[a-zA-Z0-9._-]+$') || self.matches('^[^.]+\\.[^.]+\\.svc:\\d+\\/[^\\/]+\\/[^\\/]+:[^\\/]+$')`,message="the OCI Image name should follow the host[:port][/namespace]/name format, resembling a valid URL without the scheme. Or it must be a valid .svc followed by a port, repository, image name, and tag."
// +kubebuilder:validation:Required
RenderedImagePushspec string `json:"renderedImagePushspec"`
// releaseVersion is associated with the base OS Image. This is the version of Openshift that the Base Image is associated with.
// releaseVersion is an Openshift release version which the base OS image is associated with.
// This field is populated from the machine-config-osimageurl configmap in the openshift-machine-config-operator namespace.
// It will come in the format: 4.16.0-0.nightly-2024-04-03-065948 or any valid release. The MachineOSBuilder populates this field and validates that this is a valid stream.
// This is used as a label in the dockerfile that builds the OS image.
// +kubebuilder:validation:MaxLength:=253
// +kubebuilder:validation:Pattern=`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`
// +optional
ReleaseVersion string `json:"releaseVersion,omitempty"`
// containerFile describes the custom data the user has specified to build into the image.
// this is also commonly called a Dockerfile and you can treat it as such. The content is the content of your Dockerfile.
// you can specify up to 7 containerFiles
// +patchMergeKey=containerfileArch
// +patchStrategy=merge
// +listType=map
Expand All @@ -163,7 +164,6 @@ type BuildOutputs struct {

type MachineOSImageBuilder struct {
// imageBuilderType specifies the backend to be used to build the image.
// +kubebuilder:default:=PodImageBuilder
// +kubebuilder:validation:Enum:=PodImageBuilder
// Valid options are: PodImageBuilder
ImageBuilderType MachineOSImageBuilderType `json:"imageBuilderType"`
Expand All @@ -178,7 +178,11 @@ type MachineOSContainerfile struct {
// +kubebuilder:default:=noarch
// +optional
ContainerfileArch ContainerfileArch `json:"containerfileArch"`
// content is the custom content to be built
// content is an embedded Containerfile/Dockerfile that defines the contents to be built into your image.
// for example, this would add the tree package to your hosts:
// FROM configs AS final
// RUN rpm-ostree install tree && \
// ostree container commit
// +kubebuilder:validation:Required
Content string `json:"content"`
}
Expand Down Expand Up @@ -216,6 +220,8 @@ type MachineConfigPoolReference struct {
type ImageSecretObjectReference struct {
// name is the name of the secret used to push or pull this MachineOSConfig object.
// this secret must be in the openshift-machine-config-operator namespace.
// +kubebuilder:validation:MaxLength:=253
// +kubebuilder:validation:Pattern=`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`
// +kubebuilder:validation:Required
Name string `json:"name"`
}
Expand Down
Loading

0 comments on commit a663367

Please sign in to comment.