From 8fbd9e4ee8a4ee78c70ed4635e22abfe9baadb45 Mon Sep 17 00:00:00 2001 From: Sandhya Dasu Date: Wed, 17 Jul 2019 12:22:23 -0400 Subject: [PATCH] WIP: Integrate baremetal-operator with machine-api-operator --- ...hine-api-operator_01_images.configmap.yaml | 8 +- ...ine-api-operator_12_baremetalhost.crd.yaml | 47 +++ pkg/operator/baremetal_pod.go | 397 ++++++++++++++++++ pkg/operator/config.go | 34 +- pkg/operator/config_test.go | 30 +- pkg/operator/fixtures/images.json | 8 +- pkg/operator/operator.go | 10 + pkg/operator/operator_test.go | 8 + pkg/operator/sync.go | 33 +- 9 files changed, 567 insertions(+), 8 deletions(-) create mode 100644 install/0000_30_machine-api-operator_12_baremetalhost.crd.yaml create mode 100644 pkg/operator/baremetal_pod.go diff --git a/install/0000_30_machine-api-operator_01_images.configmap.yaml b/install/0000_30_machine-api-operator_01_images.configmap.yaml index 63d62f871e..9da029f6a3 100644 --- a/install/0000_30_machine-api-operator_01_images.configmap.yaml +++ b/install/0000_30_machine-api-operator_01_images.configmap.yaml @@ -12,5 +12,11 @@ data: "clusterAPIControllerLibvirt": "docker.io/openshift/origin-libvirt-machine-controllers:v4.0.0", "clusterAPIControllerBareMetal": "quay.io/openshift/origin-baremetal-machine-controllers:v4.0.0", "clusterAPIControllerAzure": "quay.io/openshift/origin-azure-machine-controllers:v4.0.0", - "clusterAPIControllerGCP": "quay.io/openshift/origin-gcp-machine-controllers:v4.0.0" + "clusterAPIControllerGCP": "quay.io/openshift/origin-gcp-machine-controllers:v4.0.0", + "baremetalOperator": "quay.io/openshift/origin-baremetal-operator:v4.2.0", + "baremetalIronic": "quay.io/openshift/origin-ironic-image:v4.2.0", + "baremetalIronicInspector": "quay.io/openshift/origin-ironic-inspector-image:v4.2.0", + "baremetalIpaDownloader": "quay.io/openshift/origin-ironic-ipa-downloader:v4.2.0", + "baremetalRhcosDownloader": "quay.io/openshift/origin-ironic-rhcos-downloader:v4.2.0", + "baremetalStaticIpManager": "quay.io/openshift/origin-ironic-static-ip-manager:v4.2.0" } diff --git a/install/0000_30_machine-api-operator_12_baremetalhost.crd.yaml b/install/0000_30_machine-api-operator_12_baremetalhost.crd.yaml new file mode 100644 index 0000000000..0a7697d9c5 --- /dev/null +++ b/install/0000_30_machine-api-operator_12_baremetalhost.crd.yaml @@ -0,0 +1,47 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: baremetalhosts.metal3.io +spec: + group: metal3.io + names: + kind: BareMetalHost + listKind: BareMetalHostList + plural: baremetalhosts + singular: baremetalhost + shortNames: + - bmh + - bmhost + scope: Namespaced + version: v1alpha1 + subresources: + status: {} + additionalPrinterColumns: + - JSONPath: .status.operationalStatus + description: Operational status + name: Status + type: string + - JSONPath: .status.provisioning.state + description: Provisioning status + name: Provisioning Status + type: string + - JSONPath: .spec.machineRef.name + description: Machine using this host + name: Machine + type: string + - JSONPath: .spec.bmc.address + description: Address of management controler + name: BMC + type: string + - JSONPath: .status.hardwareProfile + description: The type of hardware detected + name: Hardware Profile + type: string + - JSONPath: .spec.online + description: Whether the host is online or not + name: Online + type: string + - JSONPath: .status.errorMessage + description: Most recent error + name: Error + type: string diff --git a/pkg/operator/baremetal_pod.go b/pkg/operator/baremetal_pod.go new file mode 100644 index 0000000000..9df7cad739 --- /dev/null +++ b/pkg/operator/baremetal_pod.go @@ -0,0 +1,397 @@ +package operator + +import ( + "math/rand" + "time" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/pointer" +) + +var baremetalConfigmap = "metal3-config" +var volumeMounts = []corev1.VolumeMount{ + { + Name: "metal3-shared", + MountPath: "/shared", + }, +} + +func setEnvVar(name string, key string) corev1.EnvVar { + return corev1.EnvVar{ + Name: name, + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: baremetalConfigmap, + }, + Key: key, + }, + }, + } +} + +var mariadbPassword = generateRandomPassword() + +func generateRandomPassword() string { + rand.Seed(time.Now().UnixNano()) + chars := []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + "abcdefghijklmnopqrstuvwxyz" + + "0123456789" + + "~!@#$%^&*()") + length := 8 + buf := make([]rune, length) + for i := range buf { + buf[i] = chars[rand.Intn(len(chars))] + } + return string(buf) +} + +func setMariadbPassword() corev1.EnvVar { + return corev1.EnvVar{ + Name: "MARIADB_PASSWORD", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "mariadb-password", + }, + Key: mariadbPassword, + }, + }, + } +} + +func newMetal3Deployment(config *OperatorConfig) *appsv1.Deployment { + replicas := int32(1) + template := newMetal3PodTemplateSpec(config) + + return &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "metal3", + Namespace: config.TargetNamespace, + Labels: map[string]string{ + "api": "clusterapi", + "k8s-app": "controller", + }, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "api": "clusterapi", + "k8s-app": "controller", + }, + }, + Template: *template, + }, + } +} + +func newMetal3PodTemplateSpec(config *OperatorConfig) *corev1.PodTemplateSpec { + initContainers := newMetal3InitContainers(config) + containers := newMetal3Containers(config) + tolerations := []corev1.Toleration{ + { + Key: "node-role.kubernetes.io/master", + Effect: corev1.TaintEffectNoSchedule, + }, + { + Key: "CriticalAddonsOnly", + Operator: corev1.TolerationOpExists, + }, + { + Key: "node.kubernetes.io/not-ready", + Effect: corev1.TaintEffectNoExecute, + Operator: corev1.TolerationOpExists, + TolerationSeconds: pointer.Int64Ptr(120), + }, + { + Key: "node.kubernetes.io/unreachable", + Effect: corev1.TaintEffectNoExecute, + Operator: corev1.TolerationOpExists, + TolerationSeconds: pointer.Int64Ptr(120), + }, + } + + return &corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "api": "clusterapi", + "k8s-app": "controller", + }, + }, + Spec: corev1.PodSpec{ + InitContainers: initContainers, + Containers: containers, + PriorityClassName: "system-node-critical", + NodeSelector: map[string]string{"node-role.kubernetes.io/master": ""}, + SecurityContext: &corev1.PodSecurityContext{ + RunAsNonRoot: pointer.BoolPtr(false), + }, + ServiceAccountName: "metal3-controller", + Tolerations: tolerations, + }, + } +} + +func newMetal3InitContainers(config *OperatorConfig) []corev1.Container { + initContainers := []corev1.Container{ + { + Name: "metal3-ipa-downloader", + Image: config.BaremetalControllers.IronicIpaDownloader, + Command: []string{"/usr/local/bin/get-resource.sh"}, + ImagePullPolicy: "Always", + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(true), + }, + VolumeMounts: volumeMounts, + Env: []corev1.EnvVar{ + setEnvVar("CACHEURL", "cache_url"), + }, + }, + } + initContainers = append(initContainers, createInitContainerRhcosDownloader(config)) + initContainers = append(initContainers, createInitContainerStaticIpSet(config)) + return initContainers +} + +func createInitContainerRhcosDownloader(config *OperatorConfig) corev1.Container { + initContainer := corev1.Container{ + Name: "metal3-rhcos-downloader", + Image: config.BaremetalControllers.IronicRhcosDownloader, + Command: []string{"/usr/local/bin/get-resource.sh"}, + ImagePullPolicy: "Always", + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(true), + }, + VolumeMounts: volumeMounts, + Env: []corev1.EnvVar{ + setEnvVar("RHCOS_IMAGE_URL", "rhcos_image_url"), + setEnvVar("CACHEURL", "cache_url"), + }, + } + return initContainer +} + +func createInitContainerStaticIpSet(config *OperatorConfig) corev1.Container { + initContainer := corev1.Container{ + Name: "metal3-static-ip-set", + Image: config.BaremetalControllers.IronicStaticIpManager, + Command: []string{"/set-static-ip"}, + ImagePullPolicy: "Always", + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(true), + }, + Env: []corev1.EnvVar{ + setEnvVar("PROVISIONING_IP", "provisioning_ip"), + setEnvVar("PROVISIONING_INTERFACE", "provisioning_interface"), + }, + } + return initContainer +} + +func newMetal3Containers(config *OperatorConfig) []corev1.Container { + //Starting off with the metal3-baremetal-operator container + containers := []corev1.Container{ + { + Name: "metal3-baremetal-operator", + Image: config.BaremetalControllers.BaremetalOperator, + Ports: []corev1.ContainerPort{ + { + Name: "metrics", + ContainerPort: 60000, + }, + }, + Command: []string{"/baremetal-operator"}, + ImagePullPolicy: "Always", + Env: []corev1.EnvVar{ + { + Name: "WATCH_NAMESPACE", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.namespace", + }, + }, + }, + { + Name: "POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OPERATOR_NAME", + Value: "baremetal-operator", + }, + setEnvVar("DEPLOY_KERNEL_URL", "deploy_kernel_url"), + setEnvVar("DEPLOY_RAMDISK_URL", "deploy_ramdisk_url"), + setEnvVar("IRONIC_ENDPOINT", "ironic_endpoint"), + setEnvVar("IRONIC_INSPECTOR_ENDPOINT", "ironic_inspector_endpoint"), + }, + }, + } + containers = append(containers, createContainerMetal3Dnsmasq(config)) + containers = append(containers, createContainerMetal3Mariadb(config)) + containers = append(containers, createContainerMetal3Httpd(config)) + containers = append(containers, createContainerMetal3IronicConductor(config)) + containers = append(containers, createContainerMetal3IronicApi(config)) + containers = append(containers, createContainerMetal3IronicExporter(config)) + containers = append(containers, createContainerMetal3IronicInspector(config)) + containers = append(containers, createContainerMetal3StaticIpManager(config)) + return containers +} + +func createContainerMetal3Dnsmasq(config *OperatorConfig) corev1.Container { + + container := corev1.Container{ + Name: "metal3-dnsmasq", + Image: config.BaremetalControllers.Ironic, + ImagePullPolicy: "Always", + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(true), + }, + Command: []string{"/bin/rundnsmasq"}, + VolumeMounts: volumeMounts, + Env: []corev1.EnvVar{ + setEnvVar("HTTP_PORT", "http_port"), + setEnvVar("PROVISIONING_INTERFACE", "provisioning_interface"), + setEnvVar("DHCP_RANGE", "dhcp_range"), + }, + } + return container +} + +func createContainerMetal3Mariadb(config *OperatorConfig) corev1.Container { + + container := corev1.Container{ + Name: "metal3-mariadb", + Image: config.BaremetalControllers.Ironic, + ImagePullPolicy: "Always", + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(true), + }, + Command: []string{"/bin/runmariadb"}, + VolumeMounts: volumeMounts, + Env: []corev1.EnvVar{ + setMariadbPassword(), + }, + } + return container +} + +func createContainerMetal3Httpd(config *OperatorConfig) corev1.Container { + + container := corev1.Container{ + Name: "metal3-httpd", + Image: config.BaremetalControllers.Ironic, + ImagePullPolicy: "Always", + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(true), + }, + Command: []string{"/bin/runhttpd"}, + VolumeMounts: volumeMounts, + Env: []corev1.EnvVar{ + setEnvVar("HTTP_PORT", "http_port"), + setEnvVar("PROVISIONING_INTERFACE", "provisioning_interface"), + }, + } + return container +} + +func createContainerMetal3IronicConductor(config *OperatorConfig) corev1.Container { + + container := corev1.Container{ + Name: "metal3-ironic-conductor", + Image: config.BaremetalControllers.Ironic, + ImagePullPolicy: "Always", + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(true), + }, + Command: []string{"/bin/runironic-conductor"}, + VolumeMounts: volumeMounts, + Env: []corev1.EnvVar{ + setMariadbPassword(), + setEnvVar("HTTP_PORT", "http_port"), + setEnvVar("PROVISIONING_INTERFACE", "provisioning_interface"), + }, + } + return container +} + +func createContainerMetal3IronicApi(config *OperatorConfig) corev1.Container { + + container := corev1.Container{ + Name: "metal3-ironic-api", + Image: config.BaremetalControllers.Ironic, + ImagePullPolicy: "Always", + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(true), + }, + Command: []string{"/bin/runironic-api"}, + VolumeMounts: volumeMounts, + Env: []corev1.EnvVar{ + setMariadbPassword(), + setEnvVar("HTTP_PORT", "http_port"), + setEnvVar("PROVISIONING_INTERFACE", "provisioning_interface"), + }, + } + return container +} + +func createContainerMetal3IronicExporter(config *OperatorConfig) corev1.Container { + + container := corev1.Container{ + Name: "metal3-ironic-exporter", + Image: config.BaremetalControllers.Ironic, + ImagePullPolicy: "Always", + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(true), + }, + Command: []string{"/bin/runironic-exporter"}, + VolumeMounts: volumeMounts, + Env: []corev1.EnvVar{ + setMariadbPassword(), + setEnvVar("HTTP_PORT", "http_port"), + setEnvVar("PROVISIONING_INTERFACE", "provisioning_interface"), + }, + } + return container +} + +func createContainerMetal3IronicInspector(config *OperatorConfig) corev1.Container { + + container := corev1.Container{ + Name: "metal3-ironic-inspector", + Image: config.BaremetalControllers.IronicInspector, + ImagePullPolicy: "Always", + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(true), + }, + VolumeMounts: volumeMounts, + Env: []corev1.EnvVar{ + setEnvVar("PROVISIONING_INTERFACE", "provisioning_interface"), + }, + } + return container +} + +func createContainerMetal3StaticIpManager(config *OperatorConfig) corev1.Container { + + container := corev1.Container{ + Name: "metal3-static-ip-manager", + Image: config.BaremetalControllers.IronicStaticIpManager, + Command: []string{"/bin/refresh-static-ip"}, + ImagePullPolicy: "Always", + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(true), + }, + Env: []corev1.EnvVar{ + setEnvVar("PROVISIONING_IP", "provisioning_ip"), + setEnvVar("PROVISIONING_INTERFACE", "provisioning_interface"), + }, + } + return container +} diff --git a/pkg/operator/config.go b/pkg/operator/config.go index 4adc83af78..00cb36a845 100644 --- a/pkg/operator/config.go +++ b/pkg/operator/config.go @@ -20,8 +20,9 @@ type Provider string // OperatorConfig contains configuration for MAO type OperatorConfig struct { - TargetNamespace string `json:"targetNamespace"` - Controllers Controllers + TargetNamespace string `json:"targetNamespace"` + Controllers Controllers + BaremetalControllers BaremetalControllers } type Controllers struct { @@ -30,6 +31,15 @@ type Controllers struct { MachineHealthCheck string } +type BaremetalControllers struct { + BaremetalOperator string + Ironic string + IronicInspector string + IronicIpaDownloader string + IronicRhcosDownloader string + IronicStaticIpManager string +} + // Images allows build systems to inject images for MAO components type Images struct { MachineAPIOperator string `json:"machineAPIOperator"` @@ -39,6 +49,13 @@ type Images struct { ClusterAPIControllerBareMetal string `json:"clusterAPIControllerBareMetal"` ClusterAPIControllerAzure string `json:"clusterAPIControllerAzure"` ClusterAPIControllerGCP string `json:"clusterAPIControllerGCP"` + // Images required for the metal3 pod + BaremetalOperator string `json:"baremetalOperator"` + BaremetalIronic string `json:"baremetalIronic"` + BaremetalIronicInspector string `json:"baremetalIronicInspector"` + BaremetalIpaDownloader string `json:"baremetalIpaDownloader"` + BaremetalRhcosDownloader string `json:"baremetalRhcosDownloader"` + BaremetalStaticIpManager string `json:"baremetalStaticIpManager"` } func getProviderFromInfrastructure(infra *configv1.Infrastructure) (configv1.PlatformType, error) { @@ -82,6 +99,19 @@ func getProviderControllerFromImages(platform configv1.PlatformType, images Imag } } +// This function returns images required to bring up the Baremetal Pod. +func getBaremetalControllers(images Images) (BaremetalControllers, error) { + var baremetalImages BaremetalControllers + + baremetalImages.BaremetalOperator = images.BaremetalOperator + baremetalImages.Ironic = images.BaremetalIronic + baremetalImages.IronicInspector = images.BaremetalIronicInspector + baremetalImages.IronicIpaDownloader = images.BaremetalIpaDownloader + baremetalImages.IronicRhcosDownloader = images.BaremetalRhcosDownloader + baremetalImages.IronicStaticIpManager = images.BaremetalStaticIpManager + return baremetalImages, nil +} + func getMachineAPIOperatorFromImages(images Images) (string, error) { if images.MachineAPIOperator == "" { return "", fmt.Errorf("failed gettingMachineAPIOperator image. It is empty") diff --git a/pkg/operator/config_test.go b/pkg/operator/config_test.go index aea01632e9..252a400720 100644 --- a/pkg/operator/config_test.go +++ b/pkg/operator/config_test.go @@ -15,6 +15,12 @@ var ( expectedBareMetalImage = "quay.io/openshift/origin-baremetal-machine-controllers:v4.0.0" expectedAzureImage = "quay.io/openshift/origin-azure-machine-controllers:v4.0.0" expectedGCPImage = "quay.io/openshift/origin-gcp-machine-controllers:v4.0.0" + expectedBaremetalOperator = "quay.io/openshift/origin-baremetal-operator:v4.2.0" + expectedIronic = "quay.io/openshift/origin-ironic-image:v4.2.0" + expectedIronicInspector = "quay.io/openshift/origin-ironic-inspector-image:v4.2.0" + expectedIronicIpaDownloader = "quay.io/openshift/origin-ironic-ipa-downloader:v4.2.0" + expectedIronicRhcosDownloader = "quay.io/openshift/origin-ironic-rhcos-downloader:v4.2.0" + expectedIronicStaticIpManager = "quay.io/openshift/origin-ironic-static-ip-manager:v4.2.0" ) func TestGetProviderFromInfrastructure(t *testing.T) { @@ -118,7 +124,7 @@ func TestGetImagesFromJSONFile(t *testing.T) { t.Errorf("failed getImagesFromJSONFile. Expected: %s, got: %s", expectedAzureImage, img.ClusterAPIControllerAzure) } if img.ClusterAPIControllerGCP != expectedGCPImage { - t.Errorf("failed getImagesFromJSONFile. Expected: %s, got: %s", expectedAzureImage, img.ClusterAPIControllerAzure) + t.Errorf("failed getImagesFromJSONFile. Expected: %s, got: %s", expectedGCPImage, img.ClusterAPIControllerGCP) } } @@ -196,3 +202,25 @@ func TestGetMachineAPIOperatorFromImages(t *testing.T) { t.Errorf("failed getMachineAPIOperatorFromImages. Expected: %s, got: %s", expectedMachineAPIOperatorImage, res) } } + +func TestGetBaremetalControllers(t *testing.T) { + imagesJSONFile := "fixtures/images.json" + img, err := getImagesFromJSONFile(imagesJSONFile) + if err != nil { + t.Errorf("failed getImagesFromJSONFile, %v", err) + } + + baremetalControllers, err := getBaremetalControllers(*img) + if err != nil { + t.Errorf("failed getBaremetalControllers: %v", err) + } + + if baremetalControllers.BaremetalOperator != expectedBaremetalOperator || + baremetalControllers.Ironic != expectedIronic || + baremetalControllers.IronicInspector != expectedIronicInspector || + baremetalControllers.IronicIpaDownloader != expectedIronicIpaDownloader || + baremetalControllers.IronicRhcosDownloader != expectedIronicRhcosDownloader || + baremetalControllers.IronicStaticIpManager != expectedIronicStaticIpManager { + t.Errorf("failed getAdditionalProviderImages. One or more BaremetalController images do not match the expected images.") + } +} diff --git a/pkg/operator/fixtures/images.json b/pkg/operator/fixtures/images.json index 5604326fc8..a84eccc01b 100644 --- a/pkg/operator/fixtures/images.json +++ b/pkg/operator/fixtures/images.json @@ -5,5 +5,11 @@ "machineAPIOperator": "docker.io/openshift/origin-machine-api-operator:v4.0.0", "clusterAPIControllerBareMetal": "quay.io/openshift/origin-baremetal-machine-controllers:v4.0.0", "clusterAPIControllerAzure": "quay.io/openshift/origin-azure-machine-controllers:v4.0.0", - "clusterAPIControllerGCP": "quay.io/openshift/origin-gcp-machine-controllers:v4.0.0" + "clusterAPIControllerGCP": "quay.io/openshift/origin-gcp-machine-controllers:v4.0.0", + "baremetalOperator": "quay.io/openshift/origin-baremetal-operator:v4.2.0", + "baremetalIronic": "quay.io/openshift/origin-ironic-image:v4.2.0", + "baremetalIronicInspector": "quay.io/openshift/origin-ironic-inspector-image:v4.2.0", + "baremetalIpaDownloader": "quay.io/openshift/origin-ironic-ipa-downloader:v4.2.0", + "baremetalRhcosDownloader": "quay.io/openshift/origin-ironic-rhcos-downloader:v4.2.0", + "baremetalStaticIpManager": "quay.io/openshift/origin-ironic-static-ip-manager:v4.2.0" } diff --git a/pkg/operator/operator.go b/pkg/operator/operator.go index b4cc52fb1c..0124ac7d40 100644 --- a/pkg/operator/operator.go +++ b/pkg/operator/operator.go @@ -186,6 +186,8 @@ func (optr *Operator) sync(key string) error { } func (optr *Operator) maoConfigFromInfrastructure() (*OperatorConfig, error) { + baremetalControllers := BaremetalControllers{} + infra, err := optr.osClient.ConfigV1().Infrastructures().Get("cluster", metav1.GetOptions{}) if err != nil { return nil, err @@ -206,6 +208,13 @@ func (optr *Operator) maoConfigFromInfrastructure() (*OperatorConfig, error) { return nil, err } + if provider == osconfigv1.BareMetalPlatformType { + baremetalControllers, err = getBaremetalControllers(*images) + if err != nil { + return nil, err + } + } + machineAPIOperatorImage, err := getMachineAPIOperatorFromImages(*images) if err != nil { return nil, err @@ -218,5 +227,6 @@ func (optr *Operator) maoConfigFromInfrastructure() (*OperatorConfig, error) { NodeLink: machineAPIOperatorImage, MachineHealthCheck: machineAPIOperatorImage, }, + BaremetalControllers: baremetalControllers, }, nil } diff --git a/pkg/operator/operator_test.go b/pkg/operator/operator_test.go index 8ec89b2b5b..d2946ee812 100644 --- a/pkg/operator/operator_test.go +++ b/pkg/operator/operator_test.go @@ -38,6 +38,8 @@ func newFeatureGate(featureSet v1.FeatureSet) *v1.FeatureGate { } func newOperatorConfig() *OperatorConfig { + baremetalControllers := BaremetalControllers{} + return &OperatorConfig{ targetNamespace, Controllers{ @@ -45,6 +47,7 @@ func newOperatorConfig() *OperatorConfig { "docker.io/openshift/origin-machine-api-operator:v4.0.0", "docker.io/openshift/origin-machine-api-operator:v4.0.0", }, + baremetalControllers, } } @@ -106,6 +109,10 @@ func TestOperatorSync_NoOp(t *testing.T) { platform: v1.BareMetalPlatformType, expectedNoop: false, }, + { + platform: v1.GCPPlatformType, + expectedNoop: false, + }, { platform: kubemarkPlatform, expectedNoop: false, @@ -123,6 +130,7 @@ func TestOperatorSync_NoOp(t *testing.T) { expectedNoop: true, }, } + for _, tc := range cases { t.Run(string(tc.platform), func(t *testing.T) { infra := &v1.Infrastructure{ diff --git a/pkg/operator/sync.go b/pkg/operator/sync.go index 35d3be7c43..3ceb3cfb9f 100644 --- a/pkg/operator/sync.go +++ b/pkg/operator/sync.go @@ -34,17 +34,31 @@ func (optr *Operator) syncAll(config *OperatorConfig) error { // return the outer error. glog.Errorf("Error syncing ClusterOperatorStatus: %v", err) } - glog.Errorf("Error syncing cluster api controller: %v", err) + glog.Errorf("Error syncing machine-api-controller: %v", err) return err } - glog.V(3).Info("Synced up all components") + glog.V(3).Info("Synced up all machine-api-controller components") + } + + // In addition, if the Provider is BareMetal, then bring up + // the baremetal-operator pod + if config.BaremetalControllers.BaremetalOperator != "" { + if err := optr.syncBaremetalControllers(config); err != nil { + if err := optr.statusDegraded(err.Error()); err != nil { + // Just log the error here. We still want to + // return the outer error. + glog.Errorf("Error syncing BaremetalOperatorStatus: %v", err) + } + glog.Errorf("Error syncing metal3-controller: %v", err) + return err + } + glog.V(3).Info("Synced up all metal3 components") } if err := optr.statusAvailable(); err != nil { glog.Errorf("Error syncing ClusterOperatorStatus: %v", err) return fmt.Errorf("error syncing ClusterOperatorStatus: %v", err) } - return nil } @@ -79,6 +93,19 @@ func (optr *Operator) syncClusterAPIController(config *OperatorConfig) error { return nil } +func (optr *Operator) syncBaremetalControllers(config *OperatorConfig) error { + metal3Deployment := newMetal3Deployment(config) + _, updated, err := resourceapply.ApplyDeployment(optr.kubeClient.AppsV1(), metal3Deployment) + if err != nil { + return err + } + if updated { + return optr.waitForDeploymentRollout(metal3Deployment) + } + + return nil +} + func (optr *Operator) waitForDeploymentRollout(resource *appsv1.Deployment) error { return wait.Poll(deploymentRolloutPollInterval, deploymentRolloutTimeout, func() (bool, error) { d, err := optr.deployLister.Deployments(resource.Namespace).Get(resource.Name)