diff --git a/api/v1alpha1/osartifact_types.go b/api/v1alpha1/osartifact_types.go index 34f18c1..72442bb 100644 --- a/api/v1alpha1/osartifact_types.go +++ b/api/v1alpha1/osartifact_types.go @@ -35,7 +35,30 @@ type OSArtifactSpec struct { CloudConfig string `json:"cloudConfig,omitempty"` GRUBConfig string `json:"grubConfig,omitempty"` - PullFromKube bool `json:"pullFromKube,omitempty"` + Bundles []string `json:"bundles,omitempty"` + PullOptions *Pull `json:"pull,omitempty"` + PushOptions *Push `json:"push,omitempty"` +} + +type Push struct { + Push bool `json:"push,omitempty"` + ImageName string `json:"imageName,omitempty"` + ContainerRegistryCredentials *SecretKeySelector `json:"containerRegistryCredentials,omitempty"` +} + +type Pull struct { + ContainerRegistryCredentials *SecretKeySelector `json:"containerRegistryCredentials,omitempty"` +} +type LocalObjectReference struct { + Name string `json:"name"` +} + +type SecretKeySelector struct { + LocalObjectReference `json:",inline"` + // +optional + Namespace string `json:"namespace,omitempty"` + // +optional + Key string `json:"key,omitempty"` } // OSArtifactStatus defines the observed state of OSArtifact diff --git a/config/crd/bases/build.c3os-x.io_osartifacts.yaml b/config/crd/bases/build.c3os-x.io_osartifacts.yaml index 5a1199d..7829130 100644 --- a/config/crd/bases/build.c3os-x.io_osartifacts.yaml +++ b/config/crd/bases/build.c3os-x.io_osartifacts.yaml @@ -36,6 +36,10 @@ spec: description: OSArtifactSpec defines the desired state of OSArtifact properties: cloudConfig: + description: 'TODO: treat cloudconfig as a secret, and take a secretRef + where to store it (optionally)' + type: string + grubConfig: type: string imageName: description: Foo is an example field of OSArtifact. Edit osartifact_types.go @@ -43,6 +47,8 @@ spec: type: string iso: type: boolean + pullFromKube: + type: boolean type: object status: description: OSArtifactStatus defines the observed state of OSArtifact diff --git a/controllers/deployment.go b/controllers/deployment.go index 0237f35..4221261 100644 --- a/controllers/deployment.go +++ b/controllers/deployment.go @@ -31,6 +31,78 @@ func genDeploymentLabel(s string) map[string]string { "osbuild": "workload" + s, } } + +// TODO: Handle registry auth +// TODO: This shells out, but needs ENV_VAR with key refs mapping +func unpackContainer(containerImage, pullImage string, pullOptions buildv1alpha1.Pull) v1.Container { + return v1.Container{ + ImagePullPolicy: v1.PullAlways, + Name: "pull-image", + Image: containerImage, + Command: []string{"/bin/bash", "-cxe"}, + Args: []string{ + fmt.Sprintf( + "luet util unpack %s %s", + pullImage, + "/rootfs", + ), + }, + VolumeMounts: []v1.VolumeMount{ + { + Name: "rootfs", + MountPath: "/rootfs", + }, + }, + } +} + +func createImageContainer(containerImage string, pushOptions buildv1alpha1.Push) v1.Container { + return v1.Container{ + ImagePullPolicy: v1.PullAlways, + Name: "create-image", + Image: containerImage, + Command: []string{"/bin/bash", "-cxe"}, + Args: []string{ + fmt.Sprintf( + "tar -czvpf test.tar -C /rootfs . && luet util pack %s test.tar image.tar && mv image.tar /public", + pushOptions.ImageName, + ), + }, + VolumeMounts: []v1.VolumeMount{ + { + Name: "rootfs", + MountPath: "/rootfs", + }, + { + Name: "public", + MountPath: "/public", + }, + }, + } +} + +func pushImageContainer(containerImage string, pushOptions buildv1alpha1.Push) v1.Container { + return v1.Container{ + ImagePullPolicy: v1.PullAlways, + Name: "push-image", + Image: containerImage, + Command: []string{"/bin/bash", "-cxe"}, + Args: []string{ + fmt.Sprintf( + "skopeo /public/image.tar %s", + pushOptions.ImageName, + ), + }, + VolumeMounts: []v1.VolumeMount{ + + { + Name: "public", + MountPath: "/public", + }, + }, + } +} + func (r *OSArtifactReconciler) genDeployment(artifact buildv1alpha1.OSArtifact) *appsv1.Deployment { objMeta := metav1.ObjectMeta{ Name: artifact.Name, @@ -40,6 +112,7 @@ func (r *OSArtifactReconciler) genDeployment(artifact buildv1alpha1.OSArtifact) privileged := false serviceAccount := false + buildIsoContainer := v1.Container{ ImagePullPolicy: v1.PullAlways, SecurityContext: &v1.SecurityContext{Privileged: &privileged}, @@ -48,9 +121,8 @@ func (r *OSArtifactReconciler) genDeployment(artifact buildv1alpha1.OSArtifact) Command: []string{"/bin/bash", "-cxe"}, Args: []string{ fmt.Sprintf( - "elemental --debug --name %s build-iso --date=false --overlay-iso /iso/iso-overlay %s --output /public", + "elemental --debug --name %s build-iso --date=false --overlay-iso /iso/iso-overlay --output /public dir:/rootfs", artifact.Name, - artifact.Spec.ImageName, ), }, VolumeMounts: []v1.VolumeMount{ @@ -68,31 +140,6 @@ func (r *OSArtifactReconciler) genDeployment(artifact buildv1alpha1.OSArtifact) MountPath: "/iso/iso-overlay/boot/grub2/grub.cfg", SubPath: "grub.cfg", }, - }, - } - - if artifact.Spec.PullFromKube { - buildIsoContainer.Args = []string{ - fmt.Sprintf( - "elemental --debug --name %s build-iso --date=false --overlay-iso /iso/iso-overlay --output /public /rootfs", - artifact.Name, - ), - } - } - - pullContainer := v1.Container{ - ImagePullPolicy: v1.PullAlways, - Name: "build-iso", - Image: artifact.Spec.ImageName, - Command: []string{"/bin/bash", "-cxe"}, - Args: []string{ - fmt.Sprintf( - "rsync -aqAX --exclude='mnt' --exclude='proc' --exclude='sys' --exclude='dev' --exclude='tmp' %s %s", - "/", - "/rootfs", - ), - }, - VolumeMounts: []v1.VolumeMount{ { Name: "rootfs", MountPath: "/rootfs", @@ -134,11 +181,14 @@ func (r *OSArtifactReconciler) genDeployment(artifact buildv1alpha1.OSArtifact) }, } - pod.InitContainers = []v1.Container{buildIsoContainer} - if artifact.Spec.PullFromKube { - // pull first - pod.InitContainers = append([]v1.Container{pullContainer}, pod.InitContainers...) + pod.InitContainers = []v1.Container{buildIsoContainer, unpackContainer(r.ToolImage, artifact.Spec.ImageName, *artifact.Spec.PullOptions)} + + for _, bundle := range artifact.Spec.Bundles { + pod.InitContainers = append(pod.InitContainers, unpackContainer(r.ToolImage, bundle, *artifact.Spec.PullOptions)) } + + pod.InitContainers = append(pod.InitContainers, buildIsoContainer) + pod.Containers = []v1.Container{servingContainer} deploymentLabels := genDeploymentLabel(artifact.Name) diff --git a/controllers/osartifact_controller.go b/controllers/osartifact_controller.go index cbcad05..09f4449 100644 --- a/controllers/osartifact_controller.go +++ b/controllers/osartifact_controller.go @@ -38,9 +38,9 @@ import ( // OSArtifactReconciler reconciles a OSArtifact object type OSArtifactReconciler struct { client.Client - Scheme *runtime.Scheme - clientSet *kubernetes.Clientset - ServingImage, BuildImage string + Scheme *runtime.Scheme + clientSet *kubernetes.Clientset + ServingImage, BuildImage, ToolImage string } func genOwner(artifact buildv1alpha1.OSArtifact) []metav1.OwnerReference { diff --git a/main.go b/main.go index b8f4820..7e3a373 100644 --- a/main.go +++ b/main.go @@ -52,10 +52,11 @@ func main() { var metricsAddr string var enableLeaderElection bool var probeAddr string - var buildImage, serveImage string + var buildImage, serveImage, toolImage string flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") - flag.StringVar(&buildImage, "build-image", "quay.io/costoolkit/elemental-cli:v0.0.15-8a78e6b", "Build image.") + flag.StringVar(&buildImage, "build-image", "quay.io/costoolkit/elemental-cli:v0.0.15-ae4f000", "Build image.") flag.StringVar(&serveImage, "serve-image", "nginx", "Serve image.") + flag.StringVar(&toolImage, "tool-image", "quay.io/costoolkit/elemental-cli:v0.0.15-ae4f000", "Tool image.") flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") flag.BoolVar(&enableLeaderElection, "leader-elect", false, @@ -96,6 +97,7 @@ func main() { if err = (&controllers.OSArtifactReconciler{ Client: mgr.GetClient(), ServingImage: serveImage, + ToolImage: toolImage, BuildImage: buildImage, Scheme: mgr.GetScheme(), }).SetupWithManager(mgr); err != nil {