diff --git a/api/bases/baremetal.openstack.org_openstackbaremetalsets.yaml b/api/bases/baremetal.openstack.org_openstackbaremetalsets.yaml index f9fce81..42976d9 100644 --- a/api/bases/baremetal.openstack.org_openstackbaremetalsets.yaml +++ b/api/bases/baremetal.openstack.org_openstackbaremetalsets.yaml @@ -245,12 +245,22 @@ spec: type: object type: object type: object + osContainerImageType: + default: self-extracting + description: OSContainerImageType - Whether the OS container image + is a self-extracting or a bootc based image + enum: + - self-extracting + - bootc + type: string osContainerImageUrl: description: OSContainerImageURL - Container image URL for init with the OS qcow2 image (osImage) type: string osImage: - description: OSImage - OS qcow2 image Name + description: OSImage - For osContainerImageType=self-extracting, the + name of the OS qcow2 file to extract from the image. For osContainerImageType=bootc, + the name of the qcow2 file to write to for bootc install. type: string passwordSecret: description: 'PasswordSecret the name of the secret used to optionally diff --git a/api/bases/baremetal.openstack.org_openstackprovisionservers.yaml b/api/bases/baremetal.openstack.org_openstackprovisionservers.yaml index 9d6ceda..b3fa3a0 100644 --- a/api/bases/baremetal.openstack.org_openstackprovisionservers.yaml +++ b/api/bases/baremetal.openstack.org_openstackprovisionservers.yaml @@ -67,12 +67,22 @@ spec: description: NodeSelector to target subset of worker nodes running this provision server type: object + osContainerImageType: + default: self-extracting + description: OSContainerImageType - Whether the OS container image + is a self-extracting or a bootc based image + enum: + - self-extracting + - bootc + type: string osContainerImageUrl: description: OSContainerImageURL - Container image URL for init with the OS qcow2 image (osImage) type: string osImage: - description: OSImage - OS qcow2 image (compressed as gz, or uncompressed) + description: OSImage - For osContainerImageType=self-extracting, the + name of the OS qcow2 file to extract from the image. For osContainerImageType=bootc, + the name of the qcow2 file to write to for bootc install. type: string osImageDir: default: /usr/local/apache2/htdocs @@ -143,7 +153,6 @@ spec: - agentImageUrl - apacheImageUrl - osContainerImageUrl - - osImage - osImageDir type: object status: diff --git a/api/v1beta1/openstackbaremetalset_types.go b/api/v1beta1/openstackbaremetalset_types.go index 950c1e3..efa1462 100644 --- a/api/v1beta1/openstackbaremetalset_types.go +++ b/api/v1beta1/openstackbaremetalset_types.go @@ -55,11 +55,15 @@ type OpenStackBaremetalSetSpec struct { // BaremetalHosts - Map of hostname to Instance Spec for all nodes to provision BaremetalHosts map[string]InstanceSpec `json:"baremetalHosts,omitempty"` // +kubebuilder:validation:Optional - // OSImage - OS qcow2 image Name + // OSImage - For osContainerImageType=self-extracting, the name of the OS qcow2 file to extract from the image. For osContainerImageType=bootc, the name of the qcow2 file to write to for bootc install. OSImage string `json:"osImage,omitempty"` // +kubebuilder:validation:Optional // OSContainerImageURL - Container image URL for init with the OS qcow2 image (osImage) OSContainerImageURL string `json:"osContainerImageUrl,omitempty"` + // +kubebuilder:validation:Enum=self-extracting;bootc + // +kubebuilder:default=self-extracting + // OSContainerImageType - Whether the OS container image is a self-extracting or a bootc based image + OSContainerImageType string `json:"osContainerImageType,omitempty"` // +kubebuilder:validation:Optional // ApacheImageURL - Container image URL for the main container that serves the downloaded OS qcow2 image (osImage) ApacheImageURL string `json:"apacheImageUrl,omitempty"` diff --git a/api/v1beta1/openstackprovisionserver_types.go b/api/v1beta1/openstackprovisionserver_types.go index 5478168..67af8df 100644 --- a/api/v1beta1/openstackprovisionserver_types.go +++ b/api/v1beta1/openstackprovisionserver_types.go @@ -43,6 +43,8 @@ const ( const ( // OSContainerImage - default fall-back image for OpenStackProvisionServer int container OSContainerImage = "quay.io/podified-antelope-centos9/edpm-hardened-uefi:current-podified" + // OSContainerImageType - default fall-back image type for OSContainerImage + OSContainerImageType = "self-extracting" // AgentImage - default fall-back image for OpenStackProvisionServer agent AgentImage = "quay.io/openstack-k8s-operators/openstack-baremetal-operator-agent:latest" // ApacheImage - default fall-back image for Apache @@ -61,7 +63,8 @@ type OpenStackProvisionServerSpec struct { // +kubebuilder:validation:Optional // Interface - An optional interface to use instead of the cluster's default provisioning interface (if any) Interface string `json:"interface,omitempty"` - // OSImage - OS qcow2 image (compressed as gz, or uncompressed) + // +kubebuilder:validation:Optional + // OSImage - For osContainerImageType=self-extracting, the name of the OS qcow2 file to extract from the image. For osContainerImageType=bootc, the name of the qcow2 file to write to for bootc install. OSImage string `json:"osImage"` // +kubebuilder:validation:Required // +kubebuilder:default=/usr/local/apache2/htdocs @@ -69,6 +72,10 @@ type OpenStackProvisionServerSpec struct { OSImageDir *string `json:"osImageDir"` // OSContainerImageURL - Container image URL for init with the OS qcow2 image (osImage) OSContainerImageURL string `json:"osContainerImageUrl"` + // +kubebuilder:validation:Enum=self-extracting;bootc + // +kubebuilder:default=self-extracting + // OSContainerImageType - Whether the OS container image is a self-extracting or a bootc based image + OSContainerImageType string `json:"osContainerImageType,omitempty"` // ApacheImageURL - Container image URL for the main container that serves the downloaded OS qcow2 image (osImage) ApacheImageURL string `json:"apacheImageUrl"` // AgentImageURL - Container image URL for the sidecar container that discovers provisioning network IPs @@ -138,10 +145,11 @@ type OpenStackProvisionServerList struct { // OpenStackProvisionServerDefaults - type OpenStackProvisionServerDefaults struct { - OSContainerImageURL string - AgentImageURL string - ApacheImageURL string - OSImage string + OSContainerImageURL string + OSContainerImageType string + AgentImageURL string + ApacheImageURL string + OSImage string } func init() { @@ -168,10 +176,11 @@ func (instance OpenStackProvisionServer) RbacResourceName() string { func SetupDefaults() { // Acquire environmental defaults and initialize OpenStackProvisionServer defaults with them openstackProvisionServerDefaults := OpenStackProvisionServerDefaults{ - OSContainerImageURL: util.GetEnvVar("RELATED_IMAGE_OS_CONTAINER_IMAGE_URL_DEFAULT", OSContainerImage), - AgentImageURL: util.GetEnvVar("RELATED_IMAGE_AGENT_IMAGE_URL_DEFAULT", AgentImage), - ApacheImageURL: util.GetEnvVar("RELATED_IMAGE_APACHE_IMAGE_URL_DEFAULT", ApacheImage), - OSImage: util.GetEnvVar("OS_IMAGE_DEFAULT", OSImage), + OSContainerImageURL: util.GetEnvVar("RELATED_IMAGE_OS_CONTAINER_IMAGE_URL_DEFAULT", OSContainerImage), + OSContainerImageType: util.GetEnvVar("RELATED_IMAGE_OS_CONTAINER_IMAGE_TYPE_DEFAULT", OSContainerImageType), + AgentImageURL: util.GetEnvVar("RELATED_IMAGE_AGENT_IMAGE_URL_DEFAULT", AgentImage), + ApacheImageURL: util.GetEnvVar("RELATED_IMAGE_APACHE_IMAGE_URL_DEFAULT", ApacheImage), + OSImage: util.GetEnvVar("OS_IMAGE_DEFAULT", OSImage), } SetupOpenStackProvisionServerDefaults(openstackProvisionServerDefaults) diff --git a/api/v1beta1/openstackprovisionserver_webhook.go b/api/v1beta1/openstackprovisionserver_webhook.go index 2c4d08e..f653a8c 100644 --- a/api/v1beta1/openstackprovisionserver_webhook.go +++ b/api/v1beta1/openstackprovisionserver_webhook.go @@ -106,6 +106,9 @@ func (r *OpenStackProvisionServer) Default() { if r.Spec.OSContainerImageURL == "" { r.Spec.OSContainerImageURL = openstackProvisionServerDefaults.OSContainerImageURL } + if r.Spec.OSContainerImageType == "" { + r.Spec.OSContainerImageType = openstackProvisionServerDefaults.OSContainerImageType + } if r.Spec.AgentImageURL == "" { r.Spec.AgentImageURL = openstackProvisionServerDefaults.AgentImageURL } diff --git a/config/crd/bases/baremetal.openstack.org_openstackbaremetalsets.yaml b/config/crd/bases/baremetal.openstack.org_openstackbaremetalsets.yaml index f9fce81..42976d9 100644 --- a/config/crd/bases/baremetal.openstack.org_openstackbaremetalsets.yaml +++ b/config/crd/bases/baremetal.openstack.org_openstackbaremetalsets.yaml @@ -245,12 +245,22 @@ spec: type: object type: object type: object + osContainerImageType: + default: self-extracting + description: OSContainerImageType - Whether the OS container image + is a self-extracting or a bootc based image + enum: + - self-extracting + - bootc + type: string osContainerImageUrl: description: OSContainerImageURL - Container image URL for init with the OS qcow2 image (osImage) type: string osImage: - description: OSImage - OS qcow2 image Name + description: OSImage - For osContainerImageType=self-extracting, the + name of the OS qcow2 file to extract from the image. For osContainerImageType=bootc, + the name of the qcow2 file to write to for bootc install. type: string passwordSecret: description: 'PasswordSecret the name of the secret used to optionally diff --git a/config/crd/bases/baremetal.openstack.org_openstackprovisionservers.yaml b/config/crd/bases/baremetal.openstack.org_openstackprovisionservers.yaml index 9d6ceda..b3fa3a0 100644 --- a/config/crd/bases/baremetal.openstack.org_openstackprovisionservers.yaml +++ b/config/crd/bases/baremetal.openstack.org_openstackprovisionservers.yaml @@ -67,12 +67,22 @@ spec: description: NodeSelector to target subset of worker nodes running this provision server type: object + osContainerImageType: + default: self-extracting + description: OSContainerImageType - Whether the OS container image + is a self-extracting or a bootc based image + enum: + - self-extracting + - bootc + type: string osContainerImageUrl: description: OSContainerImageURL - Container image URL for init with the OS qcow2 image (osImage) type: string osImage: - description: OSImage - OS qcow2 image (compressed as gz, or uncompressed) + description: OSImage - For osContainerImageType=self-extracting, the + name of the OS qcow2 file to extract from the image. For osContainerImageType=bootc, + the name of the qcow2 file to write to for bootc install. type: string osImageDir: default: /usr/local/apache2/htdocs @@ -143,7 +153,6 @@ spec: - agentImageUrl - apacheImageUrl - osContainerImageUrl - - osImage - osImageDir type: object status: diff --git a/controllers/openstackbaremetalset_controller.go b/controllers/openstackbaremetalset_controller.go index cb26bb9..826d310 100644 --- a/controllers/openstackbaremetalset_controller.go +++ b/controllers/openstackbaremetalset_controller.go @@ -516,6 +516,7 @@ func (r *OpenStackBaremetalSetReconciler) provisionServerCreateOrUpdate( } provisionServer.Spec.OSImage = instance.Spec.OSImage provisionServer.Spec.OSContainerImageURL = instance.Spec.OSContainerImageURL + provisionServer.Spec.OSContainerImageType = instance.Spec.OSContainerImageType provisionServer.Spec.ApacheImageURL = instance.Spec.ApacheImageURL provisionServer.Spec.AgentImageURL = instance.Spec.AgentImageURL provisionServer.Spec.NodeSelector = instance.Spec.ProvisonServerNodeSelector diff --git a/pkg/openstackprovisionserver/deployment.go b/pkg/openstackprovisionserver/deployment.go index 68a2813..f47cb74 100644 --- a/pkg/openstackprovisionserver/deployment.go +++ b/pkg/openstackprovisionserver/deployment.go @@ -183,9 +183,11 @@ func Deployment( } initContainerDetails := InitContainerDetails{ - OsImageDir: *instance.Spec.OSImageDir, - ContainerImage: instance.Spec.OSContainerImageURL, - VolumeMounts: getInitVolumeMounts(instance), + OsImageDir: *instance.Spec.OSImageDir, + OsImage: instance.Spec.OSImage, + ContainerImage: instance.Spec.OSContainerImageURL, + ContainerImageType: instance.Spec.OSContainerImageType, + VolumeMounts: getInitVolumeMounts(instance), } deployment.Spec.Template.Spec.InitContainers = InitContainer(initContainerDetails) diff --git a/pkg/openstackprovisionserver/initcontainer.go b/pkg/openstackprovisionserver/initcontainer.go index 6775337..e399118 100644 --- a/pkg/openstackprovisionserver/initcontainer.go +++ b/pkg/openstackprovisionserver/initcontainer.go @@ -3,18 +3,45 @@ package openstackprovisionserver import ( "github.com/openstack-k8s-operators/lib-common/modules/common/env" corev1 "k8s.io/api/core/v1" + "path/filepath" + "strings" ) // InitContainerDetails information type InitContainerDetails struct { - ContainerImage string - OsImageDir string - Privileged bool - VolumeMounts []corev1.VolumeMount + ContainerImage string + ContainerImageType string + OsImageDir string + OsImage string + Privileged bool + VolumeMounts []corev1.VolumeMount } // InitContainer - init container for provision server pods func InitContainer(init InitContainerDetails) []corev1.Container { + if init.ContainerImageType == "bootc" { + + osImage := init.OsImage + osImageExtension := filepath.Ext(osImage) + osImageNoExtension := strings.TrimSuffix(osImage, osImageExtension) + osImagePathRaw := filepath.Join(init.OsImageDir, osImageNoExtension+".raw") + + // TODO(sbaker) if the extension is qcow2 add an init container which runs "qemu-img convert" on the raw image + return []corev1.Container{ + { + Name: "init", + Image: init.ContainerImage, + Command: []string{"bootc", "install", "to-disk", "--generic-image", "--via-loopback", osImagePathRaw}, + SecurityContext: &corev1.SecurityContext{ + Privileged: &init.Privileged, + SELinuxOptions: &corev1.SELinuxOptions{ + Type: "unconfined_t", + }, + }, + VolumeMounts: init.VolumeMounts, + }, + } + } envs := []corev1.EnvVar{ { Name: "DEST_DIR", diff --git a/pkg/openstackprovisionserver/job.go b/pkg/openstackprovisionserver/job.go index ba83e23..befe2c1 100644 --- a/pkg/openstackprovisionserver/job.go +++ b/pkg/openstackprovisionserver/job.go @@ -86,9 +86,10 @@ func ChecksumJob( } initContainerDetails := InitContainerDetails{ - OsImageDir: *instance.Spec.OSImageDir, - ContainerImage: instance.Spec.OSContainerImageURL, - VolumeMounts: getInitVolumeMounts(instance), + OsImageDir: *instance.Spec.OSImageDir, + ContainerImage: instance.Spec.OSContainerImageURL, + ContainerImageType: instance.Spec.OSContainerImageType, + VolumeMounts: getInitVolumeMounts(instance), } job.Spec.Template.Spec.InitContainers = InitContainer(initContainerDetails) diff --git a/pkg/openstackprovisionserver/volumes.go b/pkg/openstackprovisionserver/volumes.go index 090ff17..55438f7 100644 --- a/pkg/openstackprovisionserver/volumes.go +++ b/pkg/openstackprovisionserver/volumes.go @@ -29,6 +29,22 @@ func getInitVolumes() []corev1.Volume { EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, + { + Name: "var-lib-containers", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/var/lib/containers", + }, + }, + }, + { + Name: "dev", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/dev", + }, + }, + }, } } @@ -54,6 +70,14 @@ func getInitVolumeMounts(instance *baremetalv1.OpenStackProvisionServer) []corev Name: "image-data", MountPath: *instance.Spec.OSImageDir, }, + { + Name: "var-lib-containers", + MountPath: "/var/lib/containers", + }, + { + Name: "dev", + MountPath: "/dev", + }, } } diff --git a/tests/functional/openstackbaremetalset_controller_test.go b/tests/functional/openstackbaremetalset_controller_test.go index 6a20bc3..f43900a 100644 --- a/tests/functional/openstackbaremetalset_controller_test.go +++ b/tests/functional/openstackbaremetalset_controller_test.go @@ -76,6 +76,7 @@ var _ = Describe("BaremetalSet Test", func() { }, OSImage: "", OSContainerImageURL: "", + OSContainerImageType: "", ApacheImageURL: "", AgentImageURL: "", AutomatedCleaningMode: "metadata", @@ -177,15 +178,16 @@ var _ = Describe("BaremetalSet Test", func() { provServer := GetProvisionServer(baremetalSetName) spec := baremetalv1.OpenStackProvisionServerSpec{ - Port: 6190, - Interface: "eth1", - OSImage: "edpm-hardened-uefi.qcow2", - OSImageDir: &osImageDir, - OSContainerImageURL: "quay.io/podified-antelope-centos9/edpm-hardened-uefi@latest", - ApacheImageURL: "registry.redhat.io/rhel8/httpd-24@latest", - AgentImageURL: "quay.io/openstack-k8s-operators/openstack-baremetal-operator-agent@latest", - NodeSelector: nil, - Resources: corev1.ResourceRequirements{Limits: nil, Requests: nil, Claims: nil}, + Port: 6190, + Interface: "eth1", + OSImage: "edpm-hardened-uefi.qcow2", + OSImageDir: &osImageDir, + OSContainerImageURL: "quay.io/podified-antelope-centos9/edpm-hardened-uefi@latest", + OSContainerImageType: "self-extracting", + ApacheImageURL: "registry.redhat.io/rhel8/httpd-24@latest", + AgentImageURL: "quay.io/openstack-k8s-operators/openstack-baremetal-operator-agent@latest", + NodeSelector: nil, + Resources: corev1.ResourceRequirements{Limits: nil, Requests: nil, Claims: nil}, } Expect(provServer.Spec).Should(Equal(spec)) })