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 ffd0162211..ee55e63097 100644 --- a/install/0000_30_machine-api-operator_01_images.configmap.yaml +++ b/install/0000_30_machine-api-operator_01_images.configmap.yaml @@ -19,11 +19,5 @@ data: "clusterAPIControllerGCP": "registry.svc.ci.openshift.org/openshift:gcp-machine-controllers", "clusterAPIControllerOvirt": "registry.svc.ci.openshift.org/openshift:ovirt-machine-controllers", "clusterAPIControllerKubevirt": "registry.svc.ci.openshift.org/openshift:kubevirt-machine-controllers", - "clusterAPIControllerVSphere": "registry.svc.ci.openshift.org/openshift:machine-api-operator", - "baremetalOperator": "registry.svc.ci.openshift.org/openshift:baremetal-operator", - "baremetalIronic": "registry.svc.ci.openshift.org/openshift:ironic", - "baremetalIronicInspector": "registry.svc.ci.openshift.org/openshift:ironic-inspector", - "baremetalIpaDownloader": "registry.svc.ci.openshift.org/openshift:ironic-ipa-downloader", - "baremetalMachineOsDownloader": "registry.svc.ci.openshift.org/openshift:ironic-machine-os-downloader", - "baremetalStaticIpManager": "registry.svc.ci.openshift.org/openshift:ironic-static-ip-manager" + "clusterAPIControllerVSphere": "registry.svc.ci.openshift.org/openshift:machine-api-operator" } diff --git a/install/0000_30_machine-api-operator_04_metal3provisioning.crd.yaml b/install/0000_30_machine-api-operator_04_metal3provisioning.crd.yaml deleted file mode 100644 index 8d09e53733..0000000000 --- a/install/0000_30_machine-api-operator_04_metal3provisioning.crd.yaml +++ /dev/null @@ -1,173 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: provisionings.metal3.io - annotations: - include.release.openshift.io/self-managed-high-availability: "true" -spec: - group: metal3.io - names: - kind: Provisioning - listKind: ProvisioningList - plural: provisionings - singular: provisioning - scope: Cluster - subresources: - status: {} - validation: - openAPIV3Schema: - description: 'Provisioning contains configuration used by the Provisioning - service (Ironic) to provision baremetal hosts. \n Provisioning is created - by the Openshift installer using admin or user provided information about - the provisioning network and the NIC on the server that can be used to PXE - boot it. \n This CR is a singleton, created by the installer and currently - only consumed by the machine-api-operator to bring up and update containers - in a metal3 cluster.' - type: object - required: - - spec - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Specification of provisioning configuration for Metal3. - type: object - properties: - provisioningInterface: - description: provisioningInterface is the name of the network interface - on a baremetal server to the provisioning network. It can have values - like eth1 or ens3. - type: string - provisioningIP: - description: provisioningIP is the IP address assigned to the provisioningInterface - of the baremetal server. This IP address should be within the provisioning - subnet, and outside of the DHCP range. - type: string - provisioningNetworkCIDR: - description: provisioningNetworkCIDR is the network on which the baremetal - nodes are provisioned. The provisioningIP and the IPs in the dhcpRange - all come from within this network. - type: string - provisioningDHCPExternal: - description: provisioningDHCPExternal indicates whether the DHCP server for IP - addresses in the provisioning DHCP range is present within the metal3 cluster - or external to it. This field is being deprecated in favor of provisioningNetwork. - type: boolean - provisioningDHCPRange: - description: Needs to be interpreted along with provisioningDHCPExternal or - provisioningNetwork. If the value of provisioningDHCPExternal is set to False, - provisioningDHCPRange represents the range of IP addresses that the DHCP server - running within the metal3 cluster can use while provisioning baremetal servers. - If the value of provisioningDHCPExternal is set to True, then the value of - provisioningDHCPRange will be ignored. When the value of provisioningDHCPExternal - is set to False, indicating an internal DHCP server and the value of - provisioningDHCPRange is not set, then the DHCP range is taken to be the default - range which goes from .10 to .100 of the provisioningNetworkCIDR. This is the only - value in all of the provisioning configuration that can be changed after the - installer has created the CR. This value needs to be two comma sererated IP - addresses within the provisioningNetworkCIDR where the 1st address represents the - start of the range and the 2nd address represents the last usable address in the - range. When the provisioningNetwork is set to `Managed`, the value of - provisioningDHCPRange would be used and ignored in the other 2 modes. - type: string - provisioningOSDownloadURL: - description: provisioningOSDownloadURL is the location from which the OS Image used to boot - baremetal host machines can be downloaded by the metal3 cluster. - type: string - provisioningNetwork: - description: provisioningNetwork provides a way to indicate the state of the underlying - network configuration for the provisioning network. This field can have one of the - following values - - `Managed`- when the provisioning network is completely managed by the Baremetal IPI - solution. - `Unmanaged`- when the provsioning network is present and used but the user is - responsible for managing DHCP. Virtual media provisioning is recommended but PXE - is still available if required. - `Disabled`- when the provisioning network is fully disabled. User can bring up the - baremetal cluster using virtual media or assisted installation. If using metal3 for - power management, BMCs must be accessible from the machine networks. User should - provide two IPs on the external network that would be used for provisioning services. - enum: - - Managed - - Unmanaged - - Disabled - type: string - status: - description: status holds observed values from the cluster. They may not - be overridden. - type: object - properties: - conditions: - description: conditions is a list of conditions and their status - type: array - items: - description: OperatorCondition is just the standard condition fields. - type: object - properties: - lastTransitionTime: - type: string - format: date-time - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - generations: - description: generations are used to determine when an item needs to - be reconciled or has changed in a way that needs a reaction. - type: array - items: - description: GenerationStatus keeps track of the generation for a - given resource so that decisions about forced updates can be made. - type: object - properties: - group: - description: group is the group of the thing you're tracking - type: string - hash: - description: hash is an optional field set for resources without - generation that are content sensitive like secrets and configmaps - type: string - lastGeneration: - description: lastGeneration is the last generation of the workload - controller involved - type: integer - format: int64 - name: - description: name is the name of the thing you're tracking - type: string - namespace: - description: namespace is where the thing you're tracking is - type: string - resource: - description: resource is the resource type of the thing you're - tracking - type: string - observedGeneration: - description: observedGeneration is the last generation change you've - dealt with - type: integer - format: int64 - readyReplicas: - description: readyReplicas indicates how many replicas are ready and - at the desired state - type: integer - format: int32 - version: v1alpha1 - versions: - - name: v1alpha1 - served: true - storage: true diff --git a/install/0000_30_machine-api-operator_08_baremetalhost.crd.yaml b/install/0000_30_machine-api-operator_08_baremetalhost.crd.yaml deleted file mode 100644 index b8b2cd90c2..0000000000 --- a/install/0000_30_machine-api-operator_08_baremetalhost.crd.yaml +++ /dev/null @@ -1,719 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: baremetalhosts.metal3.io - annotations: - include.release.openshift.io/self-managed-high-availability: "true" -spec: - group: metal3.io - names: - kind: BareMetalHost - listKind: BareMetalHostList - plural: baremetalhosts - shortNames: - - bmh - - bmhost - singular: baremetalhost - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Operational status - jsonPath: .status.operationalStatus - name: Status - type: string - - description: Provisioning status - jsonPath: .status.provisioning.state - name: Provisioning Status - type: string - - description: Consumer using this host - jsonPath: .spec.consumerRef.name - name: Consumer - type: string - - description: Address of management controller - jsonPath: .spec.bmc.address - name: BMC - type: string - - description: The type of hardware detected - jsonPath: .status.hardwareProfile - name: Hardware Profile - type: string - - description: Whether the host is online or not - jsonPath: .spec.online - name: Online - type: string - - description: Most recent error - jsonPath: .status.errorMessage - name: Error - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: BareMetalHost is the Schema for the baremetalhosts API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: BareMetalHostSpec defines the desired state of BareMetalHost - properties: - bmc: - description: How do we connect to the BMC? - properties: - address: - description: Address holds the URL for accessing the controller - on the network. - type: string - credentialsName: - description: The name of the secret containing the BMC credentials - (requires keys "username" and "password"). - type: string - disableCertificateVerification: - description: DisableCertificateVerification disables verification - of server certificates when using HTTPS to connect to the BMC. - This is required when the server certificate is self-signed, - but is insecure because it allows a man-in-the-middle to intercept - the connection. - type: boolean - required: - - address - - credentialsName - type: object - bootMACAddress: - description: Which MAC address will PXE boot? This is optional for - some types, but required for libvirt VMs driven by vbmc. - pattern: '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}' - type: string - bootMode: - description: Select the method of initializing the hardware during - boot. Defaults to UEFI. - enum: - - UEFI - - legacy - type: string - consumerRef: - description: ConsumerRef can be used to store information about something - that is using a host. When it is not empty, the host is considered - "in use". - properties: - apiVersion: - description: API version of the referent. - type: string - fieldPath: - description: 'If referring to a piece of an object instead of - an entire object, this string should contain a valid JSON/Go - field access statement, such as desiredState.manifest.containers[2]. - For example, if the object reference is to a container within - a pod, this would take on a value like: "spec.containers{name}" - (where "name" refers to the name of the container that triggered - the event) or if no container name is specified "spec.containers[2]" - (container with index 2 in this pod). This syntax is chosen - only to have some well-defined way of referencing a part of - an object. TODO: this design is not final and this field is - subject to change in the future.' - type: string - kind: - description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' - type: string - namespace: - description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' - type: string - resourceVersion: - description: 'Specific resourceVersion to which this reference - is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' - type: string - uid: - description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' - type: string - type: object - description: - description: Description is a human-entered text used to help identify - the host - type: string - externallyProvisioned: - description: ExternallyProvisioned means something else is managing - the image running on the host and the operator should only manage - the power status and hardware inventory inspection. If the Image - field is filled in, this field is ignored. - type: boolean - hardwareProfile: - description: What is the name of the hardware profile for this host? - It should only be necessary to set this when inspection cannot automatically - determine the profile. - type: string - image: - description: Image holds the details of the image to be provisioned. - properties: - checksum: - description: Checksum is the checksum for the image. - type: string - checksumType: - description: ChecksumType is the checksum algorithm for the image. - e.g md5, sha256, sha512 - enum: - - md5 - - sha256 - - sha512 - type: string - format: - description: DiskFormat contains the format of the image (raw, - qcow2, ...) Needs to be set to raw for raw images streaming - enum: - - raw - - qcow2 - - vdi - - vmdk - type: string - url: - description: URL is a location of an image to deploy. - type: string - required: - - checksum - - url - type: object - metaData: - description: MetaData holds the reference to the Secret containing - host metadata (e.g. meta_data.json which is passed to Config Drive). - properties: - name: - description: Name is unique within a namespace to reference a - secret resource. - type: string - namespace: - description: Namespace defines the space within which the secret - name must be unique. - type: string - type: object - networkData: - description: NetworkData holds the reference to the Secret containing - network configuration (e.g content of network_data.json which is - passed to Config Drive). - properties: - name: - description: Name is unique within a namespace to reference a - secret resource. - type: string - namespace: - description: Namespace defines the space within which the secret - name must be unique. - type: string - type: object - online: - description: Should the server be online? - type: boolean - rootDeviceHints: - description: Provide guidance about how to choose the device for the - image being provisioned. - properties: - deviceName: - description: A Linux device name like "/dev/vda". The hint must - match the actual value exactly. - type: string - hctl: - description: A SCSI bus address like 0:0:0:0. The hint must match - the actual value exactly. - type: string - minSizeGigabytes: - description: The minimum size of the device in Gigabytes. - minimum: 0 - type: integer - model: - description: A vendor-specific device identifier. The hint can - be a substring of the actual value. - type: string - rotational: - description: True if the device should use spinning media, false - otherwise. - type: boolean - serialNumber: - description: Device serial number. The hint must match the actual - value exactly. - type: string - vendor: - description: The name of the vendor or manufacturer of the device. - The hint can be a substring of the actual value. - type: string - wwn: - description: Unique storage identifier. The hint must match the - actual value exactly. - type: string - wwnVendorExtension: - description: Unique vendor storage identifier. The hint must match - the actual value exactly. - type: string - wwnWithExtension: - description: Unique storage identifier with the vendor extension - appended. The hint must match the actual value exactly. - type: string - type: object - taints: - description: Taints is the full, authoritative list of taints to apply - to the corresponding Machine. This list will overwrite any modifications - made to the Machine on an ongoing basis. - items: - description: The node this Taint is attached to has the "effect" - on any pod that does not tolerate the Taint. - properties: - effect: - description: Required. The effect of the taint on pods that - do not tolerate the taint. Valid effects are NoSchedule, PreferNoSchedule - and NoExecute. - type: string - key: - description: Required. The taint key to be applied to a node. - type: string - timeAdded: - description: TimeAdded represents the time at which the taint - was added. It is only written for NoExecute taints. - format: date-time - type: string - value: - description: Required. The taint value corresponding to the - taint key. - type: string - required: - - effect - - key - type: object - type: array - userData: - description: UserData holds the reference to the Secret containing - the user data to be passed to the host before it boots. - properties: - name: - description: Name is unique within a namespace to reference a - secret resource. - type: string - namespace: - description: Namespace defines the space within which the secret - name must be unique. - type: string - type: object - required: - - online - type: object - status: - description: BareMetalHostStatus defines the observed state of BareMetalHost - properties: - errorMessage: - description: the last error message reported by the provisioning subsystem - type: string - errorType: - description: ErrorType indicates the type of failure encountered when - the OperationalStatus is OperationalStatusError - enum: - - registration error - - inspection error - - provisioning error - - power management error - type: string - goodCredentials: - description: the last credentials we were able to validate as working - properties: - credentials: - description: SecretReference represents a Secret Reference. It - has enough information to retrieve secret in any namespace - properties: - name: - description: Name is unique within a namespace to reference - a secret resource. - type: string - namespace: - description: Namespace defines the space within which the - secret name must be unique. - type: string - type: object - credentialsVersion: - type: string - type: object - hardware: - description: The hardware discovered to exist on the host. - properties: - cpu: - description: CPU describes one processor on the host. - properties: - arch: - type: string - clockMegahertz: - description: ClockSpeed is a clock speed in MHz - type: number - count: - type: integer - flags: - items: - type: string - type: array - model: - type: string - required: - - arch - - clockMegahertz - - count - - flags - - model - type: object - firmware: - description: Firmware describes the firmware on the host. - properties: - bios: - description: The BIOS for this firmware - properties: - date: - description: The release/build date for this BIOS - type: string - vendor: - description: The vendor name for this BIOS - type: string - version: - description: The version of the BIOS - type: string - required: - - date - - vendor - - version - type: object - required: - - bios - type: object - hostname: - type: string - nics: - items: - description: NIC describes one network interface on the host. - properties: - ip: - description: The IP address of the device - type: string - mac: - description: The device MAC addr - pattern: '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}' - type: string - model: - description: The name of the model, e.g. "virt-io" - type: string - name: - description: The name of the NIC, e.g. "nic-1" - type: string - pxe: - description: Whether the NIC is PXE Bootable - type: boolean - speedGbps: - description: The speed of the device - type: integer - vlanId: - description: The untagged VLAN ID - format: int32 - maximum: 4094 - minimum: 0 - type: integer - vlans: - description: The VLANs available - items: - description: VLAN represents the name and ID of a VLAN - properties: - id: - description: VLANID is a 12-bit 802.1Q VLAN identifier - format: int32 - maximum: 4094 - minimum: 0 - type: integer - name: - type: string - required: - - id - type: object - type: array - required: - - ip - - mac - - model - - name - - pxe - - speedGbps - - vlanId - type: object - type: array - ramMebibytes: - type: integer - storage: - items: - description: Storage describes one storage device (disk, SSD, - etc.) on the host. - properties: - hctl: - description: The SCSI location of the device - type: string - model: - description: Hardware model - type: string - name: - description: A name for the disk, e.g. "disk 1 (boot)" - type: string - rotational: - description: Whether this disk represents rotational storage - type: boolean - serialNumber: - description: The serial number of the device - type: string - sizeBytes: - description: The size of the disk in Bytes - format: int64 - type: integer - vendor: - description: The name of the vendor of the device - type: string - wwn: - description: The WWN of the device - type: string - wwnVendorExtension: - description: The WWN Vendor extension of the device - type: string - wwnWithExtension: - description: The WWN with the extension - type: string - required: - - name - - rotational - - serialNumber - - sizeBytes - type: object - type: array - systemVendor: - description: HardwareSystemVendor stores details about the whole - hardware system. - properties: - manufacturer: - type: string - productName: - type: string - serialNumber: - type: string - required: - - manufacturer - - productName - - serialNumber - type: object - required: - - cpu - - firmware - - hostname - - nics - - ramMebibytes - - storage - - systemVendor - type: object - hardwareProfile: - description: The name of the profile matching the hardware details. - type: string - lastUpdated: - description: LastUpdated identifies when this status was last observed. - format: date-time - type: string - operationHistory: - description: OperationHistory holds information about operations performed - on this host. - properties: - deprovision: - description: OperationMetric contains metadata about an operation - (inspection, provisioning, etc.) used for tracking metrics. - properties: - end: - format: date-time - nullable: true - type: string - start: - format: date-time - nullable: true - type: string - type: object - inspect: - description: OperationMetric contains metadata about an operation - (inspection, provisioning, etc.) used for tracking metrics. - properties: - end: - format: date-time - nullable: true - type: string - start: - format: date-time - nullable: true - type: string - type: object - provision: - description: OperationMetric contains metadata about an operation - (inspection, provisioning, etc.) used for tracking metrics. - properties: - end: - format: date-time - nullable: true - type: string - start: - format: date-time - nullable: true - type: string - type: object - register: - description: OperationMetric contains metadata about an operation - (inspection, provisioning, etc.) used for tracking metrics. - properties: - end: - format: date-time - nullable: true - type: string - start: - format: date-time - nullable: true - type: string - type: object - type: object - operationalStatus: - description: OperationalStatus holds the status of the host - enum: - - "" - - OK - - discovered - - error - type: string - poweredOn: - description: indicator for whether or not the host is powered on - type: boolean - provisioning: - description: Information tracked by the provisioner. - properties: - ID: - description: The machine's UUID from the underlying provisioning - tool - type: string - bootMode: - description: BootMode indicates the boot mode used to provision - the node - enum: - - UEFI - - legacy - type: string - image: - description: Image holds the details of the last image successfully - provisioned to the host. - properties: - checksum: - description: Checksum is the checksum for the image. - type: string - checksumType: - description: ChecksumType is the checksum algorithm for the - image. e.g md5, sha256, sha512 - enum: - - md5 - - sha256 - - sha512 - type: string - format: - description: DiskFormat contains the format of the image (raw, - qcow2, ...) Needs to be set to raw for raw images streaming - enum: - - raw - - qcow2 - - vdi - - vmdk - type: string - url: - description: URL is a location of an image to deploy. - type: string - required: - - checksum - - url - type: object - rootDeviceHints: - description: The RootDevicehints set by the user - properties: - deviceName: - description: A Linux device name like "/dev/vda". The hint - must match the actual value exactly. - type: string - hctl: - description: A SCSI bus address like 0:0:0:0. The hint must - match the actual value exactly. - type: string - minSizeGigabytes: - description: The minimum size of the device in Gigabytes. - minimum: 0 - type: integer - model: - description: A vendor-specific device identifier. The hint - can be a substring of the actual value. - type: string - rotational: - description: True if the device should use spinning media, - false otherwise. - type: boolean - serialNumber: - description: Device serial number. The hint must match the - actual value exactly. - type: string - vendor: - description: The name of the vendor or manufacturer of the - device. The hint can be a substring of the actual value. - type: string - wwn: - description: Unique storage identifier. The hint must match - the actual value exactly. - type: string - wwnVendorExtension: - description: Unique vendor storage identifier. The hint must - match the actual value exactly. - type: string - wwnWithExtension: - description: Unique storage identifier with the vendor extension - appended. The hint must match the actual value exactly. - type: string - type: object - state: - description: An indiciator for what the provisioner is doing with - the host. - type: string - required: - - ID - - state - type: object - triedCredentials: - description: the last credentials we sent to the provisioning backend - properties: - credentials: - description: SecretReference represents a Secret Reference. It - has enough information to retrieve secret in any namespace - properties: - name: - description: Name is unique within a namespace to reference - a secret resource. - type: string - namespace: - description: Namespace defines the space within which the - secret name must be unique. - type: string - type: object - credentialsVersion: - type: string - type: object - required: - - errorMessage - - hardwareProfile - - operationHistory - - operationalStatus - - poweredOn - - provisioning - type: object - type: object - served: true - storage: true - subresources: - status: {} diff --git a/install/0000_30_machine-api-operator_09_rbac.yaml b/install/0000_30_machine-api-operator_09_rbac.yaml index 5c622c585b..794cd0f2f5 100644 --- a/install/0000_30_machine-api-operator_09_rbac.yaml +++ b/install/0000_30_machine-api-operator_09_rbac.yaml @@ -201,16 +201,6 @@ rules: - list - watch -# the baremetal pod deployment uses hostNetwork, hostPort, and privileged - - apiGroups: - - security.openshift.io - resources: - - securitycontextconstraints - verbs: - - use - resourceNames: - - privileged - --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role @@ -341,14 +331,6 @@ rules: - list - patch - - apiGroups: - - metal3.io - resources: - - provisionings - verbs: - - get - - list - - apiGroups: - admissionregistration.k8s.io resources: diff --git a/pkg/operator/baremetal_config.go b/pkg/operator/baremetal_config.go deleted file mode 100644 index f424c85d56..0000000000 --- a/pkg/operator/baremetal_config.go +++ /dev/null @@ -1,198 +0,0 @@ -package operator - -import ( - "context" - "fmt" - "net" - - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" - dynamic "k8s.io/client-go/dynamic" -) - -var provisioningGVR = schema.GroupVersionResource{Group: "metal3.io", Resource: "provisionings", Version: "v1alpha1"} -var provisioningGR = schema.GroupResource{Group: "metal3.io", Resource: "provisionings"} - -const ( - baremetalProvisioningCR = "provisioning-configuration" - baremetalHttpPort = "6180" - baremetalIronicPort = "6385" - baremetalIronicInspectorPort = "5050" - baremetalKernelUrlSubPath = "images/ironic-python-agent.kernel" - baremetalRamdiskUrlSubPath = "images/ironic-python-agent.initramfs" - baremetalIronicEndpointSubpath = "v1/" - provisioningNetworkManaged = "Managed" - provisioningNetworkUnmanaged = "Unmanaged" - provisioningNetworkDisabled = "Disabled" -) - -// Provisioning Config needed to deploy Metal3 pod -type BaremetalProvisioningConfig struct { - ProvisioningInterface string - ProvisioningIp string - ProvisioningNetworkCIDR string - ProvisioningDHCPRange string - ProvisioningOSDownloadURL string - ProvisioningNetwork string -} - -func reportError(found bool, err error, configItem string, configName string) error { - if err != nil { - return fmt.Errorf("Error while reading %s from Baremetal provisioning CR %s: %w", configItem, configName, err) - } - if !found { - return fmt.Errorf("%s not found in Baremetal provisioning CR %s", configItem, configName) - } - return fmt.Errorf("Unknown Error while reading %s from Baremetal provisioning CR %s", configItem, configName) -} - -func getBaremetalProvisioningConfig(dc dynamic.Interface, configName string) (*BaremetalProvisioningConfig, error) { - provisioningClient := dc.Resource(provisioningGVR) - provisioningConfig, err := provisioningClient.Get(context.Background(), configName, metav1.GetOptions{}) - if apierrors.IsNotFound(err) { - // The provisioning CR is not present and that is not considered an error. - return nil, nil - } - if err != nil { - return nil, reportError(true, err, "provisioning configuration", configName) - } - provisioningSpec, found, err := unstructured.NestedMap(provisioningConfig.UnstructuredContent(), "spec") - if !found || err != nil { - return nil, reportError(found, err, "Spec field", configName) - } - provisioningInterface, found, err := unstructured.NestedString(provisioningSpec, "provisioningInterface") - if !found || err != nil { - return nil, reportError(found, err, "provisioningInterface", configName) - } - provisioningIP, found, err := unstructured.NestedString(provisioningSpec, "provisioningIP") - if !found || err != nil { - return nil, reportError(found, err, "provisioningIP", configName) - } - provisioningDHCPRange, found, err := unstructured.NestedString(provisioningSpec, "provisioningDHCPRange") - if !found || err != nil { - return nil, reportError(found, err, "provisioningDHCPRange", configName) - } - provisioningOSDownloadURL, found, err := unstructured.NestedString(provisioningSpec, "provisioningOSDownloadURL") - if !found || err != nil { - return nil, reportError(found, err, "provisioningOSDownloadURL", configName) - } - // If provisioningNetwork is not provided, set its value based on provisioningDHCPExternal - provisioningNetwork, foundNetworkState, err := unstructured.NestedString(provisioningSpec, "provisioningNetwork") - if err != nil { - return nil, reportError(true, err, "provisioningNetwork", configName) - } - if !foundNetworkState { - // Check if provisioningDHCPExternal is present in the config - provisioningDHCPExternal, foundDHCP, err := unstructured.NestedBool(provisioningSpec, "provisioningDHCPExternal") - if !foundDHCP || err != nil { - // Both the new provisioningNetwork and the old provisioningDHCPExternal configs are not found. - return nil, reportError(foundDHCP, err, "provisioningNetwork and provisioningDHCPExternal", configName) - } - if !provisioningDHCPExternal { - provisioningNetwork = provisioningNetworkManaged - } else { - provisioningNetwork = provisioningNetworkUnmanaged - } - } - // provisioningNetworkCIDR needs to be present for all provisioningNetwork states (even Disabled). - // The CIDR of the network needs to be extracted to form the provisioningIPCIDR - provisioningNetworkCIDR, found, err := unstructured.NestedString(provisioningSpec, "provisioningNetworkCIDR") - if !found || err != nil { - return nil, reportError(found, err, "provisioningNetworkCIDR", configName) - } - // Check if the other config values make sense for the provisioningNetwork configured. - if provisioningInterface == "" && provisioningNetwork == provisioningNetworkManaged { - return nil, fmt.Errorf("provisioningInterface cannot be empty when provisioningNetwork is Managed.") - } - if provisioningDHCPRange == "" && provisioningNetwork == provisioningNetworkManaged { - return nil, fmt.Errorf("provisioningDHCPRange cannot be empty when provisioningNetwork is Managed or when the DHCP server needs to run with the metal3 cluster.") - } - baremetalConfig := BaremetalProvisioningConfig{ - ProvisioningInterface: provisioningInterface, - ProvisioningIp: provisioningIP, - ProvisioningNetworkCIDR: provisioningNetworkCIDR, - ProvisioningDHCPRange: provisioningDHCPRange, - ProvisioningOSDownloadURL: provisioningOSDownloadURL, - ProvisioningNetwork: provisioningNetwork, - } - return &baremetalConfig, nil -} - -func getProvisioningIPCIDR(baremetalConfig BaremetalProvisioningConfig) *string { - if baremetalConfig.ProvisioningNetworkCIDR != "" && baremetalConfig.ProvisioningIp != "" { - _, net, err := net.ParseCIDR(baremetalConfig.ProvisioningNetworkCIDR) - if err == nil { - cidr, _ := net.Mask.Size() - ipCIDR := fmt.Sprintf("%s/%d", baremetalConfig.ProvisioningIp, cidr) - return &ipCIDR - } - } - return nil -} - -func getDeployKernelUrl(baremetalConfig BaremetalProvisioningConfig) *string { - if baremetalConfig.ProvisioningIp != "" { - deployKernelUrl := fmt.Sprintf("http://%s/%s", net.JoinHostPort(baremetalConfig.ProvisioningIp, baremetalHttpPort), baremetalKernelUrlSubPath) - return &deployKernelUrl - } - return nil -} - -func getDeployRamdiskUrl(baremetalConfig BaremetalProvisioningConfig) *string { - if baremetalConfig.ProvisioningIp != "" { - deployRamdiskUrl := fmt.Sprintf("http://%s/%s", net.JoinHostPort(baremetalConfig.ProvisioningIp, baremetalHttpPort), baremetalRamdiskUrlSubPath) - return &deployRamdiskUrl - } - return nil -} - -func getIronicEndpoint(baremetalConfig BaremetalProvisioningConfig) *string { - if baremetalConfig.ProvisioningIp != "" { - ironicEndpoint := fmt.Sprintf("http://%s/%s", net.JoinHostPort(baremetalConfig.ProvisioningIp, baremetalIronicPort), baremetalIronicEndpointSubpath) - return &ironicEndpoint - } - return nil -} - -func getIronicInspectorEndpoint(baremetalConfig BaremetalProvisioningConfig) *string { - if baremetalConfig.ProvisioningIp != "" { - inspectorEndpoint := fmt.Sprintf("http://%s/%s", net.JoinHostPort(baremetalConfig.ProvisioningIp, baremetalIronicInspectorPort), baremetalIronicEndpointSubpath) - return &inspectorEndpoint - } - return nil -} - -func getProvisioningOSDownloadURL(baremetalConfig BaremetalProvisioningConfig) *string { - if baremetalConfig.ProvisioningOSDownloadURL != "" { - return &(baremetalConfig.ProvisioningOSDownloadURL) - } - return nil -} - -func getMetal3DeploymentConfig(name string, baremetalConfig BaremetalProvisioningConfig) *string { - configValue := "" - switch name { - case "PROVISIONING_IP": - return getProvisioningIPCIDR(baremetalConfig) - case "PROVISIONING_INTERFACE": - return &baremetalConfig.ProvisioningInterface - case "DEPLOY_KERNEL_URL": - return getDeployKernelUrl(baremetalConfig) - case "DEPLOY_RAMDISK_URL": - return getDeployRamdiskUrl(baremetalConfig) - case "IRONIC_ENDPOINT": - return getIronicEndpoint(baremetalConfig) - case "IRONIC_INSPECTOR_ENDPOINT": - return getIronicInspectorEndpoint(baremetalConfig) - case "HTTP_PORT": - configValue = baremetalHttpPort - return &configValue - case "DHCP_RANGE": - return &baremetalConfig.ProvisioningDHCPRange - case "RHCOS_IMAGE_URL": - return getProvisioningOSDownloadURL(baremetalConfig) - } - return nil -} diff --git a/pkg/operator/baremetal_pod.go b/pkg/operator/baremetal_pod.go deleted file mode 100644 index 56d76cb46f..0000000000 --- a/pkg/operator/baremetal_pod.go +++ /dev/null @@ -1,678 +0,0 @@ -package operator - -import ( - "context" - "crypto/rand" - "fmt" - "math/big" - - "golang.org/x/crypto/bcrypt" - - osclientset "github.com/openshift/client-go/config/clientset/versioned" - "github.com/openshift/machine-api-operator/pkg/metrics" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/intstr" - appsclientv1 "k8s.io/client-go/kubernetes/typed/apps/v1" - coreclientv1 "k8s.io/client-go/kubernetes/typed/core/v1" - "k8s.io/klog/v2" - "k8s.io/utils/pointer" -) - -const ( - baremetalDeploymentName = "metal3" - baremetalSharedVolume = "metal3-shared" - baremetalSecretName = "metal3-mariadb-password" - baremetalSecretKey = "password" - ironicCredentialsVolume = "metal3-ironic-basic-auth" - inspectorCredentialsVolume = "metal3-inspector-basic-auth" - ironicUsernameKey = "username" - ironicPasswordKey = "password" - ironicHtpasswdKey = "htpasswd" - ironicConfigKey = "auth-config" - ironicSecretName = "metal3-ironic-password" - ironicUsername = "ironic-user" - inspectorSecretName = "metal3-ironic-inspector-password" - inspectorUsername = "inspector-user" - metal3AuthRootDir = "/auth" - htpasswdEnvVar = "HTTP_BASIC_HTPASSWD" -) - -var defaultBaremetalVolumes = []corev1.Volume{ - { - Name: baremetalSharedVolume, - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }, - { - Name: ironicCredentialsVolume, - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: ironicSecretName, - Items: []corev1.KeyToPath{ - {Key: ironicUsernameKey, Path: ironicUsernameKey}, - {Key: ironicPasswordKey, Path: ironicPasswordKey}, - {Key: ironicConfigKey, Path: ironicConfigKey}, - }, - }, - }, - }, - { - Name: inspectorCredentialsVolume, - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: inspectorSecretName, - Items: []corev1.KeyToPath{ - {Key: ironicUsernameKey, Path: ironicUsernameKey}, - {Key: ironicPasswordKey, Path: ironicPasswordKey}, - {Key: ironicConfigKey, Path: ironicConfigKey}, - }, - }, - }, - }, -} - -// we define this service monitor in code instead of a yaml manifest -// because we only want it to exist when metal3 is deployed -const metal3ServiceMonitorDefinition = ` -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - namespace: openshift-machine-api - name: metal3-metrics - labels: - k8s-app: controller - annotations: - exclude.release.openshift.io/internal-openshift-hosted: "true" -spec: - namespaceSelector: - matchNames: - - openshift-machine-api - selector: - matchLabels: - k8s-app: controller - endpoints: - - port: metal3-mtrc - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token - interval: 30s - scheme: https - tlsConfig: - caFile: /etc/prometheus/configmaps/serving-certs-ca-bundle/service-ca.crt - serverName: machine-api-controllers.openshift-machine-api.svc -` - -var sharedVolumeMount = corev1.VolumeMount{ - Name: baremetalSharedVolume, - MountPath: "/shared", -} - -var ironicCredentialsMount = corev1.VolumeMount{ - Name: ironicCredentialsVolume, - MountPath: metal3AuthRootDir + "/ironic", - ReadOnly: true, -} - -var inspectorCredentialsMount = corev1.VolumeMount{ - Name: inspectorCredentialsVolume, - MountPath: metal3AuthRootDir + "/ironic-inspector", - ReadOnly: true, -} - -func buildEnvVar(name string, baremetalProvisioningConfig BaremetalProvisioningConfig) corev1.EnvVar { - value := getMetal3DeploymentConfig(name, baremetalProvisioningConfig) - if value != nil { - return corev1.EnvVar{ - Name: name, - Value: *value, - } - } - return corev1.EnvVar{ - Name: name, - } -} - -func setMariadbPassword() corev1.EnvVar { - return corev1.EnvVar{ - Name: "MARIADB_PASSWORD", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: baremetalSecretName, - }, - Key: baremetalSecretKey, - }, - }, - } -} - -func setIronicHtpasswdHash(name string, secretName string) corev1.EnvVar { - return corev1.EnvVar{ - Name: name, - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: secretName, - }, - Key: ironicHtpasswdKey, - }, - }, - } -} - -func generateRandomPassword() (string, error) { - chars := []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ" + - "abcdefghijklmnopqrstuvwxyz" + - "0123456789") - length := 16 - buf := make([]rune, length) - numChars := big.NewInt(int64(len(chars))) - for i := range buf { - c, err := rand.Int(rand.Reader, numChars) - if err != nil { - return "", err - } - buf[i] = chars[c.Uint64()] - } - return string(buf), nil -} - -func createMariadbPasswordSecret(client coreclientv1.SecretsGetter, config *OperatorConfig) error { - klog.V(3).Info("Checking if the MariaDB password secret already exists") - _, err := client.Secrets(config.TargetNamespace).Get(context.Background(), baremetalSecretName, metav1.GetOptions{}) - if !apierrors.IsNotFound(err) { - return err - } - - // Secret does not already exist. So, create one. - password, err := generateRandomPassword() - if err != nil { - return err - } - _, err = client.Secrets(config.TargetNamespace).Create( - context.Background(), - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: baremetalSecretName, - Namespace: config.TargetNamespace, - }, - StringData: map[string]string{ - baremetalSecretKey: password, - }, - }, - metav1.CreateOptions{}, - ) - return err -} - -func createIronicPasswordSecret(client coreclientv1.SecretsGetter, config *OperatorConfig, name string, username string, configSection string) error { - klog.V(3).Info(fmt.Sprintf("Checking if the %s password secret already exists", name)) - _, err := client.Secrets(config.TargetNamespace).Get(context.Background(), name, metav1.GetOptions{}) - if !apierrors.IsNotFound(err) { - return err - } - - // Secret does not already exist. So, create one. - password, err := generateRandomPassword() - if err != nil { - return err - } - hash, err := bcrypt.GenerateFromPassword([]byte(password), 5) // Use same cost as htpasswd default - if err != nil { - return err - } - // Change hash version from $2a$ to $2y$, as generated by htpasswd. - // These are equivalent for our purposes. - hash[2] = 'y' - - _, err = client.Secrets(config.TargetNamespace).Create( - context.Background(), - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: config.TargetNamespace, - }, - StringData: map[string]string{ - ironicUsernameKey: username, - ironicPasswordKey: password, - ironicHtpasswdKey: fmt.Sprintf("%s:%s", username, hash), - ironicConfigKey: fmt.Sprintf(`[%s] -auth_type = http_basic -username = %s -password = %s -`, - configSection, username, password), - }, - }, - metav1.CreateOptions{}, - ) - return err -} - -func createMetal3PasswordSecrets(client coreclientv1.SecretsGetter, config *OperatorConfig) error { - if err := createMariadbPasswordSecret(client, config); err != nil { - klog.Error("Failed to create Mariadb password.") - return err - } - if err := createIronicPasswordSecret(client, config, ironicSecretName, ironicUsername, "ironic"); err != nil { - klog.Error("Failed to create Ironic password.") - return err - } - if err := createIronicPasswordSecret(client, config, inspectorSecretName, inspectorUsername, "inspector"); err != nil { - klog.Error("Failed to create Ironic Inspector password.") - return err - } - return nil -} - -// Return false on error or if "baremetal.openshift.io/owned" annotation set -func checkMetal3DeploymentMAOOwned(client appsclientv1.DeploymentsGetter, config *OperatorConfig) (bool, error) { - existing, err := client.Deployments(config.TargetNamespace).Get(context.Background(), baremetalDeploymentName, metav1.GetOptions{}) - if err != nil { - if apierrors.IsNotFound(err) { - return true, nil - } - return false, err - } - if _, exists := existing.ObjectMeta.Annotations[cboOwnedAnnotation]; exists { - return false, nil - } - return true, nil -} - -// Return true if the baremetal clusteroperator exists -func checkForBaremetalClusterOperator(osClient osclientset.Interface) (bool, error) { - _, err := osClient.ConfigV1().ClusterOperators().Get(context.Background(), cboClusterOperatorName, metav1.GetOptions{}) - if err != nil { - if apierrors.IsNotFound(err) { - return false, nil - } - return false, err - } - return true, nil -} - -// we define this service in code instead of a yaml manifest because -// we only want it to exist when metal3 is deployed -func newMetal3Service(config *OperatorConfig, baremetalProvisioningConfig BaremetalProvisioningConfig) *corev1.Service { - return &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "metal3-metrics", - Namespace: config.TargetNamespace, - Annotations: map[string]string{ - maoOwnedAnnotation: "", - // copied from settings for machine-api-controllers - // service in - // 0000_30_machine-api-operator_10_service.yaml - "service.alpha.openshift.io/serving-cert-secret-name": certStoreName, - "exclude.release.openshift.io/internal-openshift-hosted": "true", - }, - Labels: map[string]string{ - "k8s-app": "controller", - }, - }, - Spec: corev1.ServiceSpec{ - Type: corev1.ServiceTypeNodePort, - Ports: []corev1.ServicePort{ - { - Name: "metal3-mtrc", - Port: metal3ExposeMetricsPort, - TargetPort: intstr.IntOrString{ - Type: intstr.String, - StrVal: "metal3-mtrc", - }, - }, - }, - Selector: map[string]string{ - "k8s-app": "controller", - }, - }, - } -} - -func newMetal3Deployment(config *OperatorConfig, baremetalProvisioningConfig BaremetalProvisioningConfig) *appsv1.Deployment { - replicas := int32(1) - template := newMetal3PodTemplateSpec(config, baremetalProvisioningConfig) - - return &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: baremetalDeploymentName, - Namespace: config.TargetNamespace, - Annotations: map[string]string{ - maoOwnedAnnotation: "", - }, - 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, baremetalProvisioningConfig BaremetalProvisioningConfig) *corev1.PodTemplateSpec { - initContainers := newMetal3InitContainers(config, baremetalProvisioningConfig) - containers := newMetal3Containers(config, baremetalProvisioningConfig) - 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), - }, - } - - volumes := []corev1.Volume{} - volumes = append(volumes, defaultBaremetalVolumes...) - // Include the volumes needed by newKubeProxyContainer from - // sync.go, used to set up the proxy container for metric - // collection. - volumes = append(volumes, newRBACConfigVolumes()...) - - return &corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "api": "clusterapi", - "k8s-app": "controller", - }, - }, - Spec: corev1.PodSpec{ - Volumes: volumes, - InitContainers: initContainers, - Containers: containers, - HostNetwork: true, - PriorityClassName: "system-node-critical", - NodeSelector: map[string]string{"node-role.kubernetes.io/master": ""}, - SecurityContext: &corev1.PodSecurityContext{ - RunAsNonRoot: pointer.BoolPtr(false), - }, - ServiceAccountName: "machine-api-controllers", - Tolerations: tolerations, - }, - } -} - -func newMetal3InitContainers(config *OperatorConfig, baremetalProvisioningConfig BaremetalProvisioningConfig) []corev1.Container { - initContainers := []corev1.Container{ - { - Name: "metal3-ipa-downloader", - Image: config.BaremetalControllers.IronicIpaDownloader, - Command: []string{"/usr/local/bin/get-resource.sh"}, - ImagePullPolicy: "IfNotPresent", - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(true), - }, - VolumeMounts: []corev1.VolumeMount{sharedVolumeMount}, - Env: []corev1.EnvVar{}, - }, - } - initContainers = append(initContainers, createInitContainerMachineOsDownloader(config, baremetalProvisioningConfig)) - initContainers = append(initContainers, createInitContainerStaticIpSet(config, baremetalProvisioningConfig)) - return initContainers -} - -func createInitContainerMachineOsDownloader(config *OperatorConfig, baremetalProvisioningConfig BaremetalProvisioningConfig) corev1.Container { - initContainer := corev1.Container{ - Name: "metal3-machine-os-downloader", - Image: config.BaremetalControllers.IronicMachineOsDownloader, - Command: []string{"/usr/local/bin/get-resource.sh"}, - ImagePullPolicy: "IfNotPresent", - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(true), - }, - VolumeMounts: []corev1.VolumeMount{sharedVolumeMount}, - Env: []corev1.EnvVar{ - buildEnvVar("RHCOS_IMAGE_URL", baremetalProvisioningConfig), - }, - } - return initContainer -} - -func createInitContainerStaticIpSet(config *OperatorConfig, baremetalProvisioningConfig BaremetalProvisioningConfig) corev1.Container { - initContainer := corev1.Container{ - Name: "metal3-static-ip-set", - Image: config.BaremetalControllers.IronicStaticIpManager, - Command: []string{"/set-static-ip"}, - ImagePullPolicy: "IfNotPresent", - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(true), - }, - Env: []corev1.EnvVar{ - buildEnvVar("PROVISIONING_IP", baremetalProvisioningConfig), - buildEnvVar("PROVISIONING_INTERFACE", baremetalProvisioningConfig), - }, - } - return initContainer -} - -func createContainerMetal3BareMetalOperator(config *OperatorConfig, baremetalProvisioningConfig BaremetalProvisioningConfig) corev1.Container { - return corev1.Container{ - Name: "metal3-baremetal-operator", - Image: config.BaremetalControllers.BaremetalOperator, - Command: []string{"/baremetal-operator", - "-metrics-addr", metrics.DefaultMetal3MetricsAddress}, - ImagePullPolicy: "IfNotPresent", - VolumeMounts: []corev1.VolumeMount{ - ironicCredentialsMount, - inspectorCredentialsMount, - }, - 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", - }, - buildEnvVar("DEPLOY_KERNEL_URL", baremetalProvisioningConfig), - buildEnvVar("DEPLOY_RAMDISK_URL", baremetalProvisioningConfig), - buildEnvVar("IRONIC_ENDPOINT", baremetalProvisioningConfig), - buildEnvVar("IRONIC_INSPECTOR_ENDPOINT", baremetalProvisioningConfig), - { - Name: "METAL3_AUTH_ROOT_DIR", - Value: metal3AuthRootDir, - }, - }, - } -} - -func newMetal3Containers(config *OperatorConfig, baremetalProvisioningConfig BaremetalProvisioningConfig) []corev1.Container { - containers := []corev1.Container{ - newKubeProxyContainer(config.Controllers.KubeRBACProxy, "metal3-mtrc", - metrics.DefaultMetal3MetricsAddress, metal3ExposeMetricsPort), - createContainerMetal3BareMetalOperator(config, baremetalProvisioningConfig), - createContainerMetal3Mariadb(config), - createContainerMetal3Httpd(config, baremetalProvisioningConfig), - createContainerMetal3IronicConductor(config, baremetalProvisioningConfig), - createContainerMetal3IronicApi(config, baremetalProvisioningConfig), - createContainerMetal3IronicInspector(config, baremetalProvisioningConfig), - createContainerMetal3StaticIpManager(config, baremetalProvisioningConfig), - } - if baremetalProvisioningConfig.ProvisioningNetwork != provisioningNetworkDisabled { - containers = append(containers, createContainerMetal3Dnsmasq(config, baremetalProvisioningConfig)) - } - - return containers -} - -func createContainerMetal3Dnsmasq(config *OperatorConfig, baremetalProvisioningConfig BaremetalProvisioningConfig) corev1.Container { - - container := corev1.Container{ - Name: "metal3-dnsmasq", - Image: config.BaremetalControllers.Ironic, - ImagePullPolicy: "IfNotPresent", - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(true), - }, - Command: []string{"/bin/rundnsmasq"}, - VolumeMounts: []corev1.VolumeMount{sharedVolumeMount}, - Env: []corev1.EnvVar{ - buildEnvVar("HTTP_PORT", baremetalProvisioningConfig), - buildEnvVar("PROVISIONING_INTERFACE", baremetalProvisioningConfig), - buildEnvVar("DHCP_RANGE", baremetalProvisioningConfig), - }, - } - return container -} - -func createContainerMetal3Mariadb(config *OperatorConfig) corev1.Container { - - container := corev1.Container{ - Name: "metal3-mariadb", - Image: config.BaremetalControllers.Ironic, - ImagePullPolicy: "IfNotPresent", - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(true), - }, - Command: []string{"/bin/runmariadb"}, - VolumeMounts: []corev1.VolumeMount{sharedVolumeMount}, - Env: []corev1.EnvVar{ - setMariadbPassword(), - }, - } - return container -} - -func createContainerMetal3Httpd(config *OperatorConfig, baremetalProvisioningConfig BaremetalProvisioningConfig) corev1.Container { - - container := corev1.Container{ - Name: "metal3-httpd", - Image: config.BaremetalControllers.Ironic, - ImagePullPolicy: "IfNotPresent", - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(true), - }, - Command: []string{"/bin/runhttpd"}, - VolumeMounts: []corev1.VolumeMount{sharedVolumeMount}, - Env: []corev1.EnvVar{ - buildEnvVar("HTTP_PORT", baremetalProvisioningConfig), - buildEnvVar("PROVISIONING_IP", baremetalProvisioningConfig), - buildEnvVar("PROVISIONING_INTERFACE", baremetalProvisioningConfig), - }, - } - return container -} - -func createContainerMetal3IronicConductor(config *OperatorConfig, baremetalProvisioningConfig BaremetalProvisioningConfig) corev1.Container { - - container := corev1.Container{ - Name: "metal3-ironic-conductor", - Image: config.BaremetalControllers.Ironic, - ImagePullPolicy: "IfNotPresent", - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(true), - }, - Command: []string{"/bin/runironic-conductor"}, - VolumeMounts: []corev1.VolumeMount{ - sharedVolumeMount, - inspectorCredentialsMount, - }, - Env: []corev1.EnvVar{ - setMariadbPassword(), - buildEnvVar("HTTP_PORT", baremetalProvisioningConfig), - buildEnvVar("PROVISIONING_IP", baremetalProvisioningConfig), - buildEnvVar("PROVISIONING_INTERFACE", baremetalProvisioningConfig), - }, - } - return container -} - -func createContainerMetal3IronicApi(config *OperatorConfig, baremetalProvisioningConfig BaremetalProvisioningConfig) corev1.Container { - - container := corev1.Container{ - Name: "metal3-ironic-api", - Image: config.BaremetalControllers.Ironic, - ImagePullPolicy: "IfNotPresent", - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(true), - }, - Command: []string{"/bin/runironic-api"}, - VolumeMounts: []corev1.VolumeMount{sharedVolumeMount}, - Env: []corev1.EnvVar{ - setMariadbPassword(), - buildEnvVar("HTTP_PORT", baremetalProvisioningConfig), - buildEnvVar("PROVISIONING_IP", baremetalProvisioningConfig), - buildEnvVar("PROVISIONING_INTERFACE", baremetalProvisioningConfig), - setIronicHtpasswdHash(htpasswdEnvVar, ironicSecretName), - }, - } - return container -} - -func createContainerMetal3IronicInspector(config *OperatorConfig, baremetalProvisioningConfig BaremetalProvisioningConfig) corev1.Container { - - container := corev1.Container{ - Name: "metal3-ironic-inspector", - Image: config.BaremetalControllers.IronicInspector, - ImagePullPolicy: "IfNotPresent", - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(true), - }, - VolumeMounts: []corev1.VolumeMount{ - sharedVolumeMount, - ironicCredentialsMount, - }, - Env: []corev1.EnvVar{ - buildEnvVar("PROVISIONING_IP", baremetalProvisioningConfig), - buildEnvVar("PROVISIONING_INTERFACE", baremetalProvisioningConfig), - setIronicHtpasswdHash(htpasswdEnvVar, inspectorSecretName), - }, - } - return container -} - -func createContainerMetal3StaticIpManager(config *OperatorConfig, baremetalProvisioningConfig BaremetalProvisioningConfig) corev1.Container { - - container := corev1.Container{ - Name: "metal3-static-ip-manager", - Image: config.BaremetalControllers.IronicStaticIpManager, - Command: []string{"/refresh-static-ip"}, - ImagePullPolicy: "IfNotPresent", - SecurityContext: &corev1.SecurityContext{ - Privileged: pointer.BoolPtr(true), - }, - Env: []corev1.EnvVar{ - buildEnvVar("PROVISIONING_IP", baremetalProvisioningConfig), - buildEnvVar("PROVISIONING_INTERFACE", baremetalProvisioningConfig), - }, - } - return container -} diff --git a/pkg/operator/baremetal_test.go b/pkg/operator/baremetal_test.go deleted file mode 100644 index 3607a482a7..0000000000 --- a/pkg/operator/baremetal_test.go +++ /dev/null @@ -1,541 +0,0 @@ -package operator - -import ( - "testing" - - . "github.com/onsi/gomega" - osconfigv1 "github.com/openshift/api/config/v1" - fakeos "github.com/openshift/client-go/config/clientset/versioned/fake" - "golang.org/x/net/context" - appsv1 "k8s.io/api/apps/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - fakedynamic "k8s.io/client-go/dynamic/fake" - fakekube "k8s.io/client-go/kubernetes/fake" - "sigs.k8s.io/yaml" -) - -var yamlContent = ` -apiVersion: metal3.io/v1alpha1 -kind: Provisioning -metadata: - name: test -spec: - provisioningInterface: "ensp0" - provisioningIP: "172.30.20.3" - provisioningNetworkCIDR: "172.30.20.0/24" - provisioningDHCPExternal: false - provisioningDHCPRange: "172.30.20.11, 172.30.20.101" - provisioningOSDownloadURL: "http://172.22.0.1/images/rhcos-44.81.202001171431.0-openstack.x86_64.qcow2.gz?sha256=e98f83a2b9d4043719664a2be75fe8134dc6ca1fdbde807996622f8cc7ecd234" -` -var ( - expectedProvisioningInterface = "ensp0" - expectedProvisioningIP = "172.30.20.3" - expectedProvisioningNetworkCIDR = "172.30.20.0/24" - expectedProvisioningDHCPRange = "172.30.20.11, 172.30.20.101" - expectedOSImageURL = "http://172.22.0.1/images/rhcos-44.81.202001171431.0-openstack.x86_64.qcow2.gz?sha256=e98f83a2b9d4043719664a2be75fe8134dc6ca1fdbde807996622f8cc7ecd234" - expectedProvisioningIPCIDR = "172.30.20.3/24" - expectedDeployKernelURL = "http://172.30.20.3:6180/images/ironic-python-agent.kernel" - expectedDeployRamdiskURL = "http://172.30.20.3:6180/images/ironic-python-agent.initramfs" - expectedIronicEndpoint = "http://172.30.20.3:6385/v1/" - expectedIronicInspectorEndpoint = "http://172.30.20.3:5050/v1/" - expectedHttpPort = "6180" - expectedProvisioningNetwork = "Managed" -) - -var ( - Managed = ` -apiVersion: metal3.io/v1alpha1 -kind: Provisioning -metadata: - name: test -spec: - provisioningInterface: "ensp0" - provisioningIP: "172.30.20.3" - provisioningNetworkCIDR: "172.30.20.0/24" - provisioningDHCPExternal: false - provisioningDHCPRange: "172.30.20.11, 172.30.20.101" - provisioningOSDownloadURL: "http://172.22.0.1/images/rhcos-44.81.202001171431.0-openstack.x86_64.qcow2.gz?sha256=e98f83a2b9d4043719664a2be75fe8134dc6ca1fdbde807996622f8cc7ecd234" - provisioningNetwork: "Managed" -` - Unmanaged = ` -apiVersion: metal3.io/v1alpha1 -kind: Provisioning -metadata: - name: test -spec: - provisioningInterface: "ensp0" - provisioningIP: "172.30.20.3" - provisioningNetworkCIDR: "172.30.20.0/24" - provisioningDHCPRange: "" - provisioningOSDownloadURL: "http://172.22.0.1/images/rhcos-44.81.202001171431.0-openstack.x86_64.qcow2.gz?sha256=e98f83a2b9d4043719664a2be75fe8134dc6ca1fdbde807996622f8cc7ecd234" - provisioningNetwork: "Unmanaged" -` - Disabled = ` -apiVersion: metal3.io/v1alpha1 -kind: Provisioning -metadata: - name: test -spec: - provisioningInterface: "" - provisioningIP: "172.30.20.3" - provisioningNetworkCIDR: "172.30.20.0/24" - provisioningDHCPExternal: false - provisioningDHCPRange: "" - provisioningOSDownloadURL: "http://172.22.0.1/images/rhcos-44.81.202001171431.0-openstack.x86_64.qcow2.gz?sha256=e98f83a2b9d4043719664a2be75fe8134dc6ca1fdbde807996622f8cc7ecd234" - provisioningNetwork: "Disabled" -` -) - -func TestGenerateRandomPassword(t *testing.T) { - pwd, err := generateRandomPassword() - if err != nil { - t.Errorf("Unexpected error: %s", err) - } - if pwd == "" { - t.Errorf("Expected a valid string but got null") - } -} - -func newOperatorWithBaremetalConfig() *OperatorConfig { - return &OperatorConfig{ - targetNamespace, - Controllers{ - "docker.io/openshift/origin-aws-machine-controllers:v4.0.0", - "docker.io/openshift/origin-machine-api-operator:v4.0.0", - "docker.io/openshift/origin-machine-api-operator:v4.0.0", - "docker.io/openshift/origin-machine-api-operator:v4.0.0", - "docker.io/openshift/origin-kube-rbac-proxy:v4.0.0", - "docker.io/openshift/origin-aws-machine-controllers:v4.0.0", - }, - BaremetalControllers{ - "quay.io/openshift/origin-baremetal-operator:v4.2.0", - "quay.io/openshift/origin-ironic:v4.2.0", - "quay.io/openshift/origin-ironic-inspector:v4.2.0", - "quay.io/openshift/origin-ironic-ipa-downloader:v4.2.0", - "quay.io/openshift/origin-ironic-machine-os-downloader:v4.2.0", - "quay.io/openshift/origin-ironic-static-ip-manager:v4.2.0", - }, - &osconfigv1.Proxy{}, - } -} - -//Testing the case where the password does already exist -func TestCreateMariadbPasswordSecret(t *testing.T) { - kubeClient := fakekube.NewSimpleClientset(nil...) - operatorConfig := newOperatorWithBaremetalConfig() - client := kubeClient.CoreV1() - - // First create a mariadb password secret - if err := createMariadbPasswordSecret(kubeClient.CoreV1(), operatorConfig); err != nil { - t.Fatalf("Failed to create first Mariadb password. %s ", err) - } - // Read and get Mariadb password from Secret just created. - oldMaridbPassword, err := client.Secrets(operatorConfig.TargetNamespace).Get(context.Background(), baremetalSecretName, metav1.GetOptions{}) - if err != nil { - t.Fatal("Failure getting the first Mariadb password that just got created.") - } - oldPassword, ok := oldMaridbPassword.StringData[baremetalSecretKey] - if !ok || oldPassword == "" { - t.Fatal("Failure reading first Mariadb password from Secret.") - } - - // The pasword definitely exists. Try creating again. - if err := createMariadbPasswordSecret(kubeClient.CoreV1(), operatorConfig); err != nil { - t.Fatal("Failure creating second Mariadb password.") - } - newMaridbPassword, err := client.Secrets(operatorConfig.TargetNamespace).Get(context.Background(), baremetalSecretName, metav1.GetOptions{}) - if err != nil { - t.Fatal("Failure getting the second Mariadb password.") - } - newPassword, ok := newMaridbPassword.StringData[baremetalSecretKey] - if !ok || newPassword == "" { - t.Fatal("Failure reading second Mariadb password from Secret.") - } - if oldPassword != newPassword { - t.Fatalf("Both passwords do not match.") - } else { - t.Logf("First Mariadb password is being preserved over re-creation as expected.") - } -} - -func TestGetBaremetalProvisioningConfig(t *testing.T) { - testConfigResource := "test" - u := &unstructured.Unstructured{Object: map[string]interface{}{}} - if err := yaml.Unmarshal([]byte(yamlContent), &u); err != nil { - t.Errorf("failed to unmarshall input yaml content:%v", err) - } - dynamicClient := fakedynamic.NewSimpleDynamicClient(runtime.NewScheme(), u) - baremetalConfig, err := getBaremetalProvisioningConfig(dynamicClient, testConfigResource) - if err != nil { - t.Logf("Unstructed Config: %+v", u) - t.Fatalf("Failed to get Baremetal Provisioning Interface from CR %s", testConfigResource) - } - if baremetalConfig.ProvisioningInterface != expectedProvisioningInterface || - baremetalConfig.ProvisioningIp != expectedProvisioningIP || - baremetalConfig.ProvisioningNetworkCIDR != expectedProvisioningNetworkCIDR || - baremetalConfig.ProvisioningDHCPRange != expectedProvisioningDHCPRange || - baremetalConfig.ProvisioningNetwork != provisioningNetworkManaged { - t.Logf("Expected: ProvisioningInterface: %s, ProvisioningIP: %s, ProvisioningNetworkCIDR: %s, ProvisioningNetwork: %s, expectedProvisioningDHCPRange: %s, Got: %+v", expectedProvisioningInterface, expectedProvisioningIP, expectedProvisioningNetworkCIDR, expectedProvisioningNetwork, expectedProvisioningDHCPRange, baremetalConfig) - t.Fatalf("failed getBaremetalProvisioningConfig. One or more BaremetalProvisioningConfig items do not match the expected config.") - } -} - -func TestGetIncorrectBaremetalProvisioningCR(t *testing.T) { - incorrectConfigResource := "test1" - u := &unstructured.Unstructured{Object: map[string]interface{}{}} - if err := yaml.Unmarshal([]byte(yamlContent), &u); err != nil { - t.Errorf("failed to unmarshall input yaml content:%v", err) - } - dynamicClient := fakedynamic.NewSimpleDynamicClient(runtime.NewScheme(), u) - baremetalConfig, err := getBaremetalProvisioningConfig(dynamicClient, incorrectConfigResource) - if err == nil && baremetalConfig == nil { - t.Logf("Unable to get Baremetal Provisioning Config from CR %s as expected", incorrectConfigResource) - } else { - t.Errorf("BaremetalProvisioningConfig is not expected to be set.") - } -} - -func TestGetMetal3DeploymentConfig(t *testing.T) { - testConfigResource := "test" - u := &unstructured.Unstructured{Object: map[string]interface{}{}} - if err := yaml.Unmarshal([]byte(yamlContent), &u); err != nil { - t.Errorf("failed to unmarshall input yaml content:%v", err) - } - dynamicClient := fakedynamic.NewSimpleDynamicClient(runtime.NewScheme(), u) - baremetalConfig, err := getBaremetalProvisioningConfig(dynamicClient, testConfigResource) - if err != nil { - t.Logf("Unstructed Config: %+v", u) - t.Errorf("Failed to get Baremetal Provisioning Config from CR %s", testConfigResource) - } - actualCacheURL := getMetal3DeploymentConfig("CACHEURL", *baremetalConfig) - if actualCacheURL != nil { - t.Errorf("CacheURL is found to be %s. CACHEURL is not expected.", *actualCacheURL) - } else { - t.Logf("CacheURL is not available as expected.") - } - actualOSImageURL := getMetal3DeploymentConfig("RHCOS_IMAGE_URL", *baremetalConfig) - if actualOSImageURL != nil { - t.Logf("Actual OS Image Download URL is %s, Expected is %s", *actualOSImageURL, expectedOSImageURL) - if *actualOSImageURL != expectedOSImageURL { - t.Errorf("Actual %s and Expected %s OS Image Download URLs do not match", *actualOSImageURL, expectedOSImageURL) - } - } else { - t.Errorf("OS Image Download URL is not available.") - } - actualProvisioningIPCIDR := getMetal3DeploymentConfig("PROVISIONING_IP", *baremetalConfig) - if actualProvisioningIPCIDR != nil { - t.Logf("Actual ProvisioningIP with CIDR is %s, Expected is %s", *actualProvisioningIPCIDR, expectedProvisioningIPCIDR) - if *actualProvisioningIPCIDR != expectedProvisioningIPCIDR { - t.Errorf("Actual %s and Expected %s Provisioning IPs with CIDR do not match", *actualProvisioningIPCIDR, expectedProvisioningIPCIDR) - } - } else { - t.Errorf("Provisioning IP with CIDR is not available.") - } - actualProvisioningInterface := getMetal3DeploymentConfig("PROVISIONING_INTERFACE", *baremetalConfig) - if actualProvisioningInterface != nil { - t.Logf("Actual Provisioning Interface is %s, Expected is %s", *actualProvisioningInterface, expectedProvisioningInterface) - if *actualProvisioningInterface != expectedProvisioningInterface { - t.Errorf("Actual %s and Expected %s Provisioning Interfaces do not match", *actualProvisioningIPCIDR, expectedProvisioningIPCIDR) - } - } else { - t.Errorf("Provisioning Interface is not available.") - } - actualDeployKernelURL := getMetal3DeploymentConfig("DEPLOY_KERNEL_URL", *baremetalConfig) - if actualDeployKernelURL != nil { - t.Logf("Actual Deploy Kernel URL is %s, Expected is %s", *actualDeployKernelURL, expectedDeployKernelURL) - if *actualDeployKernelURL != expectedDeployKernelURL { - t.Errorf("Actual %s and Expected %s Deploy Kernel URLs do not match", *actualDeployKernelURL, expectedDeployKernelURL) - } - } else { - t.Errorf("Deploy Kernel URL is not available.") - } - actualDeployRamdiskURL := getMetal3DeploymentConfig("DEPLOY_RAMDISK_URL", *baremetalConfig) - if actualDeployRamdiskURL != nil { - t.Logf("Actual Deploy Ramdisk URL is %s, Expected is %s", *actualDeployRamdiskURL, expectedDeployRamdiskURL) - if *actualDeployRamdiskURL != expectedDeployRamdiskURL { - t.Errorf("Actual %s and Expected %s Deploy Ramdisk URLs do not match", *actualDeployRamdiskURL, expectedDeployRamdiskURL) - } - } else { - t.Errorf("Deploy Ramdisk URL is not available.") - } - actualIronicEndpoint := getMetal3DeploymentConfig("IRONIC_ENDPOINT", *baremetalConfig) - if actualIronicEndpoint != nil { - t.Logf("Actual Ironic Endpoint is %s, Expected is %s", *actualIronicEndpoint, expectedIronicEndpoint) - if *actualIronicEndpoint != expectedIronicEndpoint { - t.Errorf("Actual %s and Expected %s Ironic Endpoints do not match", *actualIronicEndpoint, expectedIronicEndpoint) - } - } else { - t.Errorf("Ironic Endpoint is not available.") - } - actualIronicInspectorEndpoint := getMetal3DeploymentConfig("IRONIC_INSPECTOR_ENDPOINT", *baremetalConfig) - if actualIronicInspectorEndpoint != nil { - t.Logf("Actual Ironic Inspector Endpoint is %s, Expected is %s", *actualIronicInspectorEndpoint, expectedIronicInspectorEndpoint) - if *actualIronicInspectorEndpoint != expectedIronicInspectorEndpoint { - t.Errorf("Actual %s and Expected %s Ironic Inspector Endpoints do not match", *actualIronicInspectorEndpoint, expectedIronicInspectorEndpoint) - } - } else { - t.Errorf("Ironic Inspector Endpoint is not available.") - } - actualHttpPort := getMetal3DeploymentConfig("HTTP_PORT", *baremetalConfig) - t.Logf("Actual Http Port is %s, Expected is %s", *actualHttpPort, expectedHttpPort) - if *actualHttpPort != expectedHttpPort { - t.Errorf("Actual %s and Expected %s Http Ports do not match", *actualHttpPort, expectedHttpPort) - } - actualDHCPRange := getMetal3DeploymentConfig("DHCP_RANGE", *baremetalConfig) - if actualDHCPRange != nil { - t.Logf("Actual DHCP Range is %s, Expected is %s", *actualDHCPRange, expectedProvisioningDHCPRange) - if *actualDHCPRange != expectedProvisioningDHCPRange { - t.Errorf("Actual %s and Expected %s DHCP Range do not match", *actualDHCPRange, expectedProvisioningDHCPRange) - } - } else { - t.Errorf("Provisioning DHCP Range is not available.") - } -} - -// Test interpretation of different provisioning config -func TestBaremetalProvisionigConfig(t *testing.T) { - testConfigResource := "test" - configNames := []string{"PROVISIONING_IP", "PROVISIONING_INTERFACE", "DEPLOY_KERNEL_URL", "DEPLOY_RAMDISK_URL", "IRONIC_ENDPOINT", "IRONIC_INSPECTOR_ENDPOINT", "HTTP_PORT", "DHCP_RANGE", "RHCOS_IMAGE_URL"} - - testCases := []struct { - name string - config string - expectedConfigValues []string - }{ - { - name: "Managed", - config: Managed, - expectedConfigValues: []string{"172.30.20.3/24", "ensp0", "http://172.30.20.3:6180/images/ironic-python-agent.kernel", "http://172.30.20.3:6180/images/ironic-python-agent.initramfs", "http://172.30.20.3:6385/v1/", "http://172.30.20.3:5050/v1/", expectedHttpPort, "172.30.20.11, 172.30.20.101", "http://172.22.0.1/images/rhcos-44.81.202001171431.0-openstack.x86_64.qcow2.gz?sha256=e98f83a2b9d4043719664a2be75fe8134dc6ca1fdbde807996622f8cc7ecd234"}, - }, - { - name: "Unmanaged", - config: Unmanaged, - expectedConfigValues: []string{"172.30.20.3/24", "ensp0", "http://172.30.20.3:6180/images/ironic-python-agent.kernel", "http://172.30.20.3:6180/images/ironic-python-agent.initramfs", "http://172.30.20.3:6385/v1/", "http://172.30.20.3:5050/v1/", expectedHttpPort, "", "http://172.22.0.1/images/rhcos-44.81.202001171431.0-openstack.x86_64.qcow2.gz?sha256=e98f83a2b9d4043719664a2be75fe8134dc6ca1fdbde807996622f8cc7ecd234"}, - }, - { - name: "Disabled", - config: Disabled, - expectedConfigValues: []string{"172.30.20.3/24", "", "http://172.30.20.3:6180/images/ironic-python-agent.kernel", "http://172.30.20.3:6180/images/ironic-python-agent.initramfs", "http://172.30.20.3:6385/v1/", "http://172.30.20.3:5050/v1/", expectedHttpPort, "", "http://172.22.0.1/images/rhcos-44.81.202001171431.0-openstack.x86_64.qcow2.gz?sha256=e98f83a2b9d4043719664a2be75fe8134dc6ca1fdbde807996622f8cc7ecd234"}, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - g := NewWithT(t) - - t.Logf("Testing tc : %s", tc.name) - u := &unstructured.Unstructured{Object: map[string]interface{}{}} - if err := yaml.Unmarshal([]byte(tc.config), &u); err != nil { - t.Errorf("failed to unmarshall input yaml content:%v", err) - } - dynamicClient := fakedynamic.NewSimpleDynamicClient(runtime.NewScheme(), u) - baremetalConfig, err := getBaremetalProvisioningConfig(dynamicClient, testConfigResource) - if err != nil { - t.Errorf("getBaremetalProvisioningConfig returned err: %v", err) - } - g.Expect(err).To(BeNil()) - - for i, envVar := range configNames { - actualConfigValue := getMetal3DeploymentConfig(envVar, *baremetalConfig) - - g.Expect(*actualConfigValue).To(Equal(tc.expectedConfigValues[i])) - } - }) - } -} - -func TestSyncBaremetalControllers(t *testing.T) { - operatorConfig := newOperatorWithBaremetalConfig() - - stop := make(chan struct{}) - defer close(stop) - optr := newFakeOperator(nil, nil, stop) - - u := &unstructured.Unstructured{Object: map[string]interface{}{}} - if err := yaml.Unmarshal([]byte(yamlContent), &u); err != nil { - t.Errorf("failed to unmarshall input yaml content:%v", err) - } - dynamicClient := fakedynamic.NewSimpleDynamicClient(runtime.NewScheme(), u) - - optr.dynamicClient = dynamicClient - - testCases := []struct { - name string - configCRName string - expectedError error - }{ - { - name: "InvalidCRName", - configCRName: "baremetal-disabled", - expectedError: apierrors.NewNotFound(provisioningGR, "baremetal-disabled"), - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - g := NewWithT(t) - - err := optr.syncBaremetalControllers(operatorConfig, tc.configCRName) - g.Expect(err).To(Equal(tc.expectedError)) - }) - } -} - -func TestCheckMetal3DeploymentOwned(t *testing.T) { - kubeClient := fakekube.NewSimpleClientset(nil...) - operatorConfig := newOperatorWithBaremetalConfig() - client := kubeClient.AppsV1() - - testCases := []struct { - testCase string - deployment *appsv1.Deployment - expected bool - expectedError bool - }{ - { - testCase: "Only maoOwnedAnnotation", - deployment: &appsv1.Deployment{ - TypeMeta: metav1.TypeMeta{ - Kind: "Deployment", - APIVersion: "apps/v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: baremetalDeploymentName, - Annotations: map[string]string{ - maoOwnedAnnotation: "", - }, - }, - }, - expected: true, - }, - { - testCase: "Only cboOwnedAnnotation", - deployment: &appsv1.Deployment{ - TypeMeta: metav1.TypeMeta{ - Kind: "Deployment", - APIVersion: "apps/v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: baremetalDeploymentName, - Annotations: map[string]string{ - cboOwnedAnnotation: "", - }, - }, - }, - expected: false, - }, - { - testCase: "Both cboOwnedAnnotation and maoOwnedAnnotation", - deployment: &appsv1.Deployment{ - TypeMeta: metav1.TypeMeta{ - Kind: "Deployment", - APIVersion: "apps/v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: baremetalDeploymentName, - Annotations: map[string]string{ - cboOwnedAnnotation: "", - maoOwnedAnnotation: "", - }, - }, - }, - expected: false, - }, - { - testCase: "No cboOwnedAnnotation or maoOwnedAnnotation", - deployment: &appsv1.Deployment{ - TypeMeta: metav1.TypeMeta{ - Kind: "Deployment", - APIVersion: "apps/v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: baremetalDeploymentName, - Annotations: map[string]string{}, - }, - }, - expected: true, - }, - } - for _, tc := range testCases { - t.Run(string(tc.testCase), func(t *testing.T) { - - _, err := client.Deployments("test-namespace").Create(context.Background(), tc.deployment, metav1.CreateOptions{}) - if err != nil { - t.Fatalf("Could not create metal3 test deployment.\n") - } - maoOwned, err := checkMetal3DeploymentMAOOwned(client, operatorConfig) - if maoOwned != tc.expected { - t.Errorf("Expected: %v, got: %v", tc.expected, maoOwned) - } - if tc.expectedError != (err != nil) { - t.Errorf("ExpectedError: %v, got: %v", tc.expectedError, err) - } - err = client.Deployments("test-namespace").Delete(context.Background(), baremetalDeploymentName, metav1.DeleteOptions{}) - if err != nil { - t.Errorf("Could not delete metal3 test deployment.\n") - } - }) - } - -} - -func TestCheckForBaremetalClusterOperator(t *testing.T) { - testCases := []struct { - testCase string - clusterOperator *osconfigv1.ClusterOperator - expected bool - expectedError bool - }{ - { - testCase: cboClusterOperatorName, - clusterOperator: &osconfigv1.ClusterOperator{ - TypeMeta: metav1.TypeMeta{ - Kind: "ClusterOperator", - APIVersion: "config.openshift.io/v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: cboClusterOperatorName, - }, - Status: osconfigv1.ClusterOperatorStatus{ - RelatedObjects: []osconfigv1.ObjectReference{ - { - Group: "", - Resource: "namespaces", - Name: "openshift-machine-api", - }, - }, - }, - }, - expected: true, - }, - { - testCase: "invalidCO", - clusterOperator: &osconfigv1.ClusterOperator{ - ObjectMeta: metav1.ObjectMeta{ - Name: "invalidCO", - }, - }, - expected: false, - }, - } - for _, tc := range testCases { - t.Run(string(tc.testCase), func(t *testing.T) { - var osClient *fakeos.Clientset - osClient = fakeos.NewSimpleClientset(tc.clusterOperator) - _, err := osClient.ConfigV1().ClusterOperators().Create(context.Background(), tc.clusterOperator, metav1.CreateOptions{}) - if err != nil && !apierrors.IsAlreadyExists(err) { - t.Fatalf("Unable to create ClusterOperator for test: %v", err) - } - exists, err := checkForBaremetalClusterOperator(osClient) - if exists != tc.expected { - t.Errorf("Expected: %v, got: %v", tc.expected, exists) - } - if tc.expectedError != (err != nil) { - t.Errorf("ExpectedError: %v, got: %v", tc.expectedError, err) - } - err = osClient.ConfigV1().ClusterOperators().Delete(context.Background(), tc.testCase, metav1.DeleteOptions{}) - }) - } -} diff --git a/pkg/operator/config.go b/pkg/operator/config.go index 7fad984b0f..0583a71335 100644 --- a/pkg/operator/config.go +++ b/pkg/operator/config.go @@ -20,10 +20,9 @@ type Provider string // OperatorConfig contains configuration for MAO type OperatorConfig struct { - TargetNamespace string `json:"targetNamespace"` - Controllers Controllers - BaremetalControllers BaremetalControllers - Proxy *configv1.Proxy + TargetNamespace string `json:"targetNamespace"` + Controllers Controllers + Proxy *configv1.Proxy } type Controllers struct { @@ -35,15 +34,6 @@ type Controllers struct { TerminationHandler string } -type BaremetalControllers struct { - BaremetalOperator string - Ironic string - IronicInspector string - IronicIpaDownloader string - IronicMachineOsDownloader string - IronicStaticIpManager string -} - // Images allows build systems to inject images for MAO components type Images struct { MachineAPIOperator string `json:"machineAPIOperator"` @@ -57,13 +47,6 @@ type Images struct { ClusterAPIControllerVSphere string `json:"clusterAPIControllerVSphere"` ClusterAPIControllerKubevirt string `json:"clusterAPIControllerKubevirt"` KubeRBACProxy string `json:"kubeRBACProxy"` - // Images required for the metal3 pod - BaremetalOperator string `json:"baremetalOperator"` - BaremetalIronic string `json:"baremetalIronic"` - BaremetalIronicInspector string `json:"baremetalIronicInspector"` - BaremetalIpaDownloader string `json:"baremetalIpaDownloader"` - BaremetalMachineOsDownloader string `json:"baremetalMachineOsDownloader"` - BaremetalStaticIpManager string `json:"baremetalStaticIpManager"` } func getProviderFromInfrastructure(infra *configv1.Infrastructure) (configv1.PlatformType, error) { @@ -129,21 +112,6 @@ func getTerminationHandlerFromImages(platform configv1.PlatformType, images Imag } } -// This function returns images required to bring up the Baremetal Pod. -func newBaremetalControllers(images Images, usingBareMetal bool) BaremetalControllers { - if !usingBareMetal { - return BaremetalControllers{} - } - return BaremetalControllers{ - BaremetalOperator: images.BaremetalOperator, - Ironic: images.BaremetalIronic, - IronicInspector: images.BaremetalIronicInspector, - IronicIpaDownloader: images.BaremetalIpaDownloader, - IronicMachineOsDownloader: images.BaremetalMachineOsDownloader, - IronicStaticIpManager: images.BaremetalStaticIpManager, - } -} - 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 3da0ab7f8b..a419b2ba9b 100644 --- a/pkg/operator/config_test.go +++ b/pkg/operator/config_test.go @@ -7,24 +7,18 @@ import ( ) var ( - imagesJSONFile = "fixtures/images.json" - expectedAWSImage = "docker.io/openshift/origin-aws-machine-controllers:v4.0.0" - expectedLibvirtImage = "docker.io/openshift/origin-libvirt-machine-controllers:v4.0.0" - expectedOpenstackImage = "docker.io/openshift/origin-openstack-machine-controllers:v4.0.0" - expectedMachineAPIOperatorImage = "docker.io/openshift/origin-machine-api-operator:v4.0.0" - expectedKubeRBACProxyImage = "docker.io/openshift/origin-kube-rbac-proxy:v4.0.0" - 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:v4.2.0" - expectedIronicInspector = "quay.io/openshift/origin-ironic-inspector:v4.2.0" - expectedIronicIpaDownloader = "quay.io/openshift/origin-ironic-ipa-downloader:v4.2.0" - expectedIronicMachineOsDownloader = "quay.io/openshift/origin-ironic-machine-os-downloader:v4.3.0" - expectedIronicStaticIpManager = "quay.io/openshift/origin-ironic-static-ip-manager:v4.2.0" - expectedOvirtImage = "quay.io/openshift/origin-ovirt-machine-controllers" - expectedVSphereImage = "docker.io/openshift/origin-machine-api-operator:v4.0.0" - expectedKubevirtImage = "quay.io/openshift/origin-kubevirt-machine-controllers" + imagesJSONFile = "fixtures/images.json" + expectedAWSImage = "docker.io/openshift/origin-aws-machine-controllers:v4.0.0" + expectedLibvirtImage = "docker.io/openshift/origin-libvirt-machine-controllers:v4.0.0" + expectedOpenstackImage = "docker.io/openshift/origin-openstack-machine-controllers:v4.0.0" + expectedMachineAPIOperatorImage = "docker.io/openshift/origin-machine-api-operator:v4.0.0" + expectedKubeRBACProxyImage = "docker.io/openshift/origin-kube-rbac-proxy:v4.0.0" + 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" + expectedOvirtImage = "quay.io/openshift/origin-ovirt-machine-controllers" + expectedVSphereImage = "docker.io/openshift/origin-machine-api-operator:v4.0.0" + expectedKubevirtImage = "quay.io/openshift/origin-kubevirt-machine-controllers" ) func TestGetProviderFromInfrastructure(t *testing.T) { @@ -305,21 +299,3 @@ func TestGetKubeRBACProxyFromImages(t *testing.T) { t.Errorf("failed getKubeRBACProxyFromImages. Expected: %s, got: %s", expectedKubeRBACProxyImage, res) } } - -func TestGetBaremetalControllers(t *testing.T) { - img, err := getImagesFromJSONFile(imagesJSONFile) - if err != nil { - t.Errorf("failed getImagesFromJSONFile, %v", err) - } - - baremetalControllers := newBaremetalControllers(*img, true) - - if baremetalControllers.BaremetalOperator != expectedBaremetalOperator || - baremetalControllers.Ironic != expectedIronic || - baremetalControllers.IronicInspector != expectedIronicInspector || - baremetalControllers.IronicIpaDownloader != expectedIronicIpaDownloader || - baremetalControllers.IronicMachineOsDownloader != expectedIronicMachineOsDownloader || - 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 9eab289bb5..da3addcbac 100644 --- a/pkg/operator/fixtures/images.json +++ b/pkg/operator/fixtures/images.json @@ -9,11 +9,5 @@ "clusterAPIControllerOvirt": "quay.io/openshift/origin-ovirt-machine-controllers", "clusterAPIControllerVSphere": "docker.io/openshift/origin-machine-api-operator:v4.0.0", "clusterAPIControllerKubevirt": "quay.io/openshift/origin-kubevirt-machine-controllers", - "baremetalOperator": "quay.io/openshift/origin-baremetal-operator:v4.2.0", - "baremetalIronic": "quay.io/openshift/origin-ironic:v4.2.0", - "baremetalIronicInspector": "quay.io/openshift/origin-ironic-inspector:v4.2.0", - "baremetalIpaDownloader": "quay.io/openshift/origin-ironic-ipa-downloader:v4.2.0", - "baremetalMachineOsDownloader": "quay.io/openshift/origin-ironic-machine-os-downloader:v4.3.0", - "baremetalStaticIpManager": "quay.io/openshift/origin-ironic-static-ip-manager:v4.2.0", "kubeRBACProxy": "docker.io/openshift/origin-kube-rbac-proxy:v4.0.0" } diff --git a/pkg/operator/operator.go b/pkg/operator/operator.go index 9dd70365d2..b94c91eecd 100644 --- a/pkg/operator/operator.go +++ b/pkg/operator/operator.go @@ -35,10 +35,6 @@ const ( // 5ms, 10ms, 20ms, 40ms, 80ms, 160ms, 320ms, 640ms, 1.3s, 2.6s, 5.1s, 10.2s, 20.4s, 41s, 82s maxRetries = 15 maoOwnedAnnotation = "machine.openshift.io/owned" - // Indicates that the metal3 deployment is being managed by cluster-baremetal-operator - cboOwnedAnnotation = "baremetal.openshift.io/owned" - // The name of the clusteroperator for cluster-baremetal-operator - cboClusterOperatorName = "baremetal" ) // Operator defines machine api operator. @@ -352,9 +348,6 @@ func (optr *Operator) maoConfigFromInfrastructure() (*OperatorConfig, error) { return nil, err } - usingBareMetal := provider == osconfigv1.BareMetalPlatformType - baremetalControllers := newBaremetalControllers(*images, usingBareMetal) - machineAPIOperatorImage, err := getMachineAPIOperatorFromImages(*images) if err != nil { return nil, err @@ -381,6 +374,5 @@ func (optr *Operator) maoConfigFromInfrastructure() (*OperatorConfig, error) { KubeRBACProxy: kubeRBACProxy, TerminationHandler: terminationHandlerImage, }, - BaremetalControllers: baremetalControllers, }, nil } diff --git a/pkg/operator/operator_test.go b/pkg/operator/operator_test.go index cb6ab4d2d7..80c1e93fff 100644 --- a/pkg/operator/operator_test.go +++ b/pkg/operator/operator_test.go @@ -380,14 +380,6 @@ func TestMAOConfigFromInfrastructure(t *testing.T) { TerminationHandler: clusterAPIControllerNoOp, KubeRBACProxy: images.KubeRBACProxy, }, - BaremetalControllers: BaremetalControllers{ - BaremetalOperator: images.BaremetalOperator, - Ironic: images.BaremetalIronic, - IronicInspector: images.BaremetalIronicInspector, - IronicIpaDownloader: images.BaremetalIpaDownloader, - IronicMachineOsDownloader: images.BaremetalMachineOsDownloader, - IronicStaticIpManager: images.BaremetalStaticIpManager, - }, }, }, { diff --git a/pkg/operator/sync.go b/pkg/operator/sync.go index 6ff6d628b5..3d94d23132 100644 --- a/pkg/operator/sync.go +++ b/pkg/operator/sync.go @@ -36,7 +36,6 @@ const ( machineExposeMetricsPort = 8441 machineSetExposeMetricsPort = 8442 machineHealthCheckExposeMetricsPort = 8444 - metal3ExposeMetricsPort = 8445 defaultMachineHealthPort = 9440 defaultMachineSetHealthPort = 9441 defaultMachineHealthCheckHealthPort = 9442 @@ -85,26 +84,6 @@ func (optr *Operator) syncAll(config *OperatorConfig) error { } klog.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 != "" { - err := optr.syncBaremetalControllers(config, baremetalProvisioningCR) - switch true { - case apierrors.IsNotFound(err): - klog.V(3).Info("Provisioning configuration not found. Skipping metal3 deployment.") - case err == nil: - klog.V(3).Info("Synced up all metal3 components") - case err != nil: - if err := optr.statusDegraded(err.Error()); err != nil { - // Just log the error here. We still want to - // return the outer error. - klog.Errorf("Error syncing BaremetalOperatorStatus: %v", err) - } - klog.Errorf("Error syncing metal3-controller: %v", err) - return err - } - } - if err := optr.statusAvailable(); err != nil { klog.Errorf("Error syncing ClusterOperatorStatus: %v", err) return fmt.Errorf("error syncing ClusterOperatorStatus: %v", err) @@ -287,74 +266,6 @@ func mergeMutatingWebhooks(expected, current []admissionregistrationv1.MutatingW return out, nil } -func (optr *Operator) syncBaremetalControllers(config *OperatorConfig, configName string) error { - // Stand down if cluster-bare-metal operator has claimed the metal3 deployment - // and if the baremetal clusteroperator exists - maoOwned, err := checkMetal3DeploymentMAOOwned(optr.kubeClient.AppsV1(), config) - if err != nil { - return err - } - if !maoOwned { - cboExists, err := checkForBaremetalClusterOperator(optr.osClient) - if err != nil { - return err - } - if cboExists { - klog.Infof("cluster-baremetal-operator is running and managing the Metal3 deployment, standing down.") - return nil - } - } - - // Try to get baremetal provisioning config from a CR - baremetalProvisioningConfig, err := getBaremetalProvisioningConfig(optr.dynamicClient, configName) - if err == nil && baremetalProvisioningConfig == nil { - return apierrors.NewNotFound(provisioningGR, configName) - } - if err != nil { - return err - } - // Create Secrets needed for the Metal3 deployment - if err := createMetal3PasswordSecrets(optr.kubeClient.CoreV1(), config); err != nil { - klog.Error("Not proceeding with Metal3 deployment.") - return err - } - - metal3Deployment := newMetal3Deployment(config, *baremetalProvisioningConfig) - - metal3Service := newMetal3Service(config, *baremetalProvisioningConfig) - s, updated, err := resourceapply.ApplyService(optr.kubeClient.CoreV1(), - events.NewLoggingEventRecorder(optr.name), metal3Service) - if err != nil { - return err - } - if updated { - klog.V(3).Infof("updated service %s/%s", s.Namespace, s.Name) - } - - metal3ServiceMonitor := []byte(metal3ServiceMonitorDefinition) - updated, err = resourceapply.ApplyServiceMonitor(optr.dynamicClient, - events.NewLoggingEventRecorder(optr.name), metal3ServiceMonitor) - if err != nil { - return err - } - if updated { - klog.V(3).Infof("updated service monitor openshift-machine-api/metal3") - } - - expectedGeneration := resourcemerge.ExpectedDeploymentGeneration(metal3Deployment, optr.generations) - d, updated, err := resourceapply.ApplyDeployment(optr.kubeClient.AppsV1(), - events.NewLoggingEventRecorder(optr.name), metal3Deployment, expectedGeneration) - if err != nil { - return err - } - if updated { - resourcemerge.SetDeploymentGeneration(&optr.generations, d) - return optr.waitForDeploymentRollout(metal3Deployment, deploymentRolloutPollInterval, deploymentRolloutTimeout) - } - - return nil -} - func (optr *Operator) waitForDeploymentRollout(resource *appsv1.Deployment, pollInterval, rolloutTimeout time.Duration) error { var lastError error err := wait.Poll(pollInterval, rolloutTimeout, func() (bool, error) {