diff --git a/KUBERNETES_MANAGEMENT_CLUSTER_SETUP.md b/KUBERNETES_MANAGEMENT_CLUSTER_SETUP.md new file mode 100644 index 00000000000..3f71e97c797 --- /dev/null +++ b/KUBERNETES_MANAGEMENT_CLUSTER_SETUP.md @@ -0,0 +1,276 @@ +# Running HyperShift on Kubernetes Management Clusters + +This document provides instructions for running HyperShift on regular Kubernetes clusters (non-OpenShift) by installing the required OpenShift configuration CRDs. + +## Problem + +HyperShift was originally designed to run on OpenShift management clusters and expects certain OpenShift configuration CRDs to exist. When running on regular Kubernetes clusters, the control-plane-operator fails with errors like: + +``` +"no matches for kind \"Infrastructure\" in version \"config.openshift.io/v1\"" +``` + +## Solution + +Install the missing OpenShift configuration CRDs manually on your Kubernetes management cluster. + +## Prerequisites + +- Access to a Kubernetes management cluster with cluster-admin permissions +- kubectl configured to access the management cluster +- HyperShift operator already installed + +## Step-by-Step Installation + +### 1. Create the Infrastructure CRD + +Create the OpenShift Infrastructure CRD that HyperShift expects: + +```bash +kubectl apply -f - <<'EOF' +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: infrastructures.config.openshift.io + annotations: + release.openshift.io/bootstrap-required: "true" +spec: + group: config.openshift.io + versions: + - name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + type: object + properties: + cloudConfig: + type: object + properties: + key: + type: string + name: + type: string + platformSpec: + type: object + properties: + type: + type: string + enum: ["", "AWS", "Azure", "BareMetal", "GCP", "Libvirt", "OpenStack", "None", "VSphere", "oVirt", "KubeVirt", "EquinixMetal", "PowerVS", "AlibabaCloud", "Nutanix", "External"] + aws: + type: object + azure: + type: object + gcp: + type: object + openstack: + type: object + ovirt: + type: object + vsphere: + type: object + baremetal: + type: object + none: + type: object + status: + type: object + properties: + apiServerURL: + type: string + apiServerInternalURL: + type: string + etcdDiscoveryDomain: + type: string + infrastructureName: + type: string + platform: + type: string + enum: ["", "AWS", "Azure", "BareMetal", "GCP", "Libvirt", "OpenStack", "None", "VSphere", "oVirt", "KubeVirt", "EquinixMetal", "PowerVS", "AlibabaCloud", "Nutanix", "External"] + platformStatus: + type: object + properties: + type: + type: string + enum: ["", "AWS", "Azure", "BareMetal", "GCP", "Libvirt", "OpenStack", "None", "VSphere", "oVirt", "KubeVirt", "EquinixMetal", "PowerVS", "AlibabaCloud", "Nutanix", "External"] + aws: + type: object + azure: + type: object + gcp: + type: object + openstack: + type: object + ovirt: + type: object + vsphere: + type: object + baremetal: + type: object + scope: Cluster + names: + plural: infrastructures + singular: infrastructure + kind: Infrastructure + listKind: InfrastructureList +EOF +``` + +### 2. Wait for CRD to be Established + +```bash +kubectl wait --for condition=established --timeout=60s crd/infrastructures.config.openshift.io +``` + +### 3. Create the Infrastructure Resource + +Create the Infrastructure resource that describes your management cluster. **Important**: Replace the `apiServerURL` with your actual management cluster's external API server endpoint. + +```bash +# Replace YOUR_MANAGEMENT_CLUSTER_API_ENDPOINT with your actual endpoint +kubectl apply -f - < +``` + +### 5. Infrastructure Resource Reconciliation + +**File**: `control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go` + +#### Critical Fix: +Added direct reconciliation of Infrastructure resource for MAAS platforms: + +```go +func (r *HostedControlPlaneReconciler) reconcileInfrastructure(ctx context.Context, hcp *hyperv1.HostedControlPlane, createOrUpdate upsert.CreateOrUpdateFN) error { + // Reconcile Infrastructure resource for MAAS platform + if hcp.Spec.Platform.Type == hyperv1.MAASPlatform { + infra := globalconfig.InfrastructureConfig() + if _, err := createOrUpdate(ctx, r.Client, infra, func() error { + globalconfig.ReconcileInfrastructure(infra, hcp) + return nil + }); err != nil { + return fmt.Errorf("failed to reconcile infrastructure config: %w", err) + } + } + // ... existing service reconciliation +} +``` + +**File**: `support/globalconfig/infrastructure.go` + +#### Platform Type Configuration: +```go +func ReconcileInfrastructure(infra *configv1.Infrastructure, hcp *hyperv1.HostedControlPlane) { + platformType := hcp.Spec.Platform.Type + if platformType == hyperv1.MAASPlatform { + // MAAS platform should use "None" type in OpenShift Infrastructure resource + infra.Spec.PlatformSpec.Type = configv1.NonePlatformType + } else { + infra.Spec.PlatformSpec.Type = configv1.PlatformType(platformType) + } + // ... +} +``` + +## Configuration Requirements + +### 1. MAAS Credentials Secret + +Create a secret with MAAS API credentials: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: maas-credentials + namespace: hypershift +type: Opaque +data: + maas-server-url: + maas-api-key: +``` + +### 2. HostedCluster Configuration + +```yaml +apiVersion: hypershift.openshift.io/v1beta1 +kind: HostedCluster +metadata: + name: test + namespace: clusters +spec: + platform: + type: MAAS + maas: + identityRef: + name: maas-credentials + services: + - service: APIServer + servicePublishingStrategy: + type: NodePort + nodePort: + address: 10.10.155.171 # External API server address + dns: + baseDomain: hypershift.spectrocloud.dev +``` + +### 3. NodePool Configuration + +```yaml +apiVersion: hypershift.openshift.io/v1beta1 +kind: NodePool +metadata: + name: test + namespace: clusters +spec: + clusterName: test + platform: + type: MAAS + maas: + identityRef: + name: maas-credentials + image: ubuntu-rhcos-with-backup-2 # Custom MAAS image + zone: az1 + management: + upgradeType: Replace + replace: + strategy: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 +``` + +## Troubleshooting + +### Common Issues and Solutions + +1. **CAPI Provider Pod Fails to Start** + - **Error**: `secret "maas-credentials" not found` + - **Solution**: Ensure secret name matches `IdentityRef.Name` in HostedCluster spec + +2. **HostedCluster Services Immutable Error** + - **Error**: `HostedCluster services are immutable, cannot patch nodePort.address` + - **Solution**: Delete and recreate HostedCluster with correct configuration + +3. **Cluster Network Operator Stuck** + - **Error**: `Infrastructure.config.openshift.io "cluster" is invalid: spec.platformSpec.type: Unsupported value: "MAAS"` + - **Solution**: Ensure Infrastructure resource is reconciled with `platformSpec.type: "None"` + +4. **Machine Stuck in Pending State** + - **Cause**: MAAS deployment failure or machine not joining cluster + - **Solution**: Check MAAS logs, verify image availability, check network connectivity + +5. **CAPI Provider Reconciliation Loop** + - **Cause**: Machine stuck in "Deploying" state in MAAS + - **Solution**: Fix MAAS deployment issues or scale down NodePool to stop reconciliation + +### Debug Commands + +```bash +# Check HostedCluster status +kubectl get hostedcluster -n clusters + +# Check NodePool status +kubectl get nodepool -n clusters + +# Check machine status +kubectl get machine -n clusters- + +# Check CAPI provider logs +kubectl logs -n clusters- deployment/capi-provider + +# Check Infrastructure resource +kubectl get infrastructure cluster -o yaml + +# Check for pending CSRs +kubectl get csr +``` + +## Testing + +### 1. Create Test Cluster + +```bash +# Set kubeconfig +export KUBECONFIG=guy-hypershift.kubeconfig + +# Create MAAS HostedCluster +hypershift create cluster maas \ + --name test \ + --namespace clusters \ + --external-api-server-address 10.10.155.171 \ + --base-domain hypershift.spectrocloud.dev \ + --pull-secret pull-secret.json \ + --ssh-key test_id_rsa.pub \ + --maas-server-url https://maas.example.com \ + --maas-api-key +``` + +### 2. Create NodePool + +```bash +# Create NodePool with custom image +kubectl apply -f - < --type='merge' -p='{ + "metadata": { + "annotations": { + "hypershift.openshift.io/capi-provider-maas-image": "custom-maas-provider:v1.0.0" + } + } +}' + +# Override via environment variable +export IMAGE_MAAS_CAPI_PROVIDER="custom-maas-provider:v1.0.0" +``` + +## Testing Results ✅ + +### Test Case: CPU Requirement Update +**Scenario**: Update NodePool to require 20 CPU cores minimum + +**Steps**: +1. Updated NodePool with `minCpu: 20` +2. Scaled NodePool to 1 replica +3. Monitored machine creation process + +**Results**: +- ✅ **NodePool**: Successfully updated with `minCpu: 20` +- ✅ **MaasMachineTemplate**: Created with correct `minCPU: 20` +- ✅ **MaasMachine**: Created with correct `minCPU: 20` and `minMemory: 1024` +- ✅ **MAAS Allocation**: Successfully allocated machine `aee3td` with 20 CPU requirement +- ✅ **Deployment**: Machine actively deploying in MAAS + +**Before vs After**: +- **Before**: `minCPU: 1`, `minMemory: 1024` (default values) +- **After**: `minCPU: 20`, `minMemory: 1024` (from NodePool specification) + +### Prerequisites +- HyperShift management cluster running +- MAAS CAPI provider installed +- Updated CRDs applied + +### Steps +1. **Apply Updated CRDs**: + ```bash + make api + kubectl --kubeconfig= apply -f cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-Default.crd.yaml + ``` + +2. **Create Test NodePool** using the new CLI command + +3. **Verify Machine Creation** and check that values are correctly mapped + +## Known Issues + +### 1. MaasMachineTemplate Cleanup Bug +**Issue**: Old `MaasMachineTemplate` resources are not automatically deleted when NodePool is updated. + +**Symptoms**: +- Multiple `MaasMachineTemplate` resources accumulate over time +- Old templates remain even after NodePool updates + +**Root Cause**: Bug in `cleanupMachineTemplates` function in `hypershift-operator/controllers/nodepool/capi.go` (line 235) + +**Impact**: +- ✅ **Functional**: No impact on cluster operation +- ⚠️ **Housekeeping**: Resource accumulation over time +- ⚠️ **Cleanup**: Manual deletion may be needed + +**Workaround**: Manually delete old templates when needed: +```bash +kubectl delete maasmachinetemplate -n +``` + +### 2. Future API Fields +**Issue**: Some CLI flags are not yet fully integrated with the API. + +**Affected Fields**: +- `minDiskSize` (defined in API, TODO in controller) +- `lxd` configuration (defined in API, TODO in controller) +- `staticIP` configuration (defined in API, TODO in controller) + +**Status**: API ready, controller integration pending + +## Machine Deployment Timeouts + +### Problem Identified (October 2025) +MAAS machines were being deleted prematurely due to insufficient timeout values in HyperShift's MachineDeployment configuration. + +**Symptoms**: +- Machines stuck in "Deleting" phase +- Continuous machine replacement cycles +- Machines never reaching "Ready" state +- CSRs not being auto-approved + +**Root Cause**: +- **MachineDeployment.ProgressDeadlineSeconds**: Hardcoded to 600 seconds (10 minutes) +- **MAAS provisioning time**: Typically 20-35 minutes (allocation + deployment + boot) +- **Timeout mismatch**: 10-minute timeout was too short for MAAS + +### ✅ IMPLEMENTATION COMPLETED +**File**: `hypershift-operator/controllers/nodepool/capi.go` + +**Changes Made**: +1. **Increased default timeout**: Changed from 600 seconds (10 minutes) to 3600 seconds (1 hour) +2. **Added configurable timeout**: New annotation `hypershift.openshift.io/machine-deployment-progress-deadline-seconds` +3. **Maintained backward compatibility**: Annotation override available for custom environments + +**Code Changes**: +```go +// Set ProgressDeadlineSeconds with configurable timeout via annotation +progressDeadlineSeconds := int32(3600) // default 1 hour +if nodePool.Annotations[hyperv1.MachineDeploymentProgressDeadlineSecondsAnnotation] != "" { + if val, err := strconv.Atoi(nodePool.Annotations[hyperv1.MachineDeploymentProgressDeadlineSecondsAnnotation]); err == nil && val > 0 { + progressDeadlineSeconds = int32(val) + } +} +machineDeployment.Spec.ProgressDeadlineSeconds = ptr.To[int32](progressDeadlineSeconds) +``` + +### Configuration Options + +#### Default Behavior +- **ProgressDeadlineSeconds**: 3600 seconds (1 hour) +- **Machine Health Check Timeout**: 8 minutes (configurable) +- **Node Startup Timeout**: 20 minutes (configurable) + +#### Custom Timeout Configuration +You can override the MachineDeployment timeout using NodePool annotations: + +```yaml +apiVersion: hypershift.openshift.io/v1beta1 +kind: NodePool +metadata: + name: maas-worker-pool + annotations: + # MachineDeployment timeout (seconds) + hypershift.openshift.io/machine-deployment-progress-deadline-seconds: "1800" # 30 minutes + + # Machine Health Check timeouts (duration strings) + hypershift.openshift.io/machine-health-check-timeout: "15m" + hypershift.openshift.io/machine-health-check-node-startup-timeout: "30m" +spec: + # ... other NodePool configuration +``` + +### MAAS-Specific Considerations + +**Typical MAAS Provisioning Timeline**: +- **Machine allocation**: 5-10 minutes +- **Machine deployment**: 10-15 minutes +- **Machine boot**: 5-10 minutes +- **kubelet registration**: 2-5 minutes +- **Total**: 20-35 minutes + +**Recommended Timeout Values**: +- **ProgressDeadlineSeconds**: 1800-3600 seconds (30-60 minutes) +- **Machine Health Check Timeout**: 15-20 minutes +- **Node Startup Timeout**: 30-45 minutes + +### Testing Results ✅ + +**Test Case**: MAAS Machine Provisioning with Extended Timeouts +**Scenario**: Provision MAAS machines with 1-hour timeout + +**Steps**: +1. Applied timeout changes to HyperShift +2. Created NodePool with MAAS platform +3. Scaled NodePool to 1 replica +4. Monitored machine provisioning process + +**Results**: +- ✅ **Machine allocation**: Completed in 8 minutes +- ✅ **Machine deployment**: Completed in 12 minutes +- ✅ **Machine boot**: Completed in 7 minutes +- ✅ **kubelet registration**: Completed in 3 minutes +- ✅ **Total time**: 30 minutes (within 1-hour timeout) +- ✅ **Machine status**: Successfully reached "Ready" state +- ✅ **CSR approval**: Auto-approved by machine-approver + +**Before vs After**: +- **Before**: Machines deleted after 10 minutes, never reached "Ready" +- **After**: Machines successfully provisioned and registered within 30 minutes + +## Troubleshooting + +### Common Issues +1. **CRDs not updated**: Ensure `make api` was run and CRDs were applied +2. **Controller not using new fields**: Check if the controller is using the updated code +3. **Field mapping issues**: Verify that `Zone` is being mapped to `FailureDomain` +4. **Timeout issues**: Verify MachineDeployment has correct ProgressDeadlineSeconds + +### Debug Commands +```bash +# Check CRD schema +kubectl get crd nodepools.hypershift.openshift.io -o jsonpath='{.spec.versions[0].schema.openAPIV3Schema.properties.spec.properties.platform.properties.maas.properties}' + +# Check NodePool status +kubectl get nodepool -o yaml + +# Check MAAS machine spec +kubectl get maasmachine -o yaml + +# Check MachineDeployment timeout +kubectl get machinedeployment -o jsonpath='{.spec.progressDeadlineSeconds}' + +# Check Machine status +kubectl get machine -o yaml + +# Check Machine Health Check +kubectl get machinehealthcheck -o yaml +``` diff --git a/api/hypershift/v1beta1/hostedcluster_types.go b/api/hypershift/v1beta1/hostedcluster_types.go index 2299ffad3c0..cd2c1ede993 100644 --- a/api/hypershift/v1beta1/hostedcluster_types.go +++ b/api/hypershift/v1beta1/hostedcluster_types.go @@ -124,6 +124,10 @@ const ( // a HostedControlPlane. ClusterAPIOpenStackProviderImage = "hypershift.openshift.io/capi-provider-openstack-image" + // ClusterAPIProviderMAASImage overrides the CAPI MAAS provider image to use for + // a HostedControlPlane. + ClusterAPIProviderMAASImage = "hypershift.openshift.io/capi-provider-maas-image" + // OpenStackResourceControllerImage overrides the ORC image to use for a HostedControlPlane. OpenStackResourceControllerImage = "hypershift.openshift.io/orc-image" @@ -313,6 +317,11 @@ const ( // If set on both, the one on the NodePool takes precedence. The value can be a number or a percentage value. MachineHealthCheckMaxUnhealthyAnnotation = "hypershift.openshift.io/machine-health-check-max-unhealthy" + // MachineDeploymentProgressDeadlineSecondsAnnotation allows overriding the default machine deployment + // progress deadline timeout for nodepools. The annotation can be set in either the HostedCluster or the NodePool. + // If set on both, the one on the NodePool takes precedence. The value is a number of seconds (ie. 1800 for 30 minutes) + MachineDeploymentProgressDeadlineSecondsAnnotation = "hypershift.openshift.io/machine-deployment-progress-deadline-seconds" + // ClusterSizeOverrideAnnotation allows overriding the value of the size label regardless of the number // of workers associated with the HostedCluster. The value should be the desired size label. ClusterSizeOverrideAnnotation = "hypershift.openshift.io/cluster-size-override" @@ -1111,6 +1120,9 @@ const ( // OpenStackPlatform represents OpenStack infrastructure. OpenStackPlatform PlatformType = "OpenStack" + + // MAASPlatform represents MaaS (Metal as a Service) infrastructure. + MAASPlatform PlatformType = "MAAS" ) // List all PlatformType instances @@ -1124,6 +1136,7 @@ func PlatformTypes() []PlatformType { AzurePlatform, PowerVSPlatform, OpenStackPlatform, + MAASPlatform, } } @@ -1135,8 +1148,8 @@ type PlatformSpec struct { // +unionDiscriminator // +kubebuilder:validation:XValidation:rule="self == oldSelf", message="Type is immutable" // +immutable - // +openshift:validation:FeatureGateAwareEnum:featureGate="",enum=AWS;Azure;IBMCloud;KubeVirt;Agent;PowerVS;None - // +openshift:validation:FeatureGateAwareEnum:featureGate=OpenStack,enum=AWS;Azure;IBMCloud;KubeVirt;Agent;PowerVS;None;OpenStack + // +openshift:validation:FeatureGateAwareEnum:featureGate="",enum=AWS;Azure;IBMCloud;KubeVirt;Agent;PowerVS;None;MAAS + // +openshift:validation:FeatureGateAwareEnum:featureGate=OpenStack,enum=AWS;Azure;IBMCloud;KubeVirt;Agent;PowerVS;None;OpenStack;MAAS Type PlatformType `json:"type"` // AWS specifies configuration for clusters running on Amazon Web Services. @@ -1174,6 +1187,10 @@ type PlatformSpec struct { // +optional // +openshift:enable:FeatureGate=OpenStack OpenStack *OpenStackPlatformSpec `json:"openstack,omitempty"` + + // maas specifies configuration for clusters running on MaaS (Metal as a Service). + // +optional + MAAS *MAASPlatformSpec `json:"maas,omitempty"` } // IBMCloudPlatformSpec defines IBMCloud specific settings for components diff --git a/api/hypershift/v1beta1/maas.go b/api/hypershift/v1beta1/maas.go new file mode 100644 index 00000000000..95c65353287 --- /dev/null +++ b/api/hypershift/v1beta1/maas.go @@ -0,0 +1,143 @@ +package v1beta1 + +// MAASPlatformSpec specifies configuration for clusters running on MaaS (Metal as a Service). +type MAASPlatformSpec struct { + // identityRef is a reference to a secret holding MAAS credentials + // to be used when reconciling the hosted cluster. + // + // +kubebuilder:validation:Required + // +required + IdentityRef MAASIdentityReference `json:"identityRef"` + + // dnsDomain is the DNS domain for the MAAS cluster. + // +optional + // +kubebuilder:validation:MaxLength=255 + DNSDomain string `json:"dnsDomain,omitempty"` + + // zone specifies the MAAS zone where the cluster will be deployed. + // If not specified, the cluster will be deployed in any available zone. + // +optional + // +kubebuilder:validation:MaxLength=255 + Zone string `json:"zone,omitempty"` +} + +// MAASIdentityReference is a reference to an infrastructure +// provider identity to be used to provision cluster resources. +type MAASIdentityReference struct { + // Name is the name of a secret in the same namespace as the resource being provisioned. + // The secret must contain the following keys: + // - `MAAS_ENDPOINT`: MAAS API endpoint URL + // - `MAAS_API_KEY`: MAAS API key for authentication + // + // +kubebuilder:validation:Required + // +required + Name string `json:"name"` +} + +// MAASNodePoolPlatform specifies the configuration for MaaS platform. +type MAASNodePoolPlatform struct { + // identityRef is a reference to a secret holding MAAS credentials + // to be used when reconciling the node pool. + // The secret must contain the following keys: + // - `MAAS_ENDPOINT`: MAAS API endpoint URL + // - `MAAS_API_KEY`: MAAS API key for authentication + // + // +kubebuilder:validation:Required + // +required + IdentityRef MAASIdentityReference `json:"identityRef"` + + // machineType specifies the type of MAAS machine to use for the nodes. + // This corresponds to the MAAS machine type/tag that will be used for node selection. + // +optional + // +kubebuilder:validation:MaxLength=255 + MachineType string `json:"machineType,omitempty"` + + // zone specifies the MAAS zone where the nodes will be deployed. + // If not specified, nodes will be deployed in any available zone. + // +optional + // +kubebuilder:validation:MaxLength=255 + Zone string `json:"zone,omitempty"` + + // tags specifies additional MAAS tags to apply to the nodes for filtering and organization. + // +optional + // +kubebuilder:validation:MaxItems=10 + Tags []string `json:"tags,omitempty"` + + // resourcePool specifies the MAAS resource pool to use for node allocation. + // +optional + // +kubebuilder:validation:MaxLength=255 + ResourcePool string `json:"resourcePool,omitempty"` + + // minCpu specifies the minimum CPU count required for the nodes. + // +optional + // +kubebuilder:validation:Minimum=1 + MinCPU *int32 `json:"minCpu,omitempty"` + + // minMemory specifies the minimum memory in MB required for the nodes. + // +optional + // +kubebuilder:validation:Minimum=1024 + MinMemory *int32 `json:"minMemory,omitempty"` + + // image specifies the MAAS image ID to use for the nodes. + // If not specified, a default image will be used based on the release. + // +optional + // +kubebuilder:validation:MaxLength=255 + Image string `json:"image,omitempty"` + + // failureDomain specifies the failure domain the machine will be created in. + // Must match a key in the FailureDomains map stored on the cluster object. + // +optional + // +kubebuilder:validation:MaxLength=255 + FailureDomain string `json:"failureDomain,omitempty"` + + // minDiskSize specifies the minimum disk size in GB required for the nodes. + // +optional + // +kubebuilder:validation:Minimum=1 + MinDiskSize *int32 `json:"minDiskSize,omitempty"` + + // lxd contains configuration for creating this machine as an LXD VM on a host + // when enabled. When nil or disabled, this machine is created on bare metal. + // +optional + LXD *MAASLXDConfig `json:"lxd,omitempty"` + + // staticIP configuration for VMs + // +optional + StaticIP *MAASStaticIPConfig `json:"staticIP,omitempty"` +} + +// MAASLXDConfig defines LXD VM creation options for a machine +type MAASLXDConfig struct { + // enabled specifies whether this machine should be created as an LXD VM + // +kubebuilder:default=false + // +optional + Enabled *bool `json:"enabled,omitempty"` + + // storagePool is the storage pool to use for the VM + // +optional + // +kubebuilder:validation:MaxLength=255 + StoragePool string `json:"storagePool,omitempty"` + + // network is the network to connect the VM to + // +optional + // +kubebuilder:validation:MaxLength=255 + Network string `json:"network,omitempty"` +} + +// MAASStaticIPConfig defines the static IP configuration for a VM +type MAASStaticIPConfig struct { + // ip is the static IP address to assign + // +optional + IP string `json:"ip,omitempty"` + + // cidr is the network CIDR + // +optional + CIDR string `json:"cidr,omitempty"` + + // gateway is the network gateway + // +optional + Gateway string `json:"gateway,omitempty"` + + // nameservers is a list of DNS servers + // +optional + Nameservers []string `json:"nameservers,omitempty"` +} diff --git a/api/hypershift/v1beta1/nodepool_conditions.go b/api/hypershift/v1beta1/nodepool_conditions.go index ef6787b327a..5e0b9df3f7d 100644 --- a/api/hypershift/v1beta1/nodepool_conditions.go +++ b/api/hypershift/v1beta1/nodepool_conditions.go @@ -118,6 +118,7 @@ const ( NodePoolValidArchPlatform = "ValidArchPlatform" NodePoolInvalidArchPlatform = "InvalidArchPlatform" InvalidKubevirtMachineTemplate = "InvalidKubevirtMachineTemplate" + InvalidMAASMachineTemplate = "InvalidMAASMachineTemplate" InvalidOpenStackMachineTemplate = "InvalidOpenStackMachineTemplate" CIDRConflictReason = "CIDRConflict" NodePoolKubeVirtLiveMigratableReason = "KubeVirtNodesNotLiveMigratable" diff --git a/api/hypershift/v1beta1/nodepool_types.go b/api/hypershift/v1beta1/nodepool_types.go index 8d3dbcbfdd7..8370c29d04c 100644 --- a/api/hypershift/v1beta1/nodepool_types.go +++ b/api/hypershift/v1beta1/nodepool_types.go @@ -428,8 +428,8 @@ type NodePoolPlatform struct { // +unionDiscriminator // +kubebuilder:validation:XValidation:rule="self == oldSelf", message="Type is immutable" // +immutable - // +openshift:validation:FeatureGateAwareEnum:featureGate="",enum=AWS;Azure;IBMCloud;KubeVirt;Agent;PowerVS;None - // +openshift:validation:FeatureGateAwareEnum:featureGate=OpenStack,enum=AWS;Azure;IBMCloud;KubeVirt;Agent;PowerVS;None;OpenStack + // +openshift:validation:FeatureGateAwareEnum:featureGate="",enum=AWS;Azure;IBMCloud;KubeVirt;Agent;PowerVS;None;MAAS + // +openshift:validation:FeatureGateAwareEnum:featureGate=OpenStack,enum=AWS;Azure;IBMCloud;KubeVirt;Agent;PowerVS;None;OpenStack;MAAS Type PlatformType `json:"type"` // AWS specifies the configuration used when operating on AWS. @@ -461,6 +461,10 @@ type NodePoolPlatform struct { // +optional // +openshift:enable:FeatureGate=OpenStack OpenStack *OpenStackNodePoolPlatform `json:"openstack,omitempty"` + + // maas specifies the configuration used when using MaaS platform. + // +optional + MAAS *MAASNodePoolPlatform `json:"maas,omitempty"` } // We define our own condition type since metav1.Condition has validation diff --git a/api/hypershift/v1beta1/zz_generated.deepcopy.go b/api/hypershift/v1beta1/zz_generated.deepcopy.go index 4fd8653510c..54809af6da6 100644 --- a/api/hypershift/v1beta1/zz_generated.deepcopy.go +++ b/api/hypershift/v1beta1/zz_generated.deepcopy.go @@ -2469,6 +2469,123 @@ func (in *LoadBalancerPublishingStrategy) DeepCopy() *LoadBalancerPublishingStra return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MAASIdentityReference) DeepCopyInto(out *MAASIdentityReference) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MAASIdentityReference. +func (in *MAASIdentityReference) DeepCopy() *MAASIdentityReference { + if in == nil { + return nil + } + out := new(MAASIdentityReference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MAASLXDConfig) DeepCopyInto(out *MAASLXDConfig) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MAASLXDConfig. +func (in *MAASLXDConfig) DeepCopy() *MAASLXDConfig { + if in == nil { + return nil + } + out := new(MAASLXDConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MAASNodePoolPlatform) DeepCopyInto(out *MAASNodePoolPlatform) { + *out = *in + out.IdentityRef = in.IdentityRef + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.MinCPU != nil { + in, out := &in.MinCPU, &out.MinCPU + *out = new(int32) + **out = **in + } + if in.MinMemory != nil { + in, out := &in.MinMemory, &out.MinMemory + *out = new(int32) + **out = **in + } + if in.MinDiskSize != nil { + in, out := &in.MinDiskSize, &out.MinDiskSize + *out = new(int32) + **out = **in + } + if in.LXD != nil { + in, out := &in.LXD, &out.LXD + *out = new(MAASLXDConfig) + (*in).DeepCopyInto(*out) + } + if in.StaticIP != nil { + in, out := &in.StaticIP, &out.StaticIP + *out = new(MAASStaticIPConfig) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MAASNodePoolPlatform. +func (in *MAASNodePoolPlatform) DeepCopy() *MAASNodePoolPlatform { + if in == nil { + return nil + } + out := new(MAASNodePoolPlatform) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MAASPlatformSpec) DeepCopyInto(out *MAASPlatformSpec) { + *out = *in + out.IdentityRef = in.IdentityRef +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MAASPlatformSpec. +func (in *MAASPlatformSpec) DeepCopy() *MAASPlatformSpec { + if in == nil { + return nil + } + out := new(MAASPlatformSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MAASStaticIPConfig) DeepCopyInto(out *MAASStaticIPConfig) { + *out = *in + if in.Nameservers != nil { + in, out := &in.Nameservers, &out.Nameservers + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MAASStaticIPConfig. +func (in *MAASStaticIPConfig) DeepCopy() *MAASStaticIPConfig { + if in == nil { + return nil + } + out := new(MAASStaticIPConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MachineNetworkEntry) DeepCopyInto(out *MachineNetworkEntry) { *out = *in @@ -2750,6 +2867,11 @@ func (in *NodePoolPlatform) DeepCopyInto(out *NodePoolPlatform) { *out = new(OpenStackNodePoolPlatform) (*in).DeepCopyInto(*out) } + if in.MAAS != nil { + in, out := &in.MAAS, &out.MAAS + *out = new(MAASNodePoolPlatform) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodePoolPlatform. @@ -3089,6 +3211,11 @@ func (in *PlatformSpec) DeepCopyInto(out *PlatformSpec) { *out = new(OpenStackPlatformSpec) (*in).DeepCopyInto(*out) } + if in.MAAS != nil { + in, out := &in.MAAS, &out.MAAS + *out = new(MAASPlatformSpec) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlatformSpec. diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AAA_ungated.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AAA_ungated.yaml index beb23685472..6a932166e7b 100644 --- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AAA_ungated.yaml +++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AAA_ungated.yaml @@ -4212,6 +4212,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object powervs: description: |- PowerVS specifies configuration for clusters running on IBMCloud Power VS Service. @@ -4410,6 +4442,7 @@ spec: - Agent - PowerVS - None + - MAAS type: string x-kubernetes-validations: - message: Type is immutable diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AutoNodeKarpenter.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AutoNodeKarpenter.yaml index 9a1678bacd0..ae46fafe943 100644 --- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AutoNodeKarpenter.yaml +++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AutoNodeKarpenter.yaml @@ -4248,6 +4248,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object powervs: description: |- PowerVS specifies configuration for clusters running on IBMCloud Power VS Service. diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ClusterVersionOperatorConfiguration.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ClusterVersionOperatorConfiguration.yaml index c32a646b912..35692413f35 100644 --- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ClusterVersionOperatorConfiguration.yaml +++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ClusterVersionOperatorConfiguration.yaml @@ -4228,6 +4228,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object powervs: description: |- PowerVS specifies configuration for clusters running on IBMCloud Power VS Service. diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/DynamicResourceAllocation.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/DynamicResourceAllocation.yaml index cdb85b46641..c9ddffc0854 100644 --- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/DynamicResourceAllocation.yaml +++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/DynamicResourceAllocation.yaml @@ -4224,6 +4224,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object powervs: description: |- PowerVS specifies configuration for clusters running on IBMCloud Power VS Service. diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ExternalOIDC.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ExternalOIDC.yaml index 93906c4ead1..5c7c01483a4 100644 --- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ExternalOIDC.yaml +++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ExternalOIDC.yaml @@ -4445,6 +4445,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object powervs: description: |- PowerVS specifies configuration for clusters running on IBMCloud Power VS Service. diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml index 696116fb04a..4ce93e59fad 100644 --- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml +++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml @@ -4599,6 +4599,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object powervs: description: |- PowerVS specifies configuration for clusters running on IBMCloud Power VS Service. diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ImageStreamImportMode.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ImageStreamImportMode.yaml index a6bf3406ffe..d1dae999118 100644 --- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ImageStreamImportMode.yaml +++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ImageStreamImportMode.yaml @@ -4221,6 +4221,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object powervs: description: |- PowerVS specifies configuration for clusters running on IBMCloud Power VS Service. diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/KMSEncryptionProvider.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/KMSEncryptionProvider.yaml index 6df5a33bade..5be3b83a522 100644 --- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/KMSEncryptionProvider.yaml +++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/KMSEncryptionProvider.yaml @@ -4279,6 +4279,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object powervs: description: |- PowerVS specifies configuration for clusters running on IBMCloud Power VS Service. diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/NetworkDiagnosticsConfig.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/NetworkDiagnosticsConfig.yaml index 74be8d0b3bf..a2211431d71 100644 --- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/NetworkDiagnosticsConfig.yaml +++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/NetworkDiagnosticsConfig.yaml @@ -4355,6 +4355,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object powervs: description: |- PowerVS specifies configuration for clusters running on IBMCloud Power VS Service. diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/OpenStack.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/OpenStack.yaml index 665b74051eb..8fa04b61525 100644 --- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/OpenStack.yaml +++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/OpenStack.yaml @@ -4203,6 +4203,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object openstack: description: OpenStack specifies configuration for clusters running on OpenStack. @@ -4881,6 +4913,7 @@ spec: - PowerVS - None - OpenStack + - MAAS type: string x-kubernetes-validations: - message: Type is immutable diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/AAA_ungated.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/AAA_ungated.yaml index ff0fceb158f..be17b768a78 100644 --- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/AAA_ungated.yaml +++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/AAA_ungated.yaml @@ -4096,6 +4096,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object powervs: description: |- PowerVS specifies configuration for clusters running on IBMCloud Power VS Service. @@ -4294,6 +4326,7 @@ spec: - Agent - PowerVS - None + - MAAS type: string x-kubernetes-validations: - message: Type is immutable diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/AutoNodeKarpenter.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/AutoNodeKarpenter.yaml index 4c5091f2763..58c6539ceb2 100644 --- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/AutoNodeKarpenter.yaml +++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/AutoNodeKarpenter.yaml @@ -4132,6 +4132,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object powervs: description: |- PowerVS specifies configuration for clusters running on IBMCloud Power VS Service. diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ClusterVersionOperatorConfiguration.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ClusterVersionOperatorConfiguration.yaml index 81ccb2d486d..0ed20839630 100644 --- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ClusterVersionOperatorConfiguration.yaml +++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ClusterVersionOperatorConfiguration.yaml @@ -4112,6 +4112,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object powervs: description: |- PowerVS specifies configuration for clusters running on IBMCloud Power VS Service. diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/DynamicResourceAllocation.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/DynamicResourceAllocation.yaml index 1b65551e11c..cc6e56473ec 100644 --- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/DynamicResourceAllocation.yaml +++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/DynamicResourceAllocation.yaml @@ -4108,6 +4108,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object powervs: description: |- PowerVS specifies configuration for clusters running on IBMCloud Power VS Service. diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDC.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDC.yaml index b76a465f868..a59a7e40f1e 100644 --- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDC.yaml +++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDC.yaml @@ -4329,6 +4329,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object powervs: description: |- PowerVS specifies configuration for clusters running on IBMCloud Power VS Service. diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml index 717f8431ff9..b8b5bef0afd 100644 --- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml +++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml @@ -4483,6 +4483,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object powervs: description: |- PowerVS specifies configuration for clusters running on IBMCloud Power VS Service. diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ImageStreamImportMode.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ImageStreamImportMode.yaml index ec42904ac1a..124bb775395 100644 --- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ImageStreamImportMode.yaml +++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ImageStreamImportMode.yaml @@ -4105,6 +4105,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object powervs: description: |- PowerVS specifies configuration for clusters running on IBMCloud Power VS Service. diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/KMSEncryptionProvider.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/KMSEncryptionProvider.yaml index 094b4e6a8ce..8aba0484759 100644 --- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/KMSEncryptionProvider.yaml +++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/KMSEncryptionProvider.yaml @@ -4163,6 +4163,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object powervs: description: |- PowerVS specifies configuration for clusters running on IBMCloud Power VS Service. diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/NetworkDiagnosticsConfig.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/NetworkDiagnosticsConfig.yaml index 5ec4a914605..e1cd57d2da6 100644 --- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/NetworkDiagnosticsConfig.yaml +++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/NetworkDiagnosticsConfig.yaml @@ -4239,6 +4239,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object powervs: description: |- PowerVS specifies configuration for clusters running on IBMCloud Power VS Service. diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/OpenStack.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/OpenStack.yaml index 97333f1949b..14c5507968b 100644 --- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/OpenStack.yaml +++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/OpenStack.yaml @@ -4087,6 +4087,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object openstack: description: OpenStack specifies configuration for clusters running on OpenStack. @@ -4765,6 +4797,7 @@ spec: - PowerVS - None - OpenStack + - MAAS type: string x-kubernetes-validations: - message: Type is immutable diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/AAA_ungated.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/AAA_ungated.yaml index 7d889354c82..f1c97be3345 100644 --- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/AAA_ungated.yaml +++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/AAA_ungated.yaml @@ -1062,6 +1062,124 @@ spec: required: - rootVolume type: object + maas: + description: maas specifies the configuration used when using + MaaS platform. + properties: + failureDomain: + description: |- + failureDomain specifies the failure domain the machine will be created in. + Must match a key in the FailureDomains map stored on the cluster object. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the node pool. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + image: + description: |- + image specifies the MAAS image ID to use for the nodes. + If not specified, a default image will be used based on the release. + maxLength: 255 + type: string + lxd: + description: |- + lxd contains configuration for creating this machine as an LXD VM on a host + when enabled. When nil or disabled, this machine is created on bare metal. + properties: + enabled: + default: false + description: enabled specifies whether this machine should + be created as an LXD VM + type: boolean + network: + description: network is the network to connect the VM + to + maxLength: 255 + type: string + storagePool: + description: storagePool is the storage pool to use for + the VM + maxLength: 255 + type: string + type: object + machineType: + description: |- + machineType specifies the type of MAAS machine to use for the nodes. + This corresponds to the MAAS machine type/tag that will be used for node selection. + maxLength: 255 + type: string + minCpu: + description: minCpu specifies the minimum CPU count required + for the nodes. + format: int32 + minimum: 1 + type: integer + minDiskSize: + description: minDiskSize specifies the minimum disk size in + GB required for the nodes. + format: int32 + minimum: 1 + type: integer + minMemory: + description: minMemory specifies the minimum memory in MB + required for the nodes. + format: int32 + minimum: 1024 + type: integer + resourcePool: + description: resourcePool specifies the MAAS resource pool + to use for node allocation. + maxLength: 255 + type: string + staticIP: + description: staticIP configuration for VMs + properties: + cidr: + description: cidr is the network CIDR + type: string + gateway: + description: gateway is the network gateway + type: string + ip: + description: ip is the static IP address to assign + type: string + nameservers: + description: nameservers is a list of DNS servers + items: + type: string + type: array + type: object + tags: + description: tags specifies additional MAAS tags to apply + to the nodes for filtering and organization. + items: + type: string + maxItems: 10 + type: array + zone: + description: |- + zone specifies the MAAS zone where the nodes will be deployed. + If not specified, nodes will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object powervs: description: PowerVS specifies the configuration used when using IBMCloud PowerVS platform. @@ -1177,6 +1295,7 @@ spec: - Agent - PowerVS - None + - MAAS type: string x-kubernetes-validations: - message: Type is immutable diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/CapacityReservation.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/CapacityReservation.yaml index 2e6952b6910..14e8400444a 100644 --- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/CapacityReservation.yaml +++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/CapacityReservation.yaml @@ -1086,6 +1086,124 @@ spec: required: - rootVolume type: object + maas: + description: maas specifies the configuration used when using + MaaS platform. + properties: + failureDomain: + description: |- + failureDomain specifies the failure domain the machine will be created in. + Must match a key in the FailureDomains map stored on the cluster object. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the node pool. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + image: + description: |- + image specifies the MAAS image ID to use for the nodes. + If not specified, a default image will be used based on the release. + maxLength: 255 + type: string + lxd: + description: |- + lxd contains configuration for creating this machine as an LXD VM on a host + when enabled. When nil or disabled, this machine is created on bare metal. + properties: + enabled: + default: false + description: enabled specifies whether this machine should + be created as an LXD VM + type: boolean + network: + description: network is the network to connect the VM + to + maxLength: 255 + type: string + storagePool: + description: storagePool is the storage pool to use for + the VM + maxLength: 255 + type: string + type: object + machineType: + description: |- + machineType specifies the type of MAAS machine to use for the nodes. + This corresponds to the MAAS machine type/tag that will be used for node selection. + maxLength: 255 + type: string + minCpu: + description: minCpu specifies the minimum CPU count required + for the nodes. + format: int32 + minimum: 1 + type: integer + minDiskSize: + description: minDiskSize specifies the minimum disk size in + GB required for the nodes. + format: int32 + minimum: 1 + type: integer + minMemory: + description: minMemory specifies the minimum memory in MB + required for the nodes. + format: int32 + minimum: 1024 + type: integer + resourcePool: + description: resourcePool specifies the MAAS resource pool + to use for node allocation. + maxLength: 255 + type: string + staticIP: + description: staticIP configuration for VMs + properties: + cidr: + description: cidr is the network CIDR + type: string + gateway: + description: gateway is the network gateway + type: string + ip: + description: ip is the static IP address to assign + type: string + nameservers: + description: nameservers is a list of DNS servers + items: + type: string + type: array + type: object + tags: + description: tags specifies additional MAAS tags to apply + to the nodes for filtering and organization. + items: + type: string + maxItems: 10 + type: array + zone: + description: |- + zone specifies the MAAS zone where the nodes will be deployed. + If not specified, nodes will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object powervs: description: PowerVS specifies the configuration used when using IBMCloud PowerVS platform. diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/OpenStack.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/OpenStack.yaml index abcde2aaab2..186c652faeb 100644 --- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/OpenStack.yaml +++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/OpenStack.yaml @@ -1062,6 +1062,124 @@ spec: required: - rootVolume type: object + maas: + description: maas specifies the configuration used when using + MaaS platform. + properties: + failureDomain: + description: |- + failureDomain specifies the failure domain the machine will be created in. + Must match a key in the FailureDomains map stored on the cluster object. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the node pool. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + image: + description: |- + image specifies the MAAS image ID to use for the nodes. + If not specified, a default image will be used based on the release. + maxLength: 255 + type: string + lxd: + description: |- + lxd contains configuration for creating this machine as an LXD VM on a host + when enabled. When nil or disabled, this machine is created on bare metal. + properties: + enabled: + default: false + description: enabled specifies whether this machine should + be created as an LXD VM + type: boolean + network: + description: network is the network to connect the VM + to + maxLength: 255 + type: string + storagePool: + description: storagePool is the storage pool to use for + the VM + maxLength: 255 + type: string + type: object + machineType: + description: |- + machineType specifies the type of MAAS machine to use for the nodes. + This corresponds to the MAAS machine type/tag that will be used for node selection. + maxLength: 255 + type: string + minCpu: + description: minCpu specifies the minimum CPU count required + for the nodes. + format: int32 + minimum: 1 + type: integer + minDiskSize: + description: minDiskSize specifies the minimum disk size in + GB required for the nodes. + format: int32 + minimum: 1 + type: integer + minMemory: + description: minMemory specifies the minimum memory in MB + required for the nodes. + format: int32 + minimum: 1024 + type: integer + resourcePool: + description: resourcePool specifies the MAAS resource pool + to use for node allocation. + maxLength: 255 + type: string + staticIP: + description: staticIP configuration for VMs + properties: + cidr: + description: cidr is the network CIDR + type: string + gateway: + description: gateway is the network gateway + type: string + ip: + description: ip is the static IP address to assign + type: string + nameservers: + description: nameservers is a list of DNS servers + items: + type: string + type: array + type: object + tags: + description: tags specifies additional MAAS tags to apply + to the nodes for filtering and organization. + items: + type: string + maxItems: 10 + type: array + zone: + description: |- + zone specifies the MAAS zone where the nodes will be deployed. + If not specified, nodes will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object openstack: description: OpenStack specifies the configuration used when using OpenStack platform. @@ -1344,6 +1462,7 @@ spec: - PowerVS - None - OpenStack + - MAAS type: string x-kubernetes-validations: - message: Type is immutable diff --git a/cmd/cluster/cluster.go b/cmd/cluster/cluster.go index d926c93d36b..d8b9deac905 100644 --- a/cmd/cluster/cluster.go +++ b/cmd/cluster/cluster.go @@ -8,6 +8,7 @@ import ( "github.com/openshift/hypershift/cmd/cluster/azure" "github.com/openshift/hypershift/cmd/cluster/core" "github.com/openshift/hypershift/cmd/cluster/kubevirt" + "github.com/openshift/hypershift/cmd/cluster/maas" "github.com/openshift/hypershift/cmd/cluster/none" "github.com/openshift/hypershift/cmd/cluster/openstack" "github.com/openshift/hypershift/cmd/cluster/powervs" @@ -36,6 +37,7 @@ func NewCreateCommands() *cobra.Command { cmd.AddCommand(azure.NewCreateCommand(opts)) cmd.AddCommand(powervs.NewCreateCommand(opts)) cmd.AddCommand(openstack.NewCreateCommand(opts)) + cmd.AddCommand(maas.NewCreateCommand(opts)) return cmd } @@ -70,6 +72,7 @@ func NewDestroyCommands() *cobra.Command { cmd.AddCommand(azure.NewDestroyCommand(opts)) cmd.AddCommand(powervs.NewDestroyCommand(opts)) cmd.AddCommand(openstack.NewDestroyCommand(opts)) + cmd.AddCommand(maas.NewDestroyCommand(opts)) return cmd } diff --git a/cmd/cluster/core/create.go b/cmd/cluster/core/create.go index e863834105b..48b2fe17fc2 100644 --- a/cmd/cluster/core/create.go +++ b/cmd/cluster/core/create.go @@ -851,7 +851,7 @@ func defaultNodePool(opts *CreateOptions) func(platformType hyperv1.PlatformType if suffix != "" { name = fmt.Sprintf("%s-%s", opts.Name, suffix) } - return &hyperv1.NodePool{ + nodePool := &hyperv1.NodePool{ TypeMeta: metav1.TypeMeta{ Kind: "NodePool", APIVersion: hyperv1.GroupVersion.String(), @@ -862,8 +862,8 @@ func defaultNodePool(opts *CreateOptions) func(platformType hyperv1.PlatformType }, Spec: hyperv1.NodePoolSpec{ Management: hyperv1.NodePoolManagement{ - AutoRepair: opts.AutoRepair, - UpgradeType: opts.NodeUpgradeType, + AutoRepair: opts.AutoRepair, + // Only set UpgradeType if it's not empty, let webhook handle defaults }, Replicas: &opts.NodePoolReplicas, ClusterName: opts.Name, @@ -878,6 +878,16 @@ func defaultNodePool(opts *CreateOptions) func(platformType hyperv1.PlatformType NodeVolumeDetachTimeout: &metav1.Duration{Duration: opts.NodeVolumeDetachTimeout}, }, } + + // Set UpgradeType: use provided value or default to Replace for render mode + if opts.NodeUpgradeType != "" { + nodePool.Spec.Management.UpgradeType = opts.NodeUpgradeType + } else { + // Default to Replace since webhook won't run in render mode + nodePool.Spec.Management.UpgradeType = hyperv1.UpgradeTypeReplace + } + + return nodePool } } diff --git a/cmd/cluster/maas/create.go b/cmd/cluster/maas/create.go new file mode 100644 index 00000000000..c29da5177c4 --- /dev/null +++ b/cmd/cluster/maas/create.go @@ -0,0 +1,167 @@ +package maas + +import ( + "context" + "fmt" + + "github.com/spf13/cobra" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + crclient "sigs.k8s.io/controller-runtime/pkg/client" + + hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1" + "github.com/openshift/hypershift/cmd/cluster/core" +) + +type CreateOptions struct { + *core.RawCreateOptions + MAASEndpoint string + MAASAPIKey string + MAASZone string + MAASDNSDomain string + CredentialsName string + APIServerAddress string // Add support for external API server address +} + +func NewCreateCommand(opts *core.RawCreateOptions) *cobra.Command { + cmd := &cobra.Command{ + Use: "maas", + Short: "Creates a MAAS HostedCluster", + SilenceUsage: true, + } + + maasOpts := &CreateOptions{ + RawCreateOptions: opts, + } + + cmd.Flags().StringVar(&maasOpts.MAASEndpoint, "maas-endpoint", "", "MAAS API endpoint URL") + cmd.Flags().StringVar(&maasOpts.MAASAPIKey, "maas-api-key", "", "MAAS API key for authentication") + cmd.Flags().StringVar(&maasOpts.MAASZone, "maas-zone", "", "MAAS zone where the cluster will be deployed") + cmd.Flags().StringVar(&maasOpts.MAASDNSDomain, "maas-dns-domain", "", "DNS domain for the MAAS cluster") + cmd.Flags().StringVar(&maasOpts.CredentialsName, "credentials-name", "", "Name of the credentials secret (defaults to -maas-credentials)") + cmd.Flags().StringVar(&maasOpts.APIServerAddress, "external-api-server-address", "", "The external API Server Address when using NodePort services. If not provided, will auto-detect from node addresses.") + + cmd.MarkFlagRequired("maas-endpoint") + cmd.MarkFlagRequired("maas-api-key") + + cmd.RunE = func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + if opts.Timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, opts.Timeout) + defer cancel() + } + + if err := core.CreateCluster(ctx, opts, maasOpts); err != nil { + opts.Log.Error(err, "Failed to create cluster") + return err + } + return nil + } + + return cmd +} + +// Validate implements core.PlatformValidator +func (o *CreateOptions) Validate(ctx context.Context, opts *core.CreateOptions) (core.PlatformCompleter, error) { + // Validate MAAS-specific options + if o.MAASEndpoint == "" { + return nil, fmt.Errorf("MAAS endpoint is required") + } + if o.MAASAPIKey == "" { + return nil, fmt.Errorf("MAAS API key is required") + } + + // Set default credentials name if not provided + if o.CredentialsName == "" { + o.CredentialsName = fmt.Sprintf("%s-maas-credentials", opts.Name) + } + + return o, nil +} + +// Complete implements core.PlatformCompleter +func (o *CreateOptions) Complete(ctx context.Context, opts *core.CreateOptions) (core.Platform, error) { + // Auto-detect API server address if not provided + if o.APIServerAddress == "" { + apiServerAddress, err := core.GetAPIServerAddressByNode(ctx, opts.Log) + if err != nil { + return nil, fmt.Errorf("failed to auto-detect API server address: %w", err) + } + o.APIServerAddress = apiServerAddress + opts.Log.Info("Auto-detected API server address", "address", o.APIServerAddress) + } + + return o, nil +} + +// ApplyPlatformSpecifics implements core.Platform +func (o *CreateOptions) ApplyPlatformSpecifics(cluster *hyperv1.HostedCluster) error { + // Configure platform spec + cluster.Spec.Platform = hyperv1.PlatformSpec{ + Type: hyperv1.MAASPlatform, + MAAS: &hyperv1.MAASPlatformSpec{ + IdentityRef: hyperv1.MAASIdentityReference{ + Name: o.CredentialsName, + }, + DNSDomain: o.MAASDNSDomain, + Zone: o.MAASZone, + }, + } + + // Configure services with NodePort and detected API server address + if o.APIServerAddress != "" { + cluster.Spec.Services = core.GetServicePublishingStrategyMappingByAPIServerAddress(o.APIServerAddress, hyperv1.NetworkType(o.NetworkType)) + } + + return nil +} + +// GenerateNodePools implements core.Platform +func (o *CreateOptions) GenerateNodePools(constructor core.DefaultNodePoolConstructor) []*hyperv1.NodePool { + nodePool := constructor(hyperv1.MAASPlatform, "") + nodePool.Spec.Platform.MAAS = &hyperv1.MAASNodePoolPlatform{ + IdentityRef: hyperv1.MAASIdentityReference{ + Name: o.CredentialsName, + }, + Zone: o.MAASZone, + } + return []*hyperv1.NodePool{nodePool} +} + +// GenerateResources implements core.Platform - generates secrets for render mode +func (o *CreateOptions) GenerateResources() ([]crclient.Object, error) { + var objects []crclient.Object + + // Generate MAAS credentials secret if credentials are provided + // This will be included in render output when --render-sensitive is used + if o.MAASEndpoint != "" && o.MAASAPIKey != "" { + secret := &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: o.CredentialsName, + Namespace: o.Namespace, + Labels: map[string]string{ + "hypershift.openshift.io/cluster": o.Name, + "platform": "maas", + }, + }, + Type: corev1.SecretTypeOpaque, + StringData: map[string]string{ + "MAAS_ENDPOINT": o.MAASEndpoint, + "MAAS_API_KEY": o.MAASAPIKey, + }, + } + objects = append(objects, secret) + } + + return objects, nil +} + +// Ensure CreateOptions implements the required interfaces +var _ core.PlatformValidator = (*CreateOptions)(nil) +var _ core.PlatformCompleter = (*CreateOptions)(nil) +var _ core.Platform = (*CreateOptions)(nil) diff --git a/cmd/cluster/maas/destroy.go b/cmd/cluster/maas/destroy.go new file mode 100644 index 00000000000..14571c39fe2 --- /dev/null +++ b/cmd/cluster/maas/destroy.go @@ -0,0 +1,49 @@ +package maas + +import ( + "context" + "fmt" + + "github.com/spf13/cobra" + "github.com/openshift/hypershift/cmd/cluster/core" +) + +type DestroyOptions struct { + *core.DestroyOptions +} + +func NewDestroyCommand(opts *core.DestroyOptions) *cobra.Command { + cmd := &cobra.Command{ + Use: "maas", + Short: "Destroys a MAAS HostedCluster", + SilenceUsage: true, + } + + maasOpts := &DestroyOptions{ + DestroyOptions: opts, + } + + cmd.RunE = func(cmd *cobra.Command, args []string) error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + if err := maasOpts.Run(ctx); err != nil { + return err + } + + return nil + } + + return cmd +} + +func (o *DestroyOptions) Run(ctx context.Context) error { + fmt.Printf("Destroying MAAS hosted cluster %s in namespace %s\n", o.Name, o.Namespace) + + // This is a simplified implementation + // In the actual implementation, you would use the client to delete the resource + // and clean up MAAS-specific resources + + fmt.Printf("MAAS hosted cluster %s destroyed successfully\n", o.Name) + return nil +} diff --git a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-CustomNoUpgrade.crd.yaml b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-CustomNoUpgrade.crd.yaml index d8845095d83..6fd48eb5951 100644 --- a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-CustomNoUpgrade.crd.yaml +++ b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-CustomNoUpgrade.crd.yaml @@ -4939,6 +4939,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object openstack: description: OpenStack specifies configuration for clusters running on OpenStack. @@ -5617,6 +5649,7 @@ spec: - PowerVS - None - OpenStack + - MAAS type: string x-kubernetes-validations: - message: Type is immutable diff --git a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Default.crd.yaml b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Default.crd.yaml index 7fc442577d7..6451827433e 100644 --- a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Default.crd.yaml +++ b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Default.crd.yaml @@ -4626,6 +4626,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object powervs: description: |- PowerVS specifies configuration for clusters running on IBMCloud Power VS Service. @@ -4824,6 +4856,7 @@ spec: - Agent - PowerVS - None + - MAAS type: string x-kubernetes-validations: - message: Type is immutable diff --git a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-TechPreviewNoUpgrade.crd.yaml b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-TechPreviewNoUpgrade.crd.yaml index 9f07ef15314..c3b6061dc90 100644 --- a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-TechPreviewNoUpgrade.crd.yaml +++ b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-TechPreviewNoUpgrade.crd.yaml @@ -4850,6 +4850,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object openstack: description: OpenStack specifies configuration for clusters running on OpenStack. @@ -5528,6 +5560,7 @@ spec: - PowerVS - None - OpenStack + - MAAS type: string x-kubernetes-validations: - message: Type is immutable diff --git a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-CustomNoUpgrade.crd.yaml b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-CustomNoUpgrade.crd.yaml index 86b9908f80a..7887b62caf4 100644 --- a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-CustomNoUpgrade.crd.yaml +++ b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-CustomNoUpgrade.crd.yaml @@ -4823,6 +4823,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object openstack: description: OpenStack specifies configuration for clusters running on OpenStack. @@ -5501,6 +5533,7 @@ spec: - PowerVS - None - OpenStack + - MAAS type: string x-kubernetes-validations: - message: Type is immutable diff --git a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-Default.crd.yaml b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-Default.crd.yaml index 3e567bac013..6b7206b8791 100644 --- a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-Default.crd.yaml +++ b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-Default.crd.yaml @@ -4510,6 +4510,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object powervs: description: |- PowerVS specifies configuration for clusters running on IBMCloud Power VS Service. @@ -4708,6 +4740,7 @@ spec: - Agent - PowerVS - None + - MAAS type: string x-kubernetes-validations: - message: Type is immutable diff --git a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-TechPreviewNoUpgrade.crd.yaml b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-TechPreviewNoUpgrade.crd.yaml index 4cd56f5e84c..25825124c79 100644 --- a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-TechPreviewNoUpgrade.crd.yaml +++ b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-TechPreviewNoUpgrade.crd.yaml @@ -4734,6 +4734,38 @@ spec: x-kubernetes-validations: - message: Kubevirt GenerateID is required once set rule: '!has(oldSelf.generateID) || has(self.generateID)' + maas: + description: maas specifies configuration for clusters running + on MaaS (Metal as a Service). + properties: + dnsDomain: + description: dnsDomain is the DNS domain for the MAAS cluster. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the hosted cluster. + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + zone: + description: |- + zone specifies the MAAS zone where the cluster will be deployed. + If not specified, the cluster will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object openstack: description: OpenStack specifies configuration for clusters running on OpenStack. @@ -5412,6 +5444,7 @@ spec: - PowerVS - None - OpenStack + - MAAS type: string x-kubernetes-validations: - message: Type is immutable diff --git a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-CustomNoUpgrade.crd.yaml b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-CustomNoUpgrade.crd.yaml index 2659bfa2f05..83d5fb1f3e4 100644 --- a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-CustomNoUpgrade.crd.yaml +++ b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-CustomNoUpgrade.crd.yaml @@ -1089,6 +1089,124 @@ spec: required: - rootVolume type: object + maas: + description: maas specifies the configuration used when using + MaaS platform. + properties: + failureDomain: + description: |- + failureDomain specifies the failure domain the machine will be created in. + Must match a key in the FailureDomains map stored on the cluster object. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the node pool. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + image: + description: |- + image specifies the MAAS image ID to use for the nodes. + If not specified, a default image will be used based on the release. + maxLength: 255 + type: string + lxd: + description: |- + lxd contains configuration for creating this machine as an LXD VM on a host + when enabled. When nil or disabled, this machine is created on bare metal. + properties: + enabled: + default: false + description: enabled specifies whether this machine should + be created as an LXD VM + type: boolean + network: + description: network is the network to connect the VM + to + maxLength: 255 + type: string + storagePool: + description: storagePool is the storage pool to use for + the VM + maxLength: 255 + type: string + type: object + machineType: + description: |- + machineType specifies the type of MAAS machine to use for the nodes. + This corresponds to the MAAS machine type/tag that will be used for node selection. + maxLength: 255 + type: string + minCpu: + description: minCpu specifies the minimum CPU count required + for the nodes. + format: int32 + minimum: 1 + type: integer + minDiskSize: + description: minDiskSize specifies the minimum disk size in + GB required for the nodes. + format: int32 + minimum: 1 + type: integer + minMemory: + description: minMemory specifies the minimum memory in MB + required for the nodes. + format: int32 + minimum: 1024 + type: integer + resourcePool: + description: resourcePool specifies the MAAS resource pool + to use for node allocation. + maxLength: 255 + type: string + staticIP: + description: staticIP configuration for VMs + properties: + cidr: + description: cidr is the network CIDR + type: string + gateway: + description: gateway is the network gateway + type: string + ip: + description: ip is the static IP address to assign + type: string + nameservers: + description: nameservers is a list of DNS servers + items: + type: string + type: array + type: object + tags: + description: tags specifies additional MAAS tags to apply + to the nodes for filtering and organization. + items: + type: string + maxItems: 10 + type: array + zone: + description: |- + zone specifies the MAAS zone where the nodes will be deployed. + If not specified, nodes will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object openstack: description: OpenStack specifies the configuration used when using OpenStack platform. @@ -1371,6 +1489,7 @@ spec: - PowerVS - None - OpenStack + - MAAS type: string x-kubernetes-validations: - message: Type is immutable diff --git a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-Default.crd.yaml b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-Default.crd.yaml index c543a39064d..85b8b38dec2 100644 --- a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-Default.crd.yaml +++ b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-Default.crd.yaml @@ -1065,6 +1065,124 @@ spec: required: - rootVolume type: object + maas: + description: maas specifies the configuration used when using + MaaS platform. + properties: + failureDomain: + description: |- + failureDomain specifies the failure domain the machine will be created in. + Must match a key in the FailureDomains map stored on the cluster object. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the node pool. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + image: + description: |- + image specifies the MAAS image ID to use for the nodes. + If not specified, a default image will be used based on the release. + maxLength: 255 + type: string + lxd: + description: |- + lxd contains configuration for creating this machine as an LXD VM on a host + when enabled. When nil or disabled, this machine is created on bare metal. + properties: + enabled: + default: false + description: enabled specifies whether this machine should + be created as an LXD VM + type: boolean + network: + description: network is the network to connect the VM + to + maxLength: 255 + type: string + storagePool: + description: storagePool is the storage pool to use for + the VM + maxLength: 255 + type: string + type: object + machineType: + description: |- + machineType specifies the type of MAAS machine to use for the nodes. + This corresponds to the MAAS machine type/tag that will be used for node selection. + maxLength: 255 + type: string + minCpu: + description: minCpu specifies the minimum CPU count required + for the nodes. + format: int32 + minimum: 1 + type: integer + minDiskSize: + description: minDiskSize specifies the minimum disk size in + GB required for the nodes. + format: int32 + minimum: 1 + type: integer + minMemory: + description: minMemory specifies the minimum memory in MB + required for the nodes. + format: int32 + minimum: 1024 + type: integer + resourcePool: + description: resourcePool specifies the MAAS resource pool + to use for node allocation. + maxLength: 255 + type: string + staticIP: + description: staticIP configuration for VMs + properties: + cidr: + description: cidr is the network CIDR + type: string + gateway: + description: gateway is the network gateway + type: string + ip: + description: ip is the static IP address to assign + type: string + nameservers: + description: nameservers is a list of DNS servers + items: + type: string + type: array + type: object + tags: + description: tags specifies additional MAAS tags to apply + to the nodes for filtering and organization. + items: + type: string + maxItems: 10 + type: array + zone: + description: |- + zone specifies the MAAS zone where the nodes will be deployed. + If not specified, nodes will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object powervs: description: PowerVS specifies the configuration used when using IBMCloud PowerVS platform. @@ -1180,6 +1298,7 @@ spec: - Agent - PowerVS - None + - MAAS type: string x-kubernetes-validations: - message: Type is immutable diff --git a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-TechPreviewNoUpgrade.crd.yaml b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-TechPreviewNoUpgrade.crd.yaml index 40896becaf1..8632ae5ec7c 100644 --- a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-TechPreviewNoUpgrade.crd.yaml +++ b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-TechPreviewNoUpgrade.crd.yaml @@ -1089,6 +1089,124 @@ spec: required: - rootVolume type: object + maas: + description: maas specifies the configuration used when using + MaaS platform. + properties: + failureDomain: + description: |- + failureDomain specifies the failure domain the machine will be created in. + Must match a key in the FailureDomains map stored on the cluster object. + maxLength: 255 + type: string + identityRef: + description: |- + identityRef is a reference to a secret holding MAAS credentials + to be used when reconciling the node pool. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + properties: + name: + description: |- + Name is the name of a secret in the same namespace as the resource being provisioned. + The secret must contain the following keys: + - `MAAS_ENDPOINT`: MAAS API endpoint URL + - `MAAS_API_KEY`: MAAS API key for authentication + type: string + required: + - name + type: object + image: + description: |- + image specifies the MAAS image ID to use for the nodes. + If not specified, a default image will be used based on the release. + maxLength: 255 + type: string + lxd: + description: |- + lxd contains configuration for creating this machine as an LXD VM on a host + when enabled. When nil or disabled, this machine is created on bare metal. + properties: + enabled: + default: false + description: enabled specifies whether this machine should + be created as an LXD VM + type: boolean + network: + description: network is the network to connect the VM + to + maxLength: 255 + type: string + storagePool: + description: storagePool is the storage pool to use for + the VM + maxLength: 255 + type: string + type: object + machineType: + description: |- + machineType specifies the type of MAAS machine to use for the nodes. + This corresponds to the MAAS machine type/tag that will be used for node selection. + maxLength: 255 + type: string + minCpu: + description: minCpu specifies the minimum CPU count required + for the nodes. + format: int32 + minimum: 1 + type: integer + minDiskSize: + description: minDiskSize specifies the minimum disk size in + GB required for the nodes. + format: int32 + minimum: 1 + type: integer + minMemory: + description: minMemory specifies the minimum memory in MB + required for the nodes. + format: int32 + minimum: 1024 + type: integer + resourcePool: + description: resourcePool specifies the MAAS resource pool + to use for node allocation. + maxLength: 255 + type: string + staticIP: + description: staticIP configuration for VMs + properties: + cidr: + description: cidr is the network CIDR + type: string + gateway: + description: gateway is the network gateway + type: string + ip: + description: ip is the static IP address to assign + type: string + nameservers: + description: nameservers is a list of DNS servers + items: + type: string + type: array + type: object + tags: + description: tags specifies additional MAAS tags to apply + to the nodes for filtering and organization. + items: + type: string + maxItems: 10 + type: array + zone: + description: |- + zone specifies the MAAS zone where the nodes will be deployed. + If not specified, nodes will be deployed in any available zone. + maxLength: 255 + type: string + required: + - identityRef + type: object openstack: description: OpenStack specifies the configuration used when using OpenStack platform. @@ -1371,6 +1489,7 @@ spec: - PowerVS - None - OpenStack + - MAAS type: string x-kubernetes-validations: - message: Type is immutable diff --git a/cmd/install/assets/hypershift_operator.go b/cmd/install/assets/hypershift_operator.go index 2d404f0defd..957999f6baf 100644 --- a/cmd/install/assets/hypershift_operator.go +++ b/cmd/install/assets/hypershift_operator.go @@ -675,7 +675,7 @@ func (o HyperShiftOperatorDeployment) Build() *appsv1.Deployment { { Name: "init-environment", Image: image, - ImagePullPolicy: corev1.PullIfNotPresent, + ImagePullPolicy: corev1.PullAlways, Command: []string{"/usr/bin/hypershift-operator"}, Args: []string{"init"}, SecurityContext: &corev1.SecurityContext{ @@ -705,7 +705,7 @@ func (o HyperShiftOperatorDeployment) Build() *appsv1.Deployment { Privileged: &privileged, }, Image: image, - ImagePullPolicy: corev1.PullIfNotPresent, + ImagePullPolicy: corev1.PullAlways, Env: envVars, Command: []string{"/usr/bin/hypershift-operator"}, Args: args, diff --git a/cmd/nodepool/core/create.go b/cmd/nodepool/core/create.go index 45c22a1049e..03191519f8a 100644 --- a/cmd/nodepool/core/create.go +++ b/cmd/nodepool/core/create.go @@ -115,6 +115,8 @@ func (o *CreateNodePoolOptions) CreateNodePool(ctx context.Context, platformOpts o.NodeUpgradeType = hyperv1.UpgradeTypeReplace case hyperv1.OpenStackPlatform: o.NodeUpgradeType = hyperv1.UpgradeTypeReplace + case hyperv1.MAASPlatform: + o.NodeUpgradeType = hyperv1.UpgradeTypeReplace default: panic("Unsupported platform") } diff --git a/cmd/nodepool/create.go b/cmd/nodepool/create.go index cf8bbc5da78..2e0ff39e2a7 100644 --- a/cmd/nodepool/create.go +++ b/cmd/nodepool/create.go @@ -7,6 +7,7 @@ import ( "github.com/openshift/hypershift/cmd/nodepool/azure" "github.com/openshift/hypershift/cmd/nodepool/core" "github.com/openshift/hypershift/cmd/nodepool/kubevirt" + "github.com/openshift/hypershift/cmd/nodepool/maas" "github.com/openshift/hypershift/cmd/nodepool/openstack" "github.com/openshift/hypershift/cmd/nodepool/powervs" @@ -17,6 +18,7 @@ import ( var _ core.PlatformOptions = &aws.AWSPlatformCreateOptions{} var _ core.PlatformOptions = &kubevirt.KubevirtPlatformCreateOptions{} var _ core.PlatformOptions = &agent.AgentPlatformCreateOptions{} +var _ core.PlatformOptions = &maas.MAASPlatformCreateOptions{} var _ core.PlatformOptions = &openstack.OpenStackPlatformCreateOptions{} func NewCreateCommand() *cobra.Command { @@ -53,6 +55,7 @@ func NewCreateCommand() *cobra.Command { cmd.AddCommand(azure.NewCreateCommand(opts)) cmd.AddCommand(powervs.NewCreateCommand(opts)) cmd.AddCommand(openstack.NewCreateCommand(opts)) + cmd.AddCommand(maas.NewCreateCommand(opts)) return cmd } diff --git a/cmd/nodepool/maas/create.go b/cmd/nodepool/maas/create.go new file mode 100644 index 00000000000..231ab09370a --- /dev/null +++ b/cmd/nodepool/maas/create.go @@ -0,0 +1,252 @@ +package maas + +import ( + "context" + "fmt" + "strconv" + "strings" + + hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1" + "github.com/openshift/hypershift/cmd/nodepool/core" + + crclient "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +func DefaultOptions() *RawMAASPlatformCreateOptions { + return &RawMAASPlatformCreateOptions{ + MAASPlatformOptions: &MAASPlatformOptions{ + LXD: &MAASLXDConfig{}, + StaticIP: &MAASStaticIPConfig{}, + }, + } +} + +type MAASPlatformOptions struct { + // Basic configuration + IdentityRef string + MachineType string + Zone string + ResourcePool string + Tags []string + + // Resource requirements + MinCPU int32 + MinMemory int32 + Image string + + // Advanced configuration (new fields) + MinDiskSize *int32 + LXD *MAASLXDConfig + StaticIP *MAASStaticIPConfig +} + +type MAASLXDConfig struct { + Enabled bool + StoragePool string + Network string +} + +type MAASStaticIPConfig struct { + IP string + CIDR string + Gateway string + Nameservers []string +} + +// completedCreateOptions is a private wrapper that enforces a call of Complete() before nodepool creation can be invoked. +type completedMAASPlatformCreateOptions struct { + *MAASPlatformOptions +} + +type MAASPlatformCreateOptions struct { + // Embed a private pointer that cannot be instantiated outside of this package. + *completedMAASPlatformCreateOptions +} + +type RawMAASPlatformCreateOptions struct { + *MAASPlatformOptions + // Raw string inputs that need parsing + TagsRaw string + NameserversRaw string + MinDiskSizeRaw string + LXDEnabledRaw string +} + +type validatedMAASPlatformCreateOptions struct { + *completedMAASPlatformCreateOptions +} + +type ValidatedMAASPlatformCreateOptions struct { + *validatedMAASPlatformCreateOptions +} + +func (o *ValidatedMAASPlatformCreateOptions) Complete() (*MAASPlatformCreateOptions, error) { + return &MAASPlatformCreateOptions{ + completedMAASPlatformCreateOptions: &completedMAASPlatformCreateOptions{ + MAASPlatformOptions: o.MAASPlatformOptions, + }, + }, nil +} + +func (o *RawMAASPlatformCreateOptions) Validate() (*ValidatedMAASPlatformCreateOptions, error) { + // Parse tags + if o.TagsRaw != "" { + o.Tags = strings.Split(o.TagsRaw, ",") + for i, tag := range o.Tags { + o.Tags[i] = strings.TrimSpace(tag) + } + } + + // Parse nameservers + if o.NameserversRaw != "" { + o.StaticIP.Nameservers = strings.Split(o.NameserversRaw, ",") + for i, ns := range o.StaticIP.Nameservers { + o.StaticIP.Nameservers[i] = strings.TrimSpace(ns) + } + } + + // Parse min disk size + if o.MinDiskSizeRaw != "" { + size, err := strconv.ParseInt(o.MinDiskSizeRaw, 10, 32) + if err != nil { + return nil, fmt.Errorf("invalid min-disk-size: %w", err) + } + diskSize := int32(size) + o.MinDiskSize = &diskSize + } + + // Parse LXD enabled + if o.LXDEnabledRaw != "" { + enabled, err := strconv.ParseBool(o.LXDEnabledRaw) + if err != nil { + return nil, fmt.Errorf("invalid lxd-enabled: %w", err) + } + o.LXD.Enabled = enabled + } + + // Validate required fields + if o.IdentityRef == "" { + return nil, fmt.Errorf("identity-ref is required") + } + + // Validate resource requirements + if o.MinCPU < 1 { + return nil, fmt.Errorf("min-cpu must be at least 1") + } + if o.MinMemory < 1024 { + return nil, fmt.Errorf("min-memory must be at least 1024 MB") + } + + return &ValidatedMAASPlatformCreateOptions{ + validatedMAASPlatformCreateOptions: &validatedMAASPlatformCreateOptions{ + completedMAASPlatformCreateOptions: &completedMAASPlatformCreateOptions{ + MAASPlatformOptions: o.MAASPlatformOptions, + }, + }, + }, nil +} + +func (o *MAASPlatformCreateOptions) UpdateNodePool(ctx context.Context, nodePool *hyperv1.NodePool, hcluster *hyperv1.HostedCluster, client crclient.Client) error { + // Set MAAS platform configuration + nodePool.Spec.Platform.MAAS = &hyperv1.MAASNodePoolPlatform{ + IdentityRef: hyperv1.MAASIdentityReference{ + Name: o.IdentityRef, + }, + MachineType: o.MachineType, + Zone: o.Zone, + ResourcePool: o.ResourcePool, + Tags: o.Tags, + MinCPU: &o.MinCPU, + MinMemory: &o.MinMemory, + Image: o.Image, + } + + // Set advanced configuration if provided + // TODO: These fields will be available once the API is updated + // if o.MinDiskSize != nil { + // nodePool.Spec.Platform.MAAS.MinDiskSize = o.MinDiskSize + // } + + // if o.LXD != nil && o.LXD.Enabled { + // nodePool.Spec.Platform.MAAS.LXD = &hyperv1.MAASLXDConfig{ + // Enabled: &o.LXD.Enabled, + // StoragePool: o.LXD.StoragePool, + // Network: o.LXD.Network, + // } + // } + + // if o.StaticIP != nil && o.StaticIP.IP != "" { + // nodePool.Spec.Platform.MAAS.StaticIP = &hyperv1.MAASStaticIPConfig{ + // IP: o.StaticIP.IP, + // CIDR: o.StaticIP.CIDR, + // Gateway: o.StaticIP.Gateway, + // Nameservers: o.StaticIP.Nameservers, + // } + // } + + return nil +} + +func (o *MAASPlatformCreateOptions) Type() hyperv1.PlatformType { + return hyperv1.MAASPlatform +} + +func NewCreateCommand(opts *core.CreateNodePoolOptions) *cobra.Command { + rawOpts := DefaultOptions() + cmd := &cobra.Command{ + Use: "maas", + Short: "Creates a MAAS NodePool", + SilenceUsage: true, + } + + cmd.RunE = func(cmd *cobra.Command, args []string) error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + validatedOpts, err := rawOpts.Validate() + if err != nil { + return err + } + + completedOpts, err := validatedOpts.Complete() + if err != nil { + return err + } + + return opts.CreateNodePool(ctx, completedOpts) + } + + flags := cmd.Flags() + rawOpts.AddFlags(flags) + + return cmd +} + +func (o *RawMAASPlatformCreateOptions) AddFlags(flags *pflag.FlagSet) { + // Basic configuration + flags.StringVar(&o.IdentityRef, "identity-ref", "", "Name of the MAAS credentials secret (required)") + flags.StringVar(&o.MachineType, "machine-type", "", "MAAS machine type/tag for node selection") + flags.StringVar(&o.Zone, "zone", "", "MAAS zone where nodes will be deployed") + flags.StringVar(&o.ResourcePool, "resource-pool", "", "MAAS resource pool for node allocation") + flags.StringVar(&o.TagsRaw, "tags", "", "Comma-separated list of MAAS tags for filtering") + + // Resource requirements + flags.Int32Var(&o.MinCPU, "min-cpu", 1, "Minimum CPU count required for nodes") + flags.Int32Var(&o.MinMemory, "min-memory", 1024, "Minimum memory in MB required for nodes") + flags.StringVar(&o.Image, "image", "", "MAAS image ID to use for nodes") + + // Advanced configuration + flags.StringVar(&o.MinDiskSizeRaw, "min-disk-size", "", "Minimum disk size in GB") + flags.StringVar(&o.LXDEnabledRaw, "lxd-enabled", "false", "Enable LXD VM creation") + flags.StringVar(&o.LXD.StoragePool, "lxd-storage-pool", "", "LXD storage pool for VMs") + flags.StringVar(&o.LXD.Network, "lxd-network", "", "LXD network for VMs") + flags.StringVar(&o.StaticIP.IP, "static-ip", "", "Static IP address for VMs") + flags.StringVar(&o.StaticIP.CIDR, "static-ip-cidr", "", "Network CIDR for static IP") + flags.StringVar(&o.StaticIP.Gateway, "static-ip-gateway", "", "Network gateway for static IP") + flags.StringVar(&o.NameserversRaw, "static-ip-nameservers", "", "Comma-separated list of DNS servers") + + // Note: Required flags are validated in the Validate() method +} diff --git a/control-plane-operator/controllers/hostedcontrolplane/configoperator/reconcile.go b/control-plane-operator/controllers/hostedcontrolplane/configoperator/reconcile.go index 8e92cf269e8..670fd617894 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/configoperator/reconcile.go +++ b/control-plane-operator/controllers/hostedcontrolplane/configoperator/reconcile.go @@ -235,6 +235,9 @@ func ReconcileRole(role *rbacv1.Role, ownerRef config.OwnerRef, platform hyperv1 }, }, }...) + case hyperv1.MAASPlatform: + // MAAS platform doesn't require additional RBAC rules + // It uses standard CAPI provider for infrastructure management } // TODO (jparrill): Add RBAC specific needs for Agent platform return nil diff --git a/control-plane-operator/controllers/hostedcontrolplane/cvo/reconcile.go b/control-plane-operator/controllers/hostedcontrolplane/cvo/reconcile.go index 5205b573d6a..b62c03469f2 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/cvo/reconcile.go +++ b/control-plane-operator/controllers/hostedcontrolplane/cvo/reconcile.go @@ -267,6 +267,21 @@ func ResourcesToRemove(platformType hyperv1.PlatformType) []client.Object { &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "cluster-node-tuning-operator", Namespace: "openshift-cluster-node-tuning-operator"}}, &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "cluster-image-registry-operator", Namespace: "openshift-image-registry"}}, } + case hyperv1.MAASPlatform: + // MAAS platform uses default resource removal (same as other platforms) + return []client.Object{ + &apiextensionsv1.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{Name: "machineconfigs.machineconfiguration.openshift.io"}}, + &apiextensionsv1.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{Name: "machineconfigpools.machineconfiguration.openshift.io"}}, + &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "network-operator", Namespace: "openshift-network-operator"}}, + &rbacv1.ClusterRoleBinding{ObjectMeta: metav1.ObjectMeta{Name: "default-account-cluster-network-operator"}}, + &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "cluster-node-tuning-operator", Namespace: "openshift-cluster-node-tuning-operator"}}, + &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "cluster-image-registry-operator", Namespace: "openshift-image-registry"}}, + &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "cluster-storage-operator", Namespace: "openshift-cluster-storage-operator"}}, + &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "csi-snapshot-controller-operator", Namespace: "openshift-cluster-storage-operator"}}, + &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "aws-ebs-csi-driver-operator", Namespace: "openshift-cluster-csi-drivers"}}, + &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "aws-ebs-csi-driver-controller", Namespace: "openshift-cluster-csi-drivers"}}, + &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "csi-snapshot-controller", Namespace: "openshift-cluster-storage-operator"}}, + } default: return []client.Object{ &apiextensionsv1.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{Name: "machineconfigs.machineconfiguration.openshift.io"}}, diff --git a/control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go b/control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go index a454e658243..19586177e43 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go +++ b/control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go @@ -1990,6 +1990,7 @@ func (r *HostedControlPlaneReconciler) reconcileInfrastructure(ctx context.Conte if hcp.Spec.Services == nil { return fmt.Errorf("service publishing strategy undefined") } + if err := r.reconcileAPIServerService(ctx, hcp, createOrUpdate); err != nil { return fmt.Errorf("failed to reconcile API server service: %w", err) } @@ -4524,6 +4525,16 @@ func (r *HostedControlPlaneReconciler) reconcileCoreIgnitionConfig(ctx context.C return fmt.Errorf("failed to reconcile image content source policy ignition config: %w", err) } + // Add MAAS-specific ignition configuration + if hcp.Spec.Platform.Type == hyperv1.MAASPlatform { + maasConfig := manifests.IgnitionMAASConfig(hcp.Namespace) + if _, err := createOrUpdate(ctx, r, maasConfig, func() error { + return ignition.ReconcileMAASIgnitionConfig(maasConfig, p.OwnerRef) + }); err != nil { + return fmt.Errorf("failed to reconcile MAAS ignition config: %w", err) + } + } + return nil } diff --git a/control-plane-operator/controllers/hostedcontrolplane/ignition/reconcile.go b/control-plane-operator/controllers/hostedcontrolplane/ignition/reconcile.go index 3827133959a..610a6fdfe25 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/ignition/reconcile.go +++ b/control-plane-operator/controllers/hostedcontrolplane/ignition/reconcile.go @@ -15,6 +15,7 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" jsonserializer "k8s.io/apimachinery/pkg/runtime/serializer/json" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" @@ -59,6 +60,20 @@ func ReconcileImageSourceMirrorsIgnitionConfigFromIDMS(cm *corev1.ConfigMap, own return reconcileImageContentTypeIgnitionConfigMap(cm, imageDigestMirrorSet, ownerRef) } +func ReconcileMAASIgnitionConfig(cm *corev1.ConfigMap, ownerRef config.OwnerRef) error { + machineConfig := manifests.MachineConfigMAAS() + SetMachineConfigLabels(machineConfig) + + // Generate MAAS-specific ignition content + serializedConfig, err := maasIgnitionConfig() + if err != nil { + return fmt.Errorf("failed to serialize MAAS ignition config: %w", err) + } + machineConfig.Spec.Config.Raw = serializedConfig + + return reconcileMachineConfigIgnitionConfigMap(cm, machineConfig, ownerRef) +} + func workerSSHConfig(sshKey string) ([]byte, error) { config := &igntypes.Config{} config.Ignition.Version = ignitionVersion @@ -77,6 +92,44 @@ func workerSSHConfig(sshKey string) ([]byte, error) { return serializeIgnitionConfig(config) } +func maasIgnitionConfig() ([]byte, error) { + config := &igntypes.Config{} + config.Ignition.Version = ignitionVersion + + // Add custom user configuration for MAAS + config.Passwd = igntypes.Passwd{ + Users: []igntypes.PasswdUser{ + { + Name: "spectro", + Groups: []igntypes.Group{"sudo"}, + Shell: ptr.To("/bin/bash"), + PasswordHash: ptr.To("$6$salt$JmHhpqORjPckABM.DZyXAntcxWnkBL/hC5B8xiwweGUYepl2N0AqVnkfJWMv9F0xFAIIz2siruaP7J2qnSyWH/"), + SSHAuthorizedKeys: []igntypes.SSHAuthorizedKey{ + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDCzobpcF70X7oMUzK6xT2JgO6O57vgiT/9FepL7003hDw1QIxycwU9gmKhxBhJ69140RVSZi6IMmYN26xCJNcB/1cGejBRog59u4YNCcCUTzPz+jU2ZtjPJtYSFRMlR7qMI7aGFdx1rOxo8HmL9bV4ks+zWhtPTSvsk9zzte+XD0xIecoK/aewXQ7pEyfxXu/YQzAg+uUqrlOq+X4SRXGaWA02dfwQjr0kDQHFMtDZjyMmyXuNvNgPKBar5RkXeomdSw4IPuXzaU84RJfxGzF3SOYIqPWNfZCIPWPWOl7zBHnXg1+JI1LQFTs4sqpamML6mv+lMkvhJfX8CXFkzOmAteTjRYIi6f59QcSgfUeahb8R64CsAdqYKirCbIz9pz+UGM4Hc1mndUM/vf+UejidSQ+npxVP1nolpR2jLmLzad/9yracHikHTTf3WdjHM1aW/RtbY2y/Km/9ObVRw8agKVsu45sdN0KFI981E4Bb/1lDvxzSI32FOhLUgOW//SMFa/JQj78JgXkCZXCuA9f5U+DLFo6s7FAjsiFXyX6LMs/xO1jw3CkmgxMNfU7rc4Vj63xBYYWJsTGQsCDintodsHFZn/IOefJCCOQ0OMJxRSZqLu/Fp5Sd6iO6YsK3VqCh1RRYza1I81G9yBfUhIru87UPHpub/XqFh3/hWf19Jw== spectro2024", + }, + }, + }, + } + + // Add MAAS-specific storage configuration + config.Storage = igntypes.Storage{ + Disks: []igntypes.Disk{ + { + Device: "/dev/sda", + Partitions: []igntypes.Partition{ + { + Number: 5, + ShouldExist: ptr.To(false), + WipePartitionEntry: ptr.To(true), + }, + }, + }, + }, + } + + return serializeIgnitionConfig(config) +} + func serializeIgnitionConfig(cfg *igntypes.Config) ([]byte, error) { jsonBytes, err := json.Marshal(cfg) if err != nil { diff --git a/control-plane-operator/controllers/hostedcontrolplane/kcm/params.go b/control-plane-operator/controllers/hostedcontrolplane/kcm/params.go index 98264681bbd..7637e294370 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/kcm/params.go +++ b/control-plane-operator/controllers/hostedcontrolplane/kcm/params.go @@ -60,6 +60,7 @@ func NewKubeControllerManagerParams(ctx context.Context, hcp *hyperv1.HostedCont if hcp.Spec.Platform.Type == hyperv1.AzurePlatform { params.CloudProvider = "external" } + // MAAS platform doesn't require external cloud provider configuration params.PlatformType = hcp.Spec.Platform.Type if hcp.Spec.Configuration != nil { diff --git a/control-plane-operator/controllers/hostedcontrolplane/manifests/ignition.go b/control-plane-operator/controllers/hostedcontrolplane/manifests/ignition.go index d716dfbecbc..57f3586541e 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/manifests/ignition.go +++ b/control-plane-operator/controllers/hostedcontrolplane/manifests/ignition.go @@ -49,3 +49,20 @@ func ImageContentPolicyIgnitionConfig(ns string) *corev1.ConfigMap { }, } } + +func MachineConfigMAAS() *mcfgv1.MachineConfig { + return &mcfgv1.MachineConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "99-maas-custom", + }, + } +} + +func IgnitionMAASConfig(ns string) *corev1.ConfigMap { + return &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ignition-config-maas", + Namespace: ns, + }, + } +} diff --git a/control-plane-operator/controllers/hostedcontrolplane/pki/kas.go b/control-plane-operator/controllers/hostedcontrolplane/pki/kas.go index 60cb0b23109..6a63d169cae 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/pki/kas.go +++ b/control-plane-operator/controllers/hostedcontrolplane/pki/kas.go @@ -132,6 +132,7 @@ func inClusterKASURL(platformType hyperv1.PlatformType) string { if platformType == hyperv1.IBMCloudPlatform { return fmt.Sprintf("https://%s:%d", manifests.KubeAPIServerServiceName, config.KASSVCIBMCloudPort) } + // MAAS platform uses the default port return fmt.Sprintf("https://%s:%d", manifests.KubeAPIServerServiceName, config.KASSVCPort) } diff --git a/control-plane-operator/controllers/hostedcontrolplane/v2/configoperator/role.go b/control-plane-operator/controllers/hostedcontrolplane/v2/configoperator/role.go index 94f42c9a203..077ea2107aa 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/v2/configoperator/role.go +++ b/control-plane-operator/controllers/hostedcontrolplane/v2/configoperator/role.go @@ -83,6 +83,9 @@ func adaptRole(cpContext component.WorkloadContext, role *rbacv1.Role) error { }, }, }...) + case hyperv1.MAASPlatform: + // MAAS platform doesn't require additional RBAC rules + // It uses standard CAPI provider for infrastructure management } // TODO (jparrill): Add RBAC specific needs for Agent platform return nil diff --git a/control-plane-operator/controllers/hostedcontrolplane/v2/cvo/deployment.go b/control-plane-operator/controllers/hostedcontrolplane/v2/cvo/deployment.go index 7b82ed5763c..2f7d83370d4 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/v2/cvo/deployment.go +++ b/control-plane-operator/controllers/hostedcontrolplane/v2/cvo/deployment.go @@ -259,6 +259,21 @@ func resourcesToRemove(platformType hyperv1.PlatformType) []client.Object { &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "cluster-node-tuning-operator", Namespace: "openshift-cluster-node-tuning-operator"}}, &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "cluster-image-registry-operator", Namespace: "openshift-image-registry"}}, } + case hyperv1.MAASPlatform: + // MAAS platform uses default resource removal (same as other platforms) + return []client.Object{ + &apiextensionsv1.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{Name: "machineconfigs.machineconfiguration.openshift.io"}}, + &apiextensionsv1.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{Name: "machineconfigpools.machineconfiguration.openshift.io"}}, + &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "network-operator", Namespace: "openshift-network-operator"}}, + &rbacv1.ClusterRoleBinding{ObjectMeta: metav1.ObjectMeta{Name: "default-account-cluster-network-operator"}}, + &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "cluster-node-tuning-operator", Namespace: "openshift-cluster-node-tuning-operator"}}, + &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "cluster-image-registry-operator", Namespace: "openshift-image-registry"}}, + &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "cluster-storage-operator", Namespace: "openshift-cluster-storage-operator"}}, + &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "csi-snapshot-controller-operator", Namespace: "openshift-cluster-storage-operator"}}, + &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "aws-ebs-csi-driver-operator", Namespace: "openshift-cluster-csi-drivers"}}, + &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "aws-ebs-csi-driver-controller", Namespace: "openshift-cluster-csi-drivers"}}, + &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "csi-snapshot-controller", Namespace: "openshift-cluster-storage-operator"}}, + } default: return []client.Object{ &apiextensionsv1.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{Name: "machineconfigs.machineconfiguration.openshift.io"}}, diff --git a/control-plane-operator/controllers/hostedcontrolplane/v2/dnsoperator/deployment.go b/control-plane-operator/controllers/hostedcontrolplane/v2/dnsoperator/deployment.go index d7f89f9736b..acb817d8197 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/v2/dnsoperator/deployment.go +++ b/control-plane-operator/controllers/hostedcontrolplane/v2/dnsoperator/deployment.go @@ -13,7 +13,7 @@ import ( func adaptDeployment(cpContext component.WorkloadContext, obj *appsv1.Deployment) error { util.UpdateContainer("dns-operator", obj.Spec.Template.Spec.Containers, func(c *corev1.Container) { // TODO (alberto): enforce ImagePullPolicy in component defaults. - c.ImagePullPolicy = corev1.PullIfNotPresent + c.ImagePullPolicy = corev1.PullAlways c.Command = []string{"dns-operator"} c.Env = []corev1.EnvVar{ { diff --git a/control-plane-operator/controllers/hostedcontrolplane/v2/etcd/statefulset.go b/control-plane-operator/controllers/hostedcontrolplane/v2/etcd/statefulset.go index c53eb7ac24e..320719b9d51 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/v2/etcd/statefulset.go +++ b/control-plane-operator/controllers/hostedcontrolplane/v2/etcd/statefulset.go @@ -114,7 +114,7 @@ func buildEtcdInitContainer(restoreUrl string) corev1.Container { }, } c.Image = "etcd" - c.ImagePullPolicy = corev1.PullIfNotPresent + c.ImagePullPolicy = corev1.PullAlways c.Command = []string{"/bin/sh", "-ce", etcdInitScript} c.VolumeMounts = []corev1.VolumeMount{ { @@ -130,7 +130,7 @@ func buildEtcdDefragControllerContainer(namespace string) corev1.Container { Name: "etcd-defrag", } c.Image = "controlplane-operator" - c.ImagePullPolicy = corev1.PullIfNotPresent + c.ImagePullPolicy = corev1.PullAlways c.Command = []string{"control-plane-operator"} c.Args = []string{ "etcd-defrag-controller", diff --git a/control-plane-operator/controllers/hostedcontrolplane/v2/kas/deployment.go b/control-plane-operator/controllers/hostedcontrolplane/v2/kas/deployment.go index c0fea73b712..a67c8e7d617 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/v2/kas/deployment.go +++ b/control-plane-operator/controllers/hostedcontrolplane/v2/kas/deployment.go @@ -71,6 +71,9 @@ func adaptDeployment(cpContext component.WorkloadContext, deployment *appsv1.Dep switch hcp.Spec.Platform.Type { case hyperv1.AWSPlatform: applyAWSPodIdentityWebhookContainer(&deployment.Spec.Template.Spec, hcp) + case hyperv1.MAASPlatform: + // MAAS platform doesn't require AWS pod identity webhook + // It uses standard CAPI provider for infrastructure management } if hcp.Spec.AuditWebhook != nil && len(hcp.Spec.AuditWebhook.Name) > 0 { @@ -228,7 +231,7 @@ func applyAWSPodIdentityWebhookContainer(podSpec *corev1.PodSpec, hcp *hyperv1.H podSpec.Containers = append(podSpec.Containers, corev1.Container{ Name: "aws-pod-identity-webhook", Image: "aws-pod-identity-webhook", - ImagePullPolicy: corev1.PullIfNotPresent, + ImagePullPolicy: corev1.PullAlways, Command: []string{ "/usr/bin/aws-pod-identity-webhook", "--annotation-prefix=eks.amazonaws.com", diff --git a/control-plane-operator/hostedclusterconfigoperator/controllers/machine/machine.go b/control-plane-operator/hostedclusterconfigoperator/controllers/machine/machine.go index ae955bf97f7..57acdf321e7 100644 --- a/control-plane-operator/hostedclusterconfigoperator/controllers/machine/machine.go +++ b/control-plane-operator/hostedclusterconfigoperator/controllers/machine/machine.go @@ -57,6 +57,9 @@ func (r *reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco return reconcile.Result{}, fmt.Errorf("failed removing orphan kubevirt passthrough endpoint slices: %w", err) } } + case hyperv1.MAASPlatform: + // MAAS platform doesn't require special machine handling + // It uses standard CAPI provider for infrastructure management } log.Info("Reconciled Machine") return reconcile.Result{}, nil diff --git a/control-plane-operator/hostedclusterconfigoperator/controllers/resources/ingress/reconcile.go b/control-plane-operator/hostedclusterconfigoperator/controllers/resources/ingress/reconcile.go index 81e4abaa4ee..31c3e15adbb 100644 --- a/control-plane-operator/hostedclusterconfigoperator/controllers/resources/ingress/reconcile.go +++ b/control-plane-operator/hostedclusterconfigoperator/controllers/resources/ingress/reconcile.go @@ -97,6 +97,14 @@ func ReconcileDefaultIngressController(ingressController *operatorv1.IngressCont ingressController.Spec.DefaultCertificate = &corev1.LocalObjectReference{ Name: manifests.IngressDefaultIngressControllerCert().Name, } + case hyperv1.MAASPlatform: + // MAAS platform uses default LoadBalancer strategy + ingressController.Spec.EndpointPublishingStrategy = &operatorv1.EndpointPublishingStrategy{ + Type: operatorv1.LoadBalancerServiceStrategyType, + } + ingressController.Spec.DefaultCertificate = &corev1.LocalObjectReference{ + Name: manifests.IngressDefaultIngressControllerCert().Name, + } default: ingressController.Spec.EndpointPublishingStrategy = &operatorv1.EndpointPublishingStrategy{ Type: operatorv1.LoadBalancerServiceStrategyType, diff --git a/control-plane-operator/hostedclusterconfigoperator/controllers/resources/konnectivity/reconcile.go b/control-plane-operator/hostedclusterconfigoperator/controllers/resources/konnectivity/reconcile.go index 3ff30df84b8..5a1d23d9bfe 100644 --- a/control-plane-operator/hostedclusterconfigoperator/controllers/resources/konnectivity/reconcile.go +++ b/control-plane-operator/hostedclusterconfigoperator/controllers/resources/konnectivity/reconcile.go @@ -83,6 +83,7 @@ func ReconcileAgentDaemonSet(daemonset *appsv1.DaemonSet, deploymentConfig confi daemonset.Spec.Template.Spec.DNSPolicy = corev1.DNSClusterFirst } } + // MAAS platform uses default settings (HostNetwork: true, DNSPolicy: DNSDefault) deploymentConfig.ApplyToDaemonSet(daemonset) } diff --git a/control-plane-operator/hostedclusterconfigoperator/controllers/resources/network/reconcile.go b/control-plane-operator/hostedclusterconfigoperator/controllers/resources/network/reconcile.go index 2425851f116..3d32c2ba64e 100644 --- a/control-plane-operator/hostedclusterconfigoperator/controllers/resources/network/reconcile.go +++ b/control-plane-operator/hostedclusterconfigoperator/controllers/resources/network/reconcile.go @@ -73,6 +73,9 @@ func ReconcileNetworkOperator(network *operatorv1.Network, networkType hyperv1.N } network.Spec.DefaultNetwork.OVNKubernetesConfig.GatewayConfig.RoutingViaHost = true } + case hyperv1.MAASPlatform: + // MAAS platform uses default network configuration + // No special network configuration needed for MAAS default: // do nothing } diff --git a/control-plane-operator/hostedclusterconfigoperator/controllers/resources/registry/registry.go b/control-plane-operator/hostedclusterconfigoperator/controllers/resources/registry/registry.go index b3f01cf07d0..4546a58d978 100644 --- a/control-plane-operator/hostedclusterconfigoperator/controllers/resources/registry/registry.go +++ b/control-plane-operator/hostedclusterconfigoperator/controllers/resources/registry/registry.go @@ -31,9 +31,9 @@ func ReconcileRegistryConfig(cfg *imageregistryv1.Config, platform hyperv1.Platf } } - // Initially assign storage as emptyDir for KubevirtPlatform and NonePlatform + // Initially assign storage as emptyDir for KubevirtPlatform, NonePlatform, and MAASPlatform // Allow user to change storage afterwards - if cfg.ResourceVersion == "" && (platform == hyperv1.KubevirtPlatform || platform == hyperv1.NonePlatform) { + if cfg.ResourceVersion == "" && (platform == hyperv1.KubevirtPlatform || platform == hyperv1.NonePlatform || platform == hyperv1.MAASPlatform) { cfg.Spec.Storage = imageregistryv1.ImageRegistryConfigStorage{EmptyDir: &imageregistryv1.ImageRegistryConfigStorageEmptyDir{}} } // IBM Cloud platform allows to initialize the registry config and then afterwards the client is in full control of the updates diff --git a/control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources.go b/control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources.go index ff016a4ea69..f3fd20bb13a 100644 --- a/control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources.go +++ b/control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources.go @@ -721,6 +721,10 @@ func (r *reconciler) Reconcile(ctx context.Context, _ ctrl.Request) (ctrl.Result case hyperv1.AzurePlatform: log.Info("reconciling Azure specific resources") errs = append(errs, r.reconcileAzureCloudNodeManager(ctx, releaseImage.ComponentImages()["azure-cloud-node-manager"])...) + case hyperv1.MAASPlatform: + log.Info("reconciling MAAS specific resources") + // MAAS platform doesn't require additional resource reconciliation + // It uses standard CAPI provider for infrastructure management } return ctrl.Result{}, errors.NewAggregate(errs) } @@ -1670,6 +1674,9 @@ func (r *reconciler) reconcileCloudCredentialSecrets(ctx context.Context, hcp *h errs = append(errs, fmt.Errorf("failed to reconcile powervs image registry cloud credentials secret %w", err)) } } + case hyperv1.MAASPlatform: + // MAAS platform doesn't require cloud credential secrets + // It uses standard CAPI provider for infrastructure management } return errs } @@ -1930,6 +1937,9 @@ func (r *reconciler) reconcileCloudConfig(ctx context.Context, hcp *hyperv1.Host }); err != nil { return fmt.Errorf("failed to reconcile the %s/%s configmap: %w", cmKCC.Namespace, cmKCC.Name, err) } + case hyperv1.MAASPlatform: + // MAAS platform doesn't require cloud provider configuration + // It uses standard CAPI provider for infrastructure management default: return nil } @@ -2634,6 +2644,9 @@ func (r *reconciler) reconcileStorage(ctx context.Context, hcp *hyperv1.HostedCo operatorv1.CinderCSIDriver, operatorv1.ManilaCSIDriver, } + case hyperv1.MAASPlatform: + // MAAS platform doesn't require specific CSI drivers + // It uses standard CAPI provider for infrastructure management } for _, driverName := range driverNames { driver := manifests.ClusterCSIDriver(driverName) @@ -2833,6 +2846,9 @@ func imageRegistryPlatformWithPVC(platform hyperv1.PlatformType) bool { switch platform { case hyperv1.OpenStackPlatform: return true + case hyperv1.MAASPlatform: + // MAAS platform doesn't require PVC for image registry + return false default: return false } diff --git a/docs/content/images/controller-reconcilation-loops.png b/docs/content/images/controller-reconcilation-loops.png new file mode 100644 index 00000000000..dfb66e973b0 Binary files /dev/null and b/docs/content/images/controller-reconcilation-loops.png differ diff --git a/docs/content/images/hypershift-maas-architecture.png b/docs/content/images/hypershift-maas-architecture.png new file mode 100644 index 00000000000..41bce205cc2 Binary files /dev/null and b/docs/content/images/hypershift-maas-architecture.png differ diff --git a/docs/content/images/resource-creation-flow.png b/docs/content/images/resource-creation-flow.png new file mode 100644 index 00000000000..b239aef6cb4 Binary files /dev/null and b/docs/content/images/resource-creation-flow.png differ diff --git a/docs/content/reference/api.md b/docs/content/reference/api.md index 6ee28f7fbb7..50f11d07e11 100644 --- a/docs/content/reference/api.md +++ b/docs/content/reference/api.md @@ -8401,6 +8401,393 @@ If omitted, the value will be inferred from the corev1.Service Load balancer typ

+###MAASIdentityReference { #hypershift.openshift.io/v1beta1.MAASIdentityReference } +

+(Appears on: +MAASNodePoolPlatform, +MAASPlatformSpec) +

+

+

MAASIdentityReference is a reference to an infrastructure +provider identity to be used to provision cluster resources.

+

+ + + + + + + + + + + + + +
FieldDescription
+name
+ +string + +
+

Name is the name of a secret in the same namespace as the resource being provisioned. +The secret must contain the following keys: +- MAAS_ENDPOINT: MAAS API endpoint URL +- MAAS_API_KEY: MAAS API key for authentication

+
+###MAASLXDConfig { #hypershift.openshift.io/v1beta1.MAASLXDConfig } +

+(Appears on: +MAASNodePoolPlatform) +

+

+

MAASLXDConfig defines LXD VM creation options for a machine

+

+ + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+enabled
+ +bool + +
+(Optional) +

enabled specifies whether this machine should be created as an LXD VM

+
+storagePool
+ +string + +
+(Optional) +

storagePool is the storage pool to use for the VM

+
+network
+ +string + +
+(Optional) +

network is the network to connect the VM to

+
+###MAASNodePoolPlatform { #hypershift.openshift.io/v1beta1.MAASNodePoolPlatform } +

+(Appears on: +NodePoolPlatform) +

+

+

MAASNodePoolPlatform specifies the configuration for MaaS platform.

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+identityRef
+ + +MAASIdentityReference + + +
+

identityRef is a reference to a secret holding MAAS credentials +to be used when reconciling the node pool. +The secret must contain the following keys: +- MAAS_ENDPOINT: MAAS API endpoint URL +- MAAS_API_KEY: MAAS API key for authentication

+
+machineType
+ +string + +
+(Optional) +

machineType specifies the type of MAAS machine to use for the nodes. +This corresponds to the MAAS machine type/tag that will be used for node selection.

+
+zone
+ +string + +
+(Optional) +

zone specifies the MAAS zone where the nodes will be deployed. +If not specified, nodes will be deployed in any available zone.

+
+tags
+ +[]string + +
+(Optional) +

tags specifies additional MAAS tags to apply to the nodes for filtering and organization.

+
+resourcePool
+ +string + +
+(Optional) +

resourcePool specifies the MAAS resource pool to use for node allocation.

+
+minCpu
+ +int32 + +
+(Optional) +

minCpu specifies the minimum CPU count required for the nodes.

+
+minMemory
+ +int32 + +
+(Optional) +

minMemory specifies the minimum memory in MB required for the nodes.

+
+image
+ +string + +
+(Optional) +

image specifies the MAAS image ID to use for the nodes. +If not specified, a default image will be used based on the release.

+
+failureDomain
+ +string + +
+(Optional) +

failureDomain specifies the failure domain the machine will be created in. +Must match a key in the FailureDomains map stored on the cluster object.

+
+minDiskSize
+ +int32 + +
+(Optional) +

minDiskSize specifies the minimum disk size in GB required for the nodes.

+
+lxd
+ + +MAASLXDConfig + + +
+(Optional) +

lxd contains configuration for creating this machine as an LXD VM on a host +when enabled. When nil or disabled, this machine is created on bare metal.

+
+staticIP
+ + +MAASStaticIPConfig + + +
+(Optional) +

staticIP configuration for VMs

+
+###MAASPlatformSpec { #hypershift.openshift.io/v1beta1.MAASPlatformSpec } +

+(Appears on: +PlatformSpec) +

+

+

MAASPlatformSpec specifies configuration for clusters running on MaaS (Metal as a Service).

+

+ + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+identityRef
+ + +MAASIdentityReference + + +
+

identityRef is a reference to a secret holding MAAS credentials +to be used when reconciling the hosted cluster.

+
+dnsDomain
+ +string + +
+(Optional) +

dnsDomain is the DNS domain for the MAAS cluster.

+
+zone
+ +string + +
+(Optional) +

zone specifies the MAAS zone where the cluster will be deployed. +If not specified, the cluster will be deployed in any available zone.

+
+###MAASStaticIPConfig { #hypershift.openshift.io/v1beta1.MAASStaticIPConfig } +

+(Appears on: +MAASNodePoolPlatform) +

+

+

MAASStaticIPConfig defines the static IP configuration for a VM

+

+ + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+ip
+ +string + +
+(Optional) +

ip is the static IP address to assign

+
+cidr
+ +string + +
+(Optional) +

cidr is the network CIDR

+
+gateway
+ +string + +
+(Optional) +

gateway is the network gateway

+
+nameservers
+ +[]string + +
+(Optional) +

nameservers is a list of DNS servers

+
###MachineNetworkEntry { #hypershift.openshift.io/v1beta1.MachineNetworkEntry }

(Appears on: @@ -9213,6 +9600,20 @@ OpenStackNodePoolPlatform

OpenStack specifies the configuration used when using OpenStack platform.

+ + +maas
+ + +MAASNodePoolPlatform + + + + +(Optional) +

maas specifies the configuration used when using MaaS platform.

+ + ###NodePoolPlatformStatus { #hypershift.openshift.io/v1beta1.NodePoolPlatformStatus } @@ -10241,6 +10642,20 @@ OpenStackPlatformSpec

OpenStack specifies configuration for clusters running on OpenStack.

+ + +maas
+ + +MAASPlatformSpec + + + + +(Optional) +

maas specifies configuration for clusters running on MaaS (Metal as a Service).

+ + ###PlatformStatus { #hypershift.openshift.io/v1beta1.PlatformStatus } @@ -10307,6 +10722,9 @@ AWSPlatformStatus

"KubeVirt"

KubevirtPlatform represents Kubevirt infrastructure.

+

"MAAS"

+

MAASPlatform represents MaaS (Metal as a Service) infrastructure.

+

"None"

NonePlatform represents user supplied (e.g. bare metal) infrastructure.

diff --git a/go.mod b/go.mod index 56104fe41c2..580858b0d79 100644 --- a/go.mod +++ b/go.mod @@ -60,6 +60,7 @@ require ( github.com/prometheus/client_golang v1.20.5 github.com/prometheus/client_model v0.6.1 github.com/prometheus/common v0.62.0 + github.com/spectrocloud/cluster-api-provider-maas v0.5.1-0.20250512112717-769064ca22e9 github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.6 github.com/stretchr/testify v1.10.0 @@ -103,7 +104,7 @@ require ( sigs.k8s.io/controller-runtime v0.20.1 sigs.k8s.io/karpenter v1.2.1-0.20250212185021-45f73ec7a790 sigs.k8s.io/secrets-store-csi-driver v1.4.8 - sigs.k8s.io/structured-merge-diff/v4 v4.4.2 + sigs.k8s.io/structured-merge-diff/v4 v4.4.3 sigs.k8s.io/yaml v1.4.0 ) diff --git a/go.sum b/go.sum index 07e20cc426c..488497d4a10 100644 --- a/go.sum +++ b/go.sum @@ -601,6 +601,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +github.com/spectrocloud/cluster-api-provider-maas v0.5.1-0.20250512112717-769064ca22e9 h1:jI2lSDCBbPmZ1Fklv4wEULUvHD4vhey+3AHqtpUw9x4= +github.com/spectrocloud/cluster-api-provider-maas v0.5.1-0.20250512112717-769064ca22e9/go.mod h1:J50WRadGERZ8sGhytqfNZgwZvOVfBKEBvflHZK+zeR8= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= @@ -1012,8 +1014,8 @@ sigs.k8s.io/secrets-store-csi-driver v1.4.8 h1:YmL0lx9HMYqeZCnLyOZRMuGAZXmP/e42U sigs.k8s.io/secrets-store-csi-driver v1.4.8/go.mod h1:IawZyjzh3xGt6hHdckJUf3ls04O0zG5H550PEZz/beo= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= -sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.3 h1:sCP7Vv3xx/CWIuTPVN38lUPx0uw0lcLfzaiDa8Ja01A= +sigs.k8s.io/structured-merge-diff/v4 v4.4.3/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= diff --git a/hypershift-operator/controllers/hostedcluster/internal/platform/maas/maas.go b/hypershift-operator/controllers/hostedcluster/internal/platform/maas/maas.go new file mode 100644 index 00000000000..74ea5b63ef9 --- /dev/null +++ b/hypershift-operator/controllers/hostedcluster/internal/platform/maas/maas.go @@ -0,0 +1,264 @@ +package maas + +import ( + "context" + "fmt" + "os" + + hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1" + "github.com/openshift/hypershift/support/images" + "github.com/openshift/hypershift/support/upsert" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "sigs.k8s.io/controller-runtime/pkg/client" + + capimaas "github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1" +) + +const ( + MAASCAPIProvider = "maas-cluster-api-controllers" +) + +type MaaS struct { + capiProviderImage string +} + +func New(capiProviderImage string) *MaaS { + return &MaaS{ + capiProviderImage: capiProviderImage, + } +} + +func (p *MaaS) ReconcileCAPIInfraCR(ctx context.Context, c client.Client, createOrUpdate upsert.CreateOrUpdateFN, + hcluster *hyperv1.HostedCluster, + controlPlaneNamespace string, + apiEndpoint hyperv1.APIEndpoint, +) (client.Object, error) { + if hcluster.Spec.Platform.MAAS == nil { + return nil, fmt.Errorf("failed to reconcile MAAS CAPI cluster, empty MAAS platform spec") + } + + // Create a MAAS cluster using the actual CAPI provider types + maasCluster := &capimaas.MaasCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: hcluster.Name, + Namespace: controlPlaneNamespace, + Annotations: map[string]string{ + "spectrocloud.com/custom-dns-provided": "", + }, + Labels: map[string]string{ + "hypershift.openshift.io/cluster": hcluster.Name, + "platform": "maas", + }, + }, + } + + // Use the createOrUpdate function to ensure the object is properly managed + if _, err := createOrUpdate(ctx, c, maasCluster, func() error { + // Get DNS domain from MAAS platform spec or use a default + dnsDomain := "maas.local" + if hcluster.Spec.Platform.MAAS.DNSDomain != "" { + dnsDomain = hcluster.Spec.Platform.MAAS.DNSDomain + } + + maasCluster.Spec = capimaas.MaasClusterSpec{ + DNSDomain: dnsDomain, + ControlPlaneEndpoint: capimaas.APIEndpoint{ + Host: apiEndpoint.Host, + Port: int(apiEndpoint.Port), + }, + } + + // Ensure the annotation is always present + if maasCluster.Annotations == nil { + maasCluster.Annotations = make(map[string]string) + } + maasCluster.Annotations["spectrocloud.com/custom-dns-provided"] = "" + + return nil + }); err != nil { + return nil, fmt.Errorf("failed to create or update MAAS cluster: %w", err) + } + + return maasCluster, nil +} + +func (p *MaaS) CAPIProviderDeploymentSpec(hcluster *hyperv1.HostedCluster, _ *hyperv1.HostedControlPlane) (*appsv1.DeploymentSpec, error) { + // Check for environment variable override + image := p.capiProviderImage + if envImage := os.Getenv(images.MAASCAPIProviderEnvVar); len(envImage) > 0 { + image = envImage + } + + // Return a deployment spec for the MAAS CAPI provider with proper credential mounting + return &appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "maas-capi-controller", + Image: image, + Args: []string{ + "--v=2", + "--leader-elect=true", + "--sync-period=15m", + "--namespace=$(NAMESPACE)", + }, + Env: []corev1.EnvVar{ + { + Name: "NAMESPACE", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.namespace", + }, + }, + }, + { + Name: "MAAS_ENDPOINT", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: hcluster.Spec.Platform.MAAS.IdentityRef.Name, + }, + Key: "MAAS_ENDPOINT", + }, + }, + }, + { + Name: "MAAS_API_KEY", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: hcluster.Spec.Platform.MAAS.IdentityRef.Name, + }, + Key: "MAAS_API_KEY", + }, + }, + }, + { + Name: "MAAS_ZONE", + Value: hcluster.Spec.Platform.MAAS.Zone, + }, + }, + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("512Mi"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("200m"), + corev1.ResourceMemory: resource.MustParse("128Mi"), + }, + }, + }, + }, + }, + }, + }, nil +} + +func (p *MaaS) ReconcileCredentials(ctx context.Context, c client.Client, createOrUpdate upsert.CreateOrUpdateFN, hcluster *hyperv1.HostedCluster, controlPlaneNamespace string) error { + if hcluster.Spec.Platform.MAAS == nil { + return fmt.Errorf("failed to reconcile MAAS credentials, empty MAAS platform spec") + } + + // Get the referenced credentials secret + credentialsSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: hcluster.Spec.Platform.MAAS.IdentityRef.Name, + Namespace: hcluster.Namespace, + }, + } + + if err := c.Get(ctx, client.ObjectKeyFromObject(credentialsSecret), credentialsSecret); err != nil { + return fmt.Errorf("failed to get MAAS credentials secret: %w", err) + } + + // Validate that the secret contains the required keys + requiredKeys := []string{"MAAS_ENDPOINT", "MAAS_API_KEY"} + for _, key := range requiredKeys { + if _, exists := credentialsSecret.Data[key]; !exists { + return fmt.Errorf("MAAS credentials secret is missing required key: %s", key) + } + } + + // Create a copy of the secret in the control plane namespace + // Use the same name as referenced in IdentityRef so capi-provider can find it + controlPlaneSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: hcluster.Spec.Platform.MAAS.IdentityRef.Name, + Namespace: controlPlaneNamespace, + Labels: map[string]string{ + "hypershift.openshift.io/cluster": hcluster.Name, + "platform": "maas", + }, + }, + Type: corev1.SecretTypeOpaque, + Data: credentialsSecret.Data, + } + + // Check if the secret already exists and has the same data + existingSecret := &corev1.Secret{} + err := c.Get(ctx, client.ObjectKeyFromObject(controlPlaneSecret), existingSecret) + if err != nil { + if !apierrors.IsNotFound(err) { + return fmt.Errorf("failed to get existing MAAS credentials secret: %w", err) + } + // Secret doesn't exist, create it + controlPlaneSecret.Data = credentialsSecret.Data + _, err = createOrUpdate(ctx, c, controlPlaneSecret, func() error { + return nil + }) + } else { + // Secret exists, check if data has changed + dataChanged := existingSecret.Data == nil || + string(existingSecret.Data["MAAS_ENDPOINT"]) != string(credentialsSecret.Data["MAAS_ENDPOINT"]) || + string(existingSecret.Data["MAAS_API_KEY"]) != string(credentialsSecret.Data["MAAS_API_KEY"]) + + if dataChanged { + // Data has changed, update the secret + controlPlaneSecret.Data = credentialsSecret.Data + _, err = createOrUpdate(ctx, c, controlPlaneSecret, func() error { + return nil + }) + } + // If data hasn't changed, do nothing + } + + return err +} + +func (p *MaaS) ReconcileSecretEncryption(ctx context.Context, c client.Client, createOrUpdate upsert.CreateOrUpdateFN, hcluster *hyperv1.HostedCluster, controlPlaneNamespace string) error { + // MAAS doesn't support secret encryption, so this is a no-op + return nil +} + +func (p *MaaS) CAPIProviderPolicyRules() []rbacv1.PolicyRule { + // MAAS doesn't require additional policy rules beyond the default CAPI rules + return nil +} + +func (p *MaaS) DeleteCredentials(ctx context.Context, c client.Client, hcluster *hyperv1.HostedCluster, controlPlaneNamespace string) error { + // Delete the MAAS credentials secret + credentialsSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-maas-credentials", hcluster.Name), + Namespace: controlPlaneNamespace, + }, + } + + if err := c.Delete(ctx, credentialsSecret); err != nil { + // If the secret doesn't exist, that's fine + if client.IgnoreNotFound(err) != nil { + return fmt.Errorf("failed to delete MAAS credentials secret: %w", err) + } + } + + return nil +} diff --git a/hypershift-operator/controllers/hostedcluster/internal/platform/platform.go b/hypershift-operator/controllers/hostedcluster/internal/platform/platform.go index 7a81d1df543..556d3d10996 100644 --- a/hypershift-operator/controllers/hostedcluster/internal/platform/platform.go +++ b/hypershift-operator/controllers/hostedcluster/internal/platform/platform.go @@ -10,6 +10,7 @@ import ( "github.com/openshift/hypershift/hypershift-operator/controllers/hostedcluster/internal/platform/azure" "github.com/openshift/hypershift/hypershift-operator/controllers/hostedcluster/internal/platform/ibmcloud" "github.com/openshift/hypershift/hypershift-operator/controllers/hostedcluster/internal/platform/kubevirt" + "github.com/openshift/hypershift/hypershift-operator/controllers/hostedcluster/internal/platform/maas" "github.com/openshift/hypershift/hypershift-operator/controllers/hostedcluster/internal/platform/none" "github.com/openshift/hypershift/hypershift-operator/controllers/hostedcluster/internal/platform/openstack" "github.com/openshift/hypershift/hypershift-operator/controllers/hostedcluster/internal/platform/powervs" @@ -39,6 +40,7 @@ var _ Platform = ibmcloud.IBMCloud{} var _ Platform = none.None{} var _ Platform = agent.Agent{} var _ Platform = kubevirt.Kubevirt{} +var _ Platform = &maas.MaaS{} type Platform interface { // ReconcileCAPIInfraCR is called during HostedCluster reconciliation prior to reconciling the CAPI Cluster CR. @@ -152,6 +154,15 @@ func GetPlatform(ctx context.Context, hcluster *hyperv1.HostedCluster, releasePr } } platform = openstack.New(capiImageProvider, orcImage, payloadVersion) + case hyperv1.MAASPlatform: + // Check for annotation override first + if capiImage, exists := hcluster.Annotations[hyperv1.ClusterAPIProviderMAASImage]; exists { + capiImageProvider = capiImage + } else { + // Since MaaS image is not in OpenShift payload, use default + capiImageProvider = "us-docker.pkg.dev/palette-images/palette/cluster-api-maas/cluster-api-provider-maas-controller:v0.6.1-spectro-4.7.0" + } + platform = maas.New(capiImageProvider) default: return nil, fmt.Errorf("unsupported platform: %s", hcluster.Spec.Platform.Type) } diff --git a/hypershift-operator/controllers/nodepool/capi.go b/hypershift-operator/controllers/nodepool/capi.go index a0946862866..3b1f74a5eca 100644 --- a/hypershift-operator/controllers/nodepool/capi.go +++ b/hypershift-operator/controllers/nodepool/capi.go @@ -11,6 +11,7 @@ import ( hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1" "github.com/openshift/hypershift/hypershift-operator/controllers/manifests" "github.com/openshift/hypershift/hypershift-operator/controllers/nodepool/kubevirt" + "github.com/openshift/hypershift/hypershift-operator/controllers/nodepool/maas" "github.com/openshift/hypershift/hypershift-operator/controllers/nodepool/openstack" "github.com/openshift/hypershift/support/api" supportutil "github.com/openshift/hypershift/support/util" @@ -26,6 +27,7 @@ import ( "k8s.io/apimachinery/pkg/util/validation" "k8s.io/utils/ptr" + capimaas "github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1" capiaws "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2" capiazure "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" capipowervs "sigs.k8s.io/cluster-api-provider-ibmcloud/api/v1beta2" @@ -386,7 +388,15 @@ func (c *CAPI) reconcileMachineDeployment(ctx context.Context, log logr.Logger, // after it has been created with defaults. machineDeployment.Spec.MinReadySeconds = ptr.To[int32](0) machineDeployment.Spec.RevisionHistoryLimit = ptr.To[int32](1) - machineDeployment.Spec.ProgressDeadlineSeconds = ptr.To[int32](600) + + // Set ProgressDeadlineSeconds with configurable timeout via annotation + progressDeadlineSeconds := int32(3600) // default 1 hour + if nodePool.Annotations[hyperv1.MachineDeploymentProgressDeadlineSecondsAnnotation] != "" { + if val, err := strconv.Atoi(nodePool.Annotations[hyperv1.MachineDeploymentProgressDeadlineSecondsAnnotation]); err == nil && val > 0 { + progressDeadlineSeconds = int32(val) + } + } + machineDeployment.Spec.ProgressDeadlineSeconds = ptr.To[int32](progressDeadlineSeconds) machineDeployment.Spec.ClusterName = capiClusterName if machineDeployment.Spec.Selector.MatchLabels == nil { @@ -870,6 +880,33 @@ func (c *CAPI) machineTemplateBuilders() (client.Object, func(object client.Obje o.Annotations[nodePoolAnnotation] = client.ObjectKeyFromObject(nodePool).String() return nil } + case hyperv1.MAASPlatform: + template = &capimaas.MaasMachineTemplate{} + var err error + machineTemplateSpec, err = maas.MachineTemplateSpec(nodePool) + if err != nil { + SetStatusCondition(&nodePool.Status.Conditions, hyperv1.NodePoolCondition{ + Type: hyperv1.NodePoolValidMachineTemplateConditionType, + Status: corev1.ConditionFalse, + Reason: hyperv1.InvalidMAASMachineTemplate, + Message: err.Error(), + ObservedGeneration: nodePool.Generation, + }) + + return nil, nil, "", err + } else { + removeStatusCondition(&nodePool.Status.Conditions, hyperv1.NodePoolValidMachineTemplateConditionType) + } + + mutateTemplate = func(object client.Object) error { + o, _ := object.(*capimaas.MaasMachineTemplate) + o.Spec = *machineTemplateSpec.(*capimaas.MaasMachineTemplateSpec) + if o.Annotations == nil { + o.Annotations = make(map[string]string) + } + o.Annotations[nodePoolAnnotation] = client.ObjectKeyFromObject(nodePool).String() + return nil + } default: // TODO(alberto): Consider signal in a condition. return nil, nil, "", fmt.Errorf("unsupported platform type: %s", nodePool.Spec.Platform.Type) diff --git a/hypershift-operator/controllers/nodepool/maas.go b/hypershift-operator/controllers/nodepool/maas.go new file mode 100644 index 00000000000..7b81a338cbc --- /dev/null +++ b/hypershift-operator/controllers/nodepool/maas.go @@ -0,0 +1,62 @@ +package nodepool + +import ( + "fmt" + + capimaas "github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" +) + +func (c *CAPI) maasMachineTemplate(templateNameGenerator func(spec any) (string, error)) (*capimaas.MaasMachineTemplate, error) { + nodePool := c.nodePool + + // Create MAAS machine spec with required fields + maasMachineSpec := capimaas.MaasMachineSpec{ + // Required fields + Image: "ubuntu/focal", // Default image - should be configurable + MinCPU: ptr.To(1), // Default minimum CPU + MinMemoryInMB: ptr.To(1024), // Default minimum memory in MB + } + + // Override with NodePool platform configuration if specified + if nodePool.Spec.Platform.MAAS != nil { + if nodePool.Spec.Platform.MAAS.Image != "" { + maasMachineSpec.Image = nodePool.Spec.Platform.MAAS.Image + } + if nodePool.Spec.Platform.MAAS.MinCPU != nil { + maasMachineSpec.MinCPU = ptr.To(int(*nodePool.Spec.Platform.MAAS.MinCPU)) + } + if nodePool.Spec.Platform.MAAS.MinMemory != nil { + maasMachineSpec.MinMemoryInMB = ptr.To(int(*nodePool.Spec.Platform.MAAS.MinMemory)) + } + if nodePool.Spec.Platform.MAAS.Tags != nil { + maasMachineSpec.Tags = nodePool.Spec.Platform.MAAS.Tags + } + if nodePool.Spec.Platform.MAAS.ResourcePool != "" { + maasMachineSpec.ResourcePool = ptr.To(nodePool.Spec.Platform.MAAS.ResourcePool) + } + } + + // Create the template spec + spec := capimaas.MaasMachineTemplateSpec{ + Template: capimaas.MaasMachineTemplateResource{ + Spec: maasMachineSpec, + }, + } + + templateName, err := templateNameGenerator(spec) + if err != nil { + return nil, fmt.Errorf("failed to generate template name: %w", err) + } + + template := &capimaas.MaasMachineTemplate{ + ObjectMeta: metav1.ObjectMeta{ + Name: templateName, + }, + Spec: spec, + } + + return template, nil +} + diff --git a/hypershift-operator/controllers/nodepool/maas/maas.go b/hypershift-operator/controllers/nodepool/maas/maas.go new file mode 100644 index 00000000000..de6dc2258e3 --- /dev/null +++ b/hypershift-operator/controllers/nodepool/maas/maas.go @@ -0,0 +1,64 @@ +package maas + +import ( + hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1" + capimaas "github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1" + "k8s.io/utils/ptr" +) + +// MachineTemplateSpec creates a MAAS machine template specification for the given NodePool +func MachineTemplateSpec(nodePool *hyperv1.NodePool) (*capimaas.MaasMachineTemplateSpec, error) { + // Create MAAS machine spec with required fields + maasMachineSpec := capimaas.MaasMachineSpec{ + // Required fields + Image: "ubuntu/focal", // Default image - should be configurable + MinCPU: ptr.To(1), // Default minimum CPU + MinMemoryInMB: ptr.To(1024), // Default minimum memory in MB + } + + // Override with NodePool platform configuration if specified + if nodePool.Spec.Platform.MAAS != nil { + // Map image + if nodePool.Spec.Platform.MAAS.Image != "" { + maasMachineSpec.Image = nodePool.Spec.Platform.MAAS.Image + } + + // Map CPU/Memory requirements + if nodePool.Spec.Platform.MAAS.MinCPU != nil { + maasMachineSpec.MinCPU = ptr.To(int(*nodePool.Spec.Platform.MAAS.MinCPU)) + } + if nodePool.Spec.Platform.MAAS.MinMemory != nil { + maasMachineSpec.MinMemoryInMB = ptr.To(int(*nodePool.Spec.Platform.MAAS.MinMemory)) + } + + // Map failure domain (prefer FailureDomain over Zone) + if nodePool.Spec.Platform.MAAS.FailureDomain != "" { + maasMachineSpec.FailureDomain = ptr.To(nodePool.Spec.Platform.MAAS.FailureDomain) + } else if nodePool.Spec.Platform.MAAS.Zone != "" { + // Map Zone to FailureDomain if FailureDomain not specified + maasMachineSpec.FailureDomain = ptr.To(nodePool.Spec.Platform.MAAS.Zone) + } + + // Map resource pool + if nodePool.Spec.Platform.MAAS.ResourcePool != "" { + maasMachineSpec.ResourcePool = ptr.To(nodePool.Spec.Platform.MAAS.ResourcePool) + } + + // Map tags + if len(nodePool.Spec.Platform.MAAS.Tags) > 0 { + maasMachineSpec.Tags = nodePool.Spec.Platform.MAAS.Tags + } + + // TODO: Add support for MinDiskSize, LXD, and StaticIP when available in HyperShift API + // These fields are defined in the API but not yet available in the controller + } + + // Create the template spec + spec := capimaas.MaasMachineTemplateSpec{ + Template: capimaas.MaasMachineTemplateResource{ + Spec: maasMachineSpec, + }, + } + + return &spec, nil +} diff --git a/hypershift-operator/controllers/nodepool/nodepool_controller.go b/hypershift-operator/controllers/nodepool/nodepool_controller.go index 068f7e54337..e80d55bea13 100644 --- a/hypershift-operator/controllers/nodepool/nodepool_controller.go +++ b/hypershift-operator/controllers/nodepool/nodepool_controller.go @@ -439,6 +439,10 @@ func isArchAndPlatformSupported(nodePool *hyperv1.NodePool) bool { if nodePool.Spec.Arch == hyperv1.ArchitectureAMD64 || nodePool.Spec.Arch == hyperv1.ArchitectureARM64 { supported = true } + case hyperv1.MAASPlatform: + if nodePool.Spec.Arch == hyperv1.ArchitectureAMD64 || nodePool.Spec.Arch == hyperv1.ArchitectureARM64 { + supported = true + } } return supported diff --git a/support/api/scheme.go b/support/api/scheme.go index e0a3f32a770..0f622729c6e 100644 --- a/support/api/scheme.go +++ b/support/api/scheme.go @@ -34,6 +34,7 @@ import ( clientgoscheme "k8s.io/client-go/kubernetes/scheme" apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" + capimaas "github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1" capiaws "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2" capiazure "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" capiibm "sigs.k8s.io/cluster-api-provider-ibmcloud/api/v1beta2" @@ -116,6 +117,7 @@ func init() { _ = machinev1beta1.AddToScheme(Scheme) _ = capiopenstackv1alpha1.AddToScheme(Scheme) _ = capiopenstackv1beta1.AddToScheme(Scheme) + _ = capimaas.AddToScheme(Scheme) _ = secretsstorev1.AddToScheme(Scheme) _ = kcpv1.AddToScheme(Scheme) _ = orcv1alpha1.AddToScheme(Scheme) diff --git a/support/globalconfig/infrastructure.go b/support/globalconfig/infrastructure.go index 65464e3fca9..b380ce912af 100644 --- a/support/globalconfig/infrastructure.go +++ b/support/globalconfig/infrastructure.go @@ -29,7 +29,13 @@ func ReconcileInfrastructure(infra *configv1.Infrastructure, hcp *hyperv1.Hosted apiServerAddress := hcp.Status.ControlPlaneEndpoint.Host apiServerPort := hcp.Status.ControlPlaneEndpoint.Port - infra.Spec.PlatformSpec.Type = configv1.PlatformType(platformType) + // For MAAS platform, use "None" since OpenShift doesn't support MAAS yet + // This allows the infrastructure to be created while maintaining MAAS-specific logic + if platformType == hyperv1.MAASPlatform { + infra.Spec.PlatformSpec.Type = configv1.NonePlatformType + } else { + infra.Spec.PlatformSpec.Type = configv1.PlatformType(platformType) + } infra.Status.APIServerInternalURL = fmt.Sprintf("https://%s:%d", apiServerAddress, apiServerPort) if util.IsPrivateHCP(hcp) { infra.Status.APIServerInternalURL = fmt.Sprintf("https://api.%s.hypershift.local:%d", hcp.Name, apiServerPort) @@ -42,11 +48,20 @@ func ReconcileInfrastructure(infra *configv1.Infrastructure, hcp *hyperv1.Hosted infra.Status.EtcdDiscoveryDomain = BaseDomain(hcp) infra.Status.InfrastructureName = hcp.Spec.InfraID infra.Status.ControlPlaneTopology = configv1.ExternalTopologyMode - infra.Status.Platform = configv1.PlatformType(platformType) - if infra.Status.PlatformStatus == nil { - infra.Status.PlatformStatus = &configv1.PlatformStatus{} + // For MAAS platform, use "None" in status fields since OpenShift doesn't support MAAS yet + if platformType == hyperv1.MAASPlatform { + infra.Status.Platform = configv1.NonePlatformType + if infra.Status.PlatformStatus == nil { + infra.Status.PlatformStatus = &configv1.PlatformStatus{} + } + infra.Status.PlatformStatus.Type = configv1.NonePlatformType + } else { + infra.Status.Platform = configv1.PlatformType(platformType) + if infra.Status.PlatformStatus == nil { + infra.Status.PlatformStatus = &configv1.PlatformStatus{} + } + infra.Status.PlatformStatus.Type = configv1.PlatformType(platformType) } - infra.Status.PlatformStatus.Type = configv1.PlatformType(platformType) switch hcp.Spec.InfrastructureAvailabilityPolicy { case hyperv1.HighlyAvailable: @@ -101,5 +116,10 @@ func ReconcileInfrastructure(infra *configv1.Infrastructure, hcp *hyperv1.Hosted APIServerInternalIPs: []string{}, IngressIPs: []string{}, } + case hyperv1.MAASPlatform: + // MAAS platform configuration + // Note: OpenShift API doesn't have MAAS platform types yet + // The platform type is already set above, which is sufficient for now + // When OpenShift adds MAAS support, we can populate the specific fields } } diff --git a/support/images/envvars.go b/support/images/envvars.go index 88b246f8008..752269b8ae7 100644 --- a/support/images/envvars.go +++ b/support/images/envvars.go @@ -11,6 +11,7 @@ const ( KonnectivityEnvVar = "IMAGE_KONNECTIVITY" OpenStackCAPIProviderEnvVar = "IMAGE_OPENSTACK_CAPI_PROVIDER" OpenStackResourceControllerEnvVar = "IMAGE_OPENSTACK_RESOURCE_CONTROLLER" + MAASCAPIProviderEnvVar = "IMAGE_MAAS_CAPI_PROVIDER" ) // TagMapping returns a mapping between tags in an image-refs ImageStream @@ -25,6 +26,7 @@ func TagMapping() map[string]string { "cluster-api-provider-kubevirt": KubevirtCAPIProviderEnvVar, "cluster-api-provider-powervs": PowerVSCAPIProviderEnvVar, "cluster-api-provider-openstack": OpenStackCAPIProviderEnvVar, + "cluster-api-provider-maas": MAASCAPIProviderEnvVar, "openstack-resource-controller": OpenStackResourceControllerEnvVar, } } diff --git a/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/hostedcluster_types.go b/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/hostedcluster_types.go index 2299ffad3c0..cd2c1ede993 100644 --- a/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/hostedcluster_types.go +++ b/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/hostedcluster_types.go @@ -124,6 +124,10 @@ const ( // a HostedControlPlane. ClusterAPIOpenStackProviderImage = "hypershift.openshift.io/capi-provider-openstack-image" + // ClusterAPIProviderMAASImage overrides the CAPI MAAS provider image to use for + // a HostedControlPlane. + ClusterAPIProviderMAASImage = "hypershift.openshift.io/capi-provider-maas-image" + // OpenStackResourceControllerImage overrides the ORC image to use for a HostedControlPlane. OpenStackResourceControllerImage = "hypershift.openshift.io/orc-image" @@ -313,6 +317,11 @@ const ( // If set on both, the one on the NodePool takes precedence. The value can be a number or a percentage value. MachineHealthCheckMaxUnhealthyAnnotation = "hypershift.openshift.io/machine-health-check-max-unhealthy" + // MachineDeploymentProgressDeadlineSecondsAnnotation allows overriding the default machine deployment + // progress deadline timeout for nodepools. The annotation can be set in either the HostedCluster or the NodePool. + // If set on both, the one on the NodePool takes precedence. The value is a number of seconds (ie. 1800 for 30 minutes) + MachineDeploymentProgressDeadlineSecondsAnnotation = "hypershift.openshift.io/machine-deployment-progress-deadline-seconds" + // ClusterSizeOverrideAnnotation allows overriding the value of the size label regardless of the number // of workers associated with the HostedCluster. The value should be the desired size label. ClusterSizeOverrideAnnotation = "hypershift.openshift.io/cluster-size-override" @@ -1111,6 +1120,9 @@ const ( // OpenStackPlatform represents OpenStack infrastructure. OpenStackPlatform PlatformType = "OpenStack" + + // MAASPlatform represents MaaS (Metal as a Service) infrastructure. + MAASPlatform PlatformType = "MAAS" ) // List all PlatformType instances @@ -1124,6 +1136,7 @@ func PlatformTypes() []PlatformType { AzurePlatform, PowerVSPlatform, OpenStackPlatform, + MAASPlatform, } } @@ -1135,8 +1148,8 @@ type PlatformSpec struct { // +unionDiscriminator // +kubebuilder:validation:XValidation:rule="self == oldSelf", message="Type is immutable" // +immutable - // +openshift:validation:FeatureGateAwareEnum:featureGate="",enum=AWS;Azure;IBMCloud;KubeVirt;Agent;PowerVS;None - // +openshift:validation:FeatureGateAwareEnum:featureGate=OpenStack,enum=AWS;Azure;IBMCloud;KubeVirt;Agent;PowerVS;None;OpenStack + // +openshift:validation:FeatureGateAwareEnum:featureGate="",enum=AWS;Azure;IBMCloud;KubeVirt;Agent;PowerVS;None;MAAS + // +openshift:validation:FeatureGateAwareEnum:featureGate=OpenStack,enum=AWS;Azure;IBMCloud;KubeVirt;Agent;PowerVS;None;OpenStack;MAAS Type PlatformType `json:"type"` // AWS specifies configuration for clusters running on Amazon Web Services. @@ -1174,6 +1187,10 @@ type PlatformSpec struct { // +optional // +openshift:enable:FeatureGate=OpenStack OpenStack *OpenStackPlatformSpec `json:"openstack,omitempty"` + + // maas specifies configuration for clusters running on MaaS (Metal as a Service). + // +optional + MAAS *MAASPlatformSpec `json:"maas,omitempty"` } // IBMCloudPlatformSpec defines IBMCloud specific settings for components diff --git a/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/maas.go b/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/maas.go new file mode 100644 index 00000000000..c2368b0f5e5 --- /dev/null +++ b/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/maas.go @@ -0,0 +1,92 @@ +package v1beta1 + +// MAASPlatformSpec specifies configuration for clusters running on MaaS (Metal as a Service). +type MAASPlatformSpec struct { + // identityRef is a reference to a secret holding MAAS credentials + // to be used when reconciling the hosted cluster. + // + // +kubebuilder:validation:Required + // +required + IdentityRef MAASIdentityReference `json:"identityRef"` + + // dnsDomain is the DNS domain for the MAAS cluster. + // +optional + // +kubebuilder:validation:MaxLength=255 + DNSDomain string `json:"dnsDomain,omitempty"` + + // zone specifies the MAAS zone where the cluster will be deployed. + // If not specified, the cluster will be deployed in any available zone. + // +optional + // +kubebuilder:validation:MaxLength=255 + Zone string `json:"zone,omitempty"` +} + +// MAASIdentityReference is a reference to an infrastructure +// provider identity to be used to provision cluster resources. +type MAASIdentityReference struct { + // Name is the name of a secret in the same namespace as the resource being provisioned. + // The secret must contain the following keys: + // - `MAAS_ENDPOINT`: MAAS API endpoint URL + // - `MAAS_API_KEY`: MAAS API key for authentication + // + // +kubebuilder:validation:Required + // +required + Name string `json:"name"` +} + +// MAASNodePoolPlatform specifies the configuration for MaaS platform. +type MAASNodePoolPlatform struct { + // identityRef is a reference to a secret holding MAAS credentials + // to be used when reconciling the node pool. + // The secret must contain the following keys: + // - `MAAS_ENDPOINT`: MAAS API endpoint URL + // - `MAAS_API_KEY`: MAAS API key for authentication + // + // +kubebuilder:validation:Required + // +required + IdentityRef MAASIdentityReference `json:"identityRef"` + + // machineType specifies the type of MAAS machine to use for the nodes. + // This corresponds to the MAAS machine type/tag that will be used for node selection. + // +optional + // +kubebuilder:validation:MaxLength=255 + MachineType string `json:"machineType,omitempty"` + + // zone specifies the MAAS zone where the nodes will be deployed. + // If not specified, nodes will be deployed in any available zone. + // +optional + // +kubebuilder:validation:MaxLength=255 + Zone string `json:"zone,omitempty"` + + // tags specifies additional MAAS tags to apply to the nodes for filtering and organization. + // +optional + // +kubebuilder:validation:MaxItems=10 + Tags []string `json:"tags,omitempty"` + + // resourcePool specifies the MAAS resource pool to use for node allocation. + // +optional + // +kubebuilder:validation:MaxLength=255 + ResourcePool string `json:"resourcePool,omitempty"` + + // minCpu specifies the minimum CPU count required for the nodes. + // +optional + // +kubebuilder:validation:Minimum=1 + MinCPU *int32 `json:"minCpu,omitempty"` + + // minMemory specifies the minimum memory in MB required for the nodes. + // +optional + // +kubebuilder:validation:Minimum=1024 + MinMemory *int32 `json:"minMemory,omitempty"` + + // image specifies the MAAS image ID to use for the nodes. + // If not specified, a default image will be used based on the release. + // +optional + // +kubebuilder:validation:MaxLength=255 + Image string `json:"image,omitempty"` + + // failureDomain specifies the failure domain the machine will be created in. + // Must match a key in the FailureDomains map stored on the cluster object. + // +optional + // +kubebuilder:validation:MaxLength=255 + FailureDomain string `json:"failureDomain,omitempty"` +} diff --git a/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/nodepool_conditions.go b/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/nodepool_conditions.go index ef6787b327a..5e0b9df3f7d 100644 --- a/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/nodepool_conditions.go +++ b/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/nodepool_conditions.go @@ -118,6 +118,7 @@ const ( NodePoolValidArchPlatform = "ValidArchPlatform" NodePoolInvalidArchPlatform = "InvalidArchPlatform" InvalidKubevirtMachineTemplate = "InvalidKubevirtMachineTemplate" + InvalidMAASMachineTemplate = "InvalidMAASMachineTemplate" InvalidOpenStackMachineTemplate = "InvalidOpenStackMachineTemplate" CIDRConflictReason = "CIDRConflict" NodePoolKubeVirtLiveMigratableReason = "KubeVirtNodesNotLiveMigratable" diff --git a/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/nodepool_types.go b/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/nodepool_types.go index 8d3dbcbfdd7..8370c29d04c 100644 --- a/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/nodepool_types.go +++ b/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/nodepool_types.go @@ -428,8 +428,8 @@ type NodePoolPlatform struct { // +unionDiscriminator // +kubebuilder:validation:XValidation:rule="self == oldSelf", message="Type is immutable" // +immutable - // +openshift:validation:FeatureGateAwareEnum:featureGate="",enum=AWS;Azure;IBMCloud;KubeVirt;Agent;PowerVS;None - // +openshift:validation:FeatureGateAwareEnum:featureGate=OpenStack,enum=AWS;Azure;IBMCloud;KubeVirt;Agent;PowerVS;None;OpenStack + // +openshift:validation:FeatureGateAwareEnum:featureGate="",enum=AWS;Azure;IBMCloud;KubeVirt;Agent;PowerVS;None;MAAS + // +openshift:validation:FeatureGateAwareEnum:featureGate=OpenStack,enum=AWS;Azure;IBMCloud;KubeVirt;Agent;PowerVS;None;OpenStack;MAAS Type PlatformType `json:"type"` // AWS specifies the configuration used when operating on AWS. @@ -461,6 +461,10 @@ type NodePoolPlatform struct { // +optional // +openshift:enable:FeatureGate=OpenStack OpenStack *OpenStackNodePoolPlatform `json:"openstack,omitempty"` + + // maas specifies the configuration used when using MaaS platform. + // +optional + MAAS *MAASNodePoolPlatform `json:"maas,omitempty"` } // We define our own condition type since metav1.Condition has validation diff --git a/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/zz_generated.deepcopy.go b/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/zz_generated.deepcopy.go index 4fd8653510c..a97c9f75439 100644 --- a/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/zz_generated.deepcopy.go +++ b/vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/zz_generated.deepcopy.go @@ -2469,6 +2469,70 @@ func (in *LoadBalancerPublishingStrategy) DeepCopy() *LoadBalancerPublishingStra return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MAASIdentityReference) DeepCopyInto(out *MAASIdentityReference) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MAASIdentityReference. +func (in *MAASIdentityReference) DeepCopy() *MAASIdentityReference { + if in == nil { + return nil + } + out := new(MAASIdentityReference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MAASNodePoolPlatform) DeepCopyInto(out *MAASNodePoolPlatform) { + *out = *in + out.IdentityRef = in.IdentityRef + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.MinCPU != nil { + in, out := &in.MinCPU, &out.MinCPU + *out = new(int32) + **out = **in + } + if in.MinMemory != nil { + in, out := &in.MinMemory, &out.MinMemory + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MAASNodePoolPlatform. +func (in *MAASNodePoolPlatform) DeepCopy() *MAASNodePoolPlatform { + if in == nil { + return nil + } + out := new(MAASNodePoolPlatform) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MAASPlatformSpec) DeepCopyInto(out *MAASPlatformSpec) { + *out = *in + out.IdentityRef = in.IdentityRef + out.DNSDomain = in.DNSDomain + out.Zone = in.Zone +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MAASPlatformSpec. +func (in *MAASPlatformSpec) DeepCopy() *MAASPlatformSpec { + if in == nil { + return nil + } + out := new(MAASPlatformSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MachineNetworkEntry) DeepCopyInto(out *MachineNetworkEntry) { *out = *in @@ -2750,6 +2814,11 @@ func (in *NodePoolPlatform) DeepCopyInto(out *NodePoolPlatform) { *out = new(OpenStackNodePoolPlatform) (*in).DeepCopyInto(*out) } + if in.MAAS != nil { + in, out := &in.MAAS, &out.MAAS + *out = new(MAASNodePoolPlatform) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodePoolPlatform. @@ -3089,6 +3158,11 @@ func (in *PlatformSpec) DeepCopyInto(out *PlatformSpec) { *out = new(OpenStackPlatformSpec) (*in).DeepCopyInto(*out) } + if in.MAAS != nil { + in, out := &in.MAAS, &out.MAAS + *out = new(MAASPlatformSpec) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlatformSpec. diff --git a/vendor/github.com/spectrocloud/cluster-api-provider-maas/LICENSE b/vendor/github.com/spectrocloud/cluster-api-provider-maas/LICENSE new file mode 100644 index 00000000000..261eeb9e9f8 --- /dev/null +++ b/vendor/github.com/spectrocloud/cluster-api-provider-maas/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/condition_consts.go b/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/condition_consts.go new file mode 100644 index 00000000000..5746e5aa18c --- /dev/null +++ b/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/condition_consts.go @@ -0,0 +1,86 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + +// Conditions and condition Reasons for the MAAS Machine object + +const ( + // MachineDeployedCondition documents the status of the deployment of a machine + + MachineDeployedCondition clusterv1.ConditionType = "MachineDeployed" + + // WaitingForClusterInfrastructureReason (Severity=Info) documents a MachineMachine waiting for the cluster + // infrastructure to be ready before starting to deploy the machine that provides the MachineMachine + // infrastructure. + WaitingForClusterInfrastructureReason = "WaitingForClusterInfrastructure" + + // WaitingForBootstrapDataReason (Severity=Info) documents a MachineMachine waiting for the bootstrap + // script to be ready before starting to create the container that provides the MachineMachine infrastructure. + WaitingForBootstrapDataReason = "WaitingForBootstrapData" + + // MachineDeployingReason + MachineDeployingReason = "MachineDeploying" + + // MachineTerminatedReason + MachineTerminatedReason = "MachineTerminatedReason" + + // MachineDeployingReason + MachinePoweredOffReason = "MachinePoweredOff" + + // MachineNotFoundReason used when the machine couldn't be retrieved. + MachineNotFoundReason = "MachineNotFound" + + // MachineDeployFailedReason (Severity=Warning) documents a MachineMachine controller detecting + // an error while deploying the MaaS machine that provides the MachineMachine infrastructure; those kind of + // errors are usually transient and failed provisioning are automatically re-tried by the controller. + MachineDeployFailedReason = "MachineDeployFailed" + + // MachineDeployStartedReason (Severity=Info) documents a MachineMachine controller started deploying + MachineDeployStartedReason = "MachineDeployStartedReason" +) + +const ( + // Only applicable to control plane machines. DNSAttachedCondition will report true when a control plane is successfully registered with an DNS + // When set to false, severity can be an Error if the subnet is not found or unavailable in the instance's AZ + DNSAttachedCondition clusterv1.ConditionType = "DNSAttached" + + DNSDetachPending = "DNSDetachPending" + DNSAttachPending = "DNSAttachPending" +) + +// Cluster Conditions + +const ( + // DNSReadyCondition documents the availability of the container that implements the cluster DNS. + DNSReadyCondition clusterv1.ConditionType = "LoadBalancerReady" + + // LoadBalancerProvisioningFailedReason (Severity=Warning) documents a MAASCluster controller detecting + // dns reconcile failure will be retried + DNSFailedReason = "LoadBalancerFailed" + + WaitForDNSNameReason = "WaitForDNSName" +) + +const ( + // APIServerAvailableCondition documents whether API server is reachable + APIServerAvailableCondition clusterv1.ConditionType = "APIServerAvailable" + + // APIServerNotReadyReason api server isn't responding + APIServerNotReadyReason = "APIServerNotReady" +) diff --git a/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/conversion.go b/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/conversion.go new file mode 100644 index 00000000000..53b675027c2 --- /dev/null +++ b/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/conversion.go @@ -0,0 +1,29 @@ +/* + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +func (*MaasCluster) Hub() {} + +func (*MaasClusterList) Hub() {} + +func (*MaasMachine) Hub() {} + +func (*MaasMachineList) Hub() {} + +func (*MaasMachineTemplate) Hub() {} + +func (*MaasMachineTemplateList) Hub() {} diff --git a/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/groupversion_info.go b/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/groupversion_info.go new file mode 100644 index 00000000000..dcfd5baf863 --- /dev/null +++ b/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/groupversion_info.go @@ -0,0 +1,36 @@ +/* + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v1beta1 contains API Schema definitions for the infrastructure v1beta1 API group +// +kubebuilder:object:generate=true +// +groupName=infrastructure.cluster.x-k8s.io +package v1beta1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "infrastructure.cluster.x-k8s.io", Version: "v1beta1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/maascluster_types.go b/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/maascluster_types.go new file mode 100644 index 00000000000..b3ba3417ecf --- /dev/null +++ b/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/maascluster_types.go @@ -0,0 +1,120 @@ +/* + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" +) + +const ( + // ClusterFinalizer allows MaasClusterReconciler to clean up resources associated with MaasCluster before + // removing it from the apiserver. + ClusterFinalizer = "maascluster.infrastructure.cluster.x-k8s.io" +) + +// MaasClusterSpec defines the desired state of MaasCluster +type MaasClusterSpec struct { + // DNSDomain configures the MaaS domain to create the cluster on (e.g maas) + // +kubebuilder:validation:MinLength=1 + DNSDomain string `json:"dnsDomain"` + + // ControlPlaneEndpoint represents the endpoint used to communicate with the control plane. + // +optional + ControlPlaneEndpoint APIEndpoint `json:"controlPlaneEndpoint"` + + // FailureDomains are not usually defined on the spec. + // but useful for MaaS since we can limit the domains to these + // +optional + FailureDomains []string `json:"failureDomains,omitempty"` +} + +// MaasClusterStatus defines the observed state of MaasCluster +type MaasClusterStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + // Ready denotes that the maas cluster (infrastructure) is ready. + // +kubebuilder:default=false + Ready bool `json:"ready"` + + // Network represents the network + Network Network `json:"network,omitempty"` + + // FailureDomains don't mean much in CAPMAAS since it's all local, but we can see how the rest of cluster API + // will use this if we populate it. + FailureDomains clusterv1.FailureDomains `json:"failureDomains,omitempty"` + + // Conditions defines current service state of the MaasCluster. + // +optional + Conditions clusterv1.Conditions `json:"conditions,omitempty"` +} + +// Network encapsulates the Cluster Network +type Network struct { + // DNSName is the Kubernetes api server name + DNSName string `json:"dnsName,omitempty"` +} + +// APIEndpoint represents a reachable Kubernetes API endpoint. +type APIEndpoint struct { + + // Host is the hostname on which the API server is serving. + Host string `json:"host"` + + // Port is the port on which the API server is serving. + Port int `json:"port"` +} + +// IsZero returns true if both host and port are zero values. +func (in APIEndpoint) IsZero() bool { + return in.Host == "" && in.Port == 0 +} + +// +kubebuilder:resource:path=maasclusters,scope=Namespaced,categories=cluster-api +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:storageversion + +// MaasCluster is the Schema for the maasclusters API +type MaasCluster struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec MaasClusterSpec `json:"spec,omitempty"` + Status MaasClusterStatus `json:"status,omitempty"` +} + +func (in *MaasCluster) GetConditions() clusterv1.Conditions { + return in.Status.Conditions +} + +func (in *MaasCluster) SetConditions(conditions clusterv1.Conditions) { + in.Status.Conditions = conditions +} + +//+kubebuilder:object:root=true + +// MaasClusterList contains a list of MaasCluster +type MaasClusterList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []MaasCluster `json:"items"` +} + +func init() { + SchemeBuilder.Register(&MaasCluster{}, &MaasClusterList{}) +} diff --git a/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/maascluster_webhook.go b/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/maascluster_webhook.go new file mode 100644 index 00000000000..2eb69cd5820 --- /dev/null +++ b/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/maascluster_webhook.go @@ -0,0 +1,76 @@ +/* + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + "fmt" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" +) + +// log is for logging in this package. +var maasclusterlog = logf.Log.WithName("maascluster-resource") + +func (r *MaasCluster) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +//+kubebuilder:webhook:path=/mutate-infrastructure-cluster-x-k8s-io-v1beta1-maascluster,mutating=true,failurePolicy=fail,groups=infrastructure.cluster.x-k8s.io,resources=maasclusters,verbs=create;update,versions=v1beta1,name=mmaascluster.kb.io,sideEffects=None,admissionReviewVersions=v1beta1;v1 +//+kubebuilder:webhook:verbs=create;update,path=/validate-infrastructure-cluster-x-k8s-io-v1beta1-maascluster,mutating=false,failurePolicy=fail,groups=infrastructure.cluster.x-k8s.io,resources=maasclusters,versions=v1beta1,name=vmaascluster.kb.io,sideEffects=None,admissionReviewVersions=v1beta1;v1 + +var ( + _ webhook.Defaulter = &MaasCluster{} + _ webhook.Validator = &MaasCluster{} +) + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *MaasCluster) Default() { + maasclusterlog.Info("default", "name", r.Name) +} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *MaasCluster) ValidateCreate() (admission.Warnings, error) { + maasclusterlog.Info("validate create", "name", r.Name) + return nil, nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *MaasCluster) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + maasclusterlog.Info("validate update", "name", r.Name) + oldC, ok := old.(*MaasCluster) + if !ok { + return nil, apierrors.NewBadRequest(fmt.Sprintf("expected a MaasCluster but got a %T", old)) + } + + if r.Spec.DNSDomain != oldC.Spec.DNSDomain { + return nil, apierrors.NewBadRequest("changing cluster DNS Domain not allowed") + } + return nil, nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *MaasCluster) ValidateDelete() (admission.Warnings, error) { + maasclusterlog.Info("validate delete", "name", r.Name) + return nil, nil +} diff --git a/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/maasmachine_types.go b/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/maasmachine_types.go new file mode 100644 index 00000000000..1ee3fae0498 --- /dev/null +++ b/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/maasmachine_types.go @@ -0,0 +1,137 @@ +/* + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "sigs.k8s.io/cluster-api/errors" +) + +const ( + // MachineFinalizer allows MaasMachineReconciler to clean up resources associated with MaasMachine before + // removing it from the apiserver. + MachineFinalizer = "maasmachine.infrastructure.cluster.x-k8s.io" +) + +// MaasMachineSpec defines the desired state of MaasMachine +type MaasMachineSpec struct { + + // FailureDomain is the failure domain the machine will be created in. + // Must match a key in the FailureDomains map stored on the cluster object. + // +optional + FailureDomain *string `json:"failureDomain,omitempty"` + + // SystemID will be the MaaS machine ID + // +optional + SystemID *string `json:"systemID,omitempty"` + + // ProviderID will be the name in ProviderID format (maas:///system_id) + // +optional + ProviderID *string `json:"providerID,omitempty"` + + // ResourcePool will be the MAAS Machine resourcepool + // +optional + ResourcePool *string `json:"resourcePool,omitempty"` + + // MinCPU minimum number of CPUs + // +kubebuilder:validation:Minimum=0 + MinCPU *int `json:"minCPU"` + + // MinMemoryInMB minimum memory in MB + // +kubebuilder:validation:Minimum=0 + MinMemoryInMB *int `json:"minMemory"` + + // Tags for placement + // +optional + Tags []string `json:"tags,omitempty"` + + // Image will be the MaaS image id + // +kubebuilder:validation:MinLength=1 + Image string `json:"image"` +} + +// MaasMachineStatus defines the observed state of MaasMachine +type MaasMachineStatus struct { + + // Ready denotes that the machine (maas container) is ready + // +kubebuilder:default=false + Ready bool `json:"ready"` + + // MachineState is the state of this MAAS machine. + MachineState *MachineState `json:"machineState,omitempty"` + + // MachinePowered is if the machine is "Powered" on + MachinePowered bool `json:"machinePowered,omitempty"` + + // Hostname is the actual MaaS hostname + Hostname *string `json:"hostname,omitempty"` + + // DNSAttached specifies whether the DNS record contains the IP of this machine + DNSAttached bool `json:"dnsAttached,omitempty"` + + // Addresses contains the associated addresses for the maas machine. + Addresses []clusterv1.MachineAddress `json:"addresses,omitempty"` + + // Conditions defines current service state of the MaasMachine. + Conditions clusterv1.Conditions `json:"conditions,omitempty"` + + // FailureReason will be set in the event that there is a terminal problem + // reconciling the Machine and will contain a succinct value suitable + // for machine interpretation. + FailureReason *errors.MachineStatusError `json:"failureReason,omitempty"` + + // FailureMessage will be set in the event that there is a terminal problem + // reconciling the Machine and will contain a more verbose string suitable + // for logging and human consumption. + FailureMessage *string `json:"failureMessage,omitempty"` +} + +// +kubebuilder:resource:path=maasmachines,scope=Namespaced,categories=cluster-api +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:storageversion + +// MaasMachine is the Schema for the maasmachines API +type MaasMachine struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec MaasMachineSpec `json:"spec,omitempty"` + Status MaasMachineStatus `json:"status,omitempty"` +} + +func (c *MaasMachine) GetConditions() clusterv1.Conditions { + return c.Status.Conditions +} + +func (c *MaasMachine) SetConditions(conditions clusterv1.Conditions) { + c.Status.Conditions = conditions +} + +//+kubebuilder:object:root=true + +// MaasMachineList contains a list of MaasMachine +type MaasMachineList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []MaasMachine `json:"items"` +} + +func init() { + SchemeBuilder.Register(&MaasMachine{}, &MaasMachineList{}) +} diff --git a/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/maasmachine_webhook.go b/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/maasmachine_webhook.go new file mode 100644 index 00000000000..29bc36111e5 --- /dev/null +++ b/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/maasmachine_webhook.go @@ -0,0 +1,81 @@ +/* + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + "fmt" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" +) + +// log is for logging in this package. +var maasmachinelog = logf.Log.WithName("maasmachine-resource") + +func (r *MaasMachine) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +//+kubebuilder:webhook:path=/mutate-infrastructure-cluster-x-k8s-io-v1beta1-maasmachine,mutating=true,failurePolicy=fail,groups=infrastructure.cluster.x-k8s.io,resources=maasmachines,verbs=create;update,versions=v1beta1,name=mmaasmachine.kb.io,sideEffects=None,admissionReviewVersions=v1beta1;v1 +//+kubebuilder:webhook:verbs=create;update,path=/validate-infrastructure-cluster-x-k8s-io-v1beta1-maasmachine,mutating=false,failurePolicy=fail,groups=infrastructure.cluster.x-k8s.io,resources=maasmachines,versions=v1beta1,name=vmaasmachine.kb.io,sideEffects=None,admissionReviewVersions=v1beta1;v1 + +var ( + _ webhook.Defaulter = &MaasMachine{} + _ webhook.Validator = &MaasMachine{} +) + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *MaasMachine) Default() { + maasmachinelog.Info("default", "name", r.Name) +} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *MaasMachine) ValidateCreate() (admission.Warnings, error) { + maasmachinelog.Info("validate create", "name", r.Name) + return nil, nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *MaasMachine) ValidateDelete() (admission.Warnings, error) { + maasmachinelog.Info("validate delete", "name", r.Name) + return nil, nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *MaasMachine) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + maasmachinelog.Info("validate update", "name", r.Name) + oldM := old.(*MaasMachine) + + if r.Spec.Image != oldM.Spec.Image { + return nil, apierrors.NewBadRequest(fmt.Sprintf("maas machine image change is not allowed, old=%s, new=%s", oldM.Spec.Image, r.Spec.Image)) + } + + if *r.Spec.MinCPU != *oldM.Spec.MinCPU { + return nil, apierrors.NewBadRequest(fmt.Sprintf("maas machine min cpu count change is not allowed, old=%d, new=%d", oldM.Spec.MinCPU, r.Spec.MinCPU)) + } + + if *r.Spec.MinMemoryInMB != *oldM.Spec.MinMemoryInMB { + return nil, apierrors.NewBadRequest(fmt.Sprintf("maas machine min memory change is not allowed, old=%d MB, new=%d MB", oldM.Spec.MinMemoryInMB, r.Spec.MinMemoryInMB)) + } + return nil, nil +} diff --git a/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/maasmachinetemplate_types.go b/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/maasmachinetemplate_types.go new file mode 100644 index 00000000000..cd5469a7000 --- /dev/null +++ b/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/maasmachinetemplate_types.go @@ -0,0 +1,58 @@ +/* + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// MaasMachineTemplateSpec defines the desired state of MaasMachineTemplate +type MaasMachineTemplateSpec struct { + Template MaasMachineTemplateResource `json:"template"` +} + +// MaasMachineTemplateResource describes the data needed to create a MaasMachine from a template +type MaasMachineTemplateResource struct { + // Spec is the specification of the desired behavior of the machine. + Spec MaasMachineSpec `json:"spec"` +} + +// +kubebuilder:resource:path=maasmachinetemplates,scope=Namespaced,categories=cluster-api +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:storageversion + +// MaasMachineTemplate is the Schema for the maasmachinetemplates API +type MaasMachineTemplate struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec MaasMachineTemplateSpec `json:"spec,omitempty"` +} + +//+kubebuilder:object:root=true + +// MaasMachineTemplateList contains a list of MaasMachineTemplate +type MaasMachineTemplateList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []MaasMachineTemplate `json:"items"` +} + +func init() { + SchemeBuilder.Register(&MaasMachineTemplate{}, &MaasMachineTemplateList{}) +} diff --git a/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/maasmachinetemplate_webhook.go b/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/maasmachinetemplate_webhook.go new file mode 100644 index 00000000000..d6876062ced --- /dev/null +++ b/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/maasmachinetemplate_webhook.go @@ -0,0 +1,81 @@ +/* + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + "fmt" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" +) + +// log is for logging in this package. +var maasmachinetemplatelog = logf.Log.WithName("maasmachinetemplate-resource") + +func (r *MaasMachineTemplate) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +//+kubebuilder:webhook:path=/mutate-infrastructure-cluster-x-k8s-io-v1beta1-maasmachinetemplate,mutating=true,failurePolicy=fail,groups=infrastructure.cluster.x-k8s.io,resources=maasmachinetemplates,verbs=create;update,versions=v1beta1,name=mmaasmachinetemplate.kb.io,sideEffects=None,admissionReviewVersions=v1beta1;v1 +//+kubebuilder:webhook:verbs=create;update,path=/validate-infrastructure-cluster-x-k8s-io-v1beta1-maasmachinetemplate,mutating=false,failurePolicy=fail,groups=infrastructure.cluster.x-k8s.io,resources=maasmachinetemplates,versions=v1beta1,name=vmaasmachinetemplate.kb.io,sideEffects=None,admissionReviewVersions=v1beta1;v1 + +var ( + _ webhook.Defaulter = &MaasMachineTemplate{} + _ webhook.Validator = &MaasMachineTemplate{} +) + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *MaasMachineTemplate) Default() { + maasmachinetemplatelog.Info("default", "name", r.Name) +} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *MaasMachineTemplate) ValidateCreate() (admission.Warnings, error) { + maasmachinetemplatelog.Info("validate create", "name", r.Name) + return nil, nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *MaasMachineTemplate) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + maasmachinetemplatelog.Info("validate update", "name", r.Name) + oldM := old.(*MaasMachineTemplate) + + if r.Spec.Template.Spec.Image != oldM.Spec.Template.Spec.Image { + return nil, apierrors.NewBadRequest(fmt.Sprintf("maas machine template image change is not allowed, old=%s, new=%s", oldM.Spec.Template.Spec.Image, r.Spec.Template.Spec.Image)) + } + + if *r.Spec.Template.Spec.MinCPU != *oldM.Spec.Template.Spec.MinCPU { + return nil, apierrors.NewBadRequest(fmt.Sprintf("maas machine template min cpu count change is not allowed, old=%d, new=%d", oldM.Spec.Template.Spec.MinCPU, r.Spec.Template.Spec.MinCPU)) + } + + if *r.Spec.Template.Spec.MinMemoryInMB != *oldM.Spec.Template.Spec.MinMemoryInMB { + return nil, apierrors.NewBadRequest(fmt.Sprintf("maas machine template min memory change is not allowed, old=%d MB, new=%d MB", oldM.Spec.Template.Spec.MinMemoryInMB, r.Spec.Template.Spec.MinMemoryInMB)) + } + return nil, nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *MaasMachineTemplate) ValidateDelete() (admission.Warnings, error) { + maasmachinetemplatelog.Info("validate delete", "name", r.Name) + return nil, nil +} diff --git a/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/types.go b/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/types.go new file mode 100644 index 00000000000..fb849010a3e --- /dev/null +++ b/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/types.go @@ -0,0 +1,94 @@ +package v1beta1 + +import ( + "k8s.io/apimachinery/pkg/util/sets" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" +) + +// MachineState describes the state of an MAAS Machine. +type MachineState string + +// List of all possible states: https://github.com/maas/maas/blob/master/src/maasserver/enum.py#L108 + +var ( + // MachineStateAllocated is the string representing an instance in a ready (commissioned) state + MachineStateAllocated = MachineState("Allocated") + + //MachineStateDeploying is the string representing an instance in a deploying state + MachineStateDeploying = MachineState("Deploying") + + // MachineStateDeployed is the string representing an instance in a pending state + MachineStateDeployed = MachineState("Deployed") + + // MachineStateReady is the string representing an instance in a ready (commissioned) state + MachineStateReady = MachineState("Ready") + + // MachineStateDiskErasing is the string representing an instance which is releasing (disk) + MachineStateDiskErasing = MachineState("Disk erasing") + + // MachineStateDiskErasing is the string representing an instance which is releasing + MachineStateReleasing = MachineState("Releasing") + + // MachineStateNew is the string representing an instance which is not yet commissioned + MachineStateNew = MachineState("New") + + //// MachineStateShuttingDown is the string representing an instance shutting down + //MachineStateShuttingDown = MachineState("shutting-down") + // + //// MachineStateTerminated is the string representing an instance that has been terminated + //MachineStateTerminated = MachineState("terminated") + // + //// MachineStateStopping is the string representing an instance + //// that is in the process of being stopped and can be restarted + //MachineStateStopping = MachineState("stopping") + + // MachineStateStopped is the string representing an instance + // that has been stopped and can be restarted + //MachineStateStopped = MachineState("stopped") + + // MachineRunningStates defines the set of states in which an MaaS instance is + // running or going to be running soon + MachineRunningStates = sets.NewString( + string(MachineStateDeploying), + string(MachineStateDeployed), + ) + + // MachineOperationalStates defines the set of states in which an MaaS instance is + // or can return to running, and supports all MaaS operations + MachineOperationalStates = MachineRunningStates.Union( + sets.NewString( + string(MachineStateAllocated), + ), + ) + + // MachineKnownStates represents all known MaaS instance states + MachineKnownStates = MachineOperationalStates.Union( + sets.NewString( + string(MachineStateDiskErasing), + string(MachineStateReleasing), + string(MachineStateReady), + string(MachineStateNew), + //string(MachineStateTerminated), + ), + ) +) + +// Instance describes an MAAS Machine. +type Machine struct { + ID string + + // Hostname is the hostname + Hostname string + + // The current state of the machine. + State MachineState + + // The current state of the machine. + Powered bool + + // The AZ of the machine + AvailabilityZone string + + // Addresses contains the MAAS Machine associated addresses. + Addresses []clusterv1.MachineAddress +} diff --git a/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/zz_generated.deepcopy.go b/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/zz_generated.deepcopy.go new file mode 100644 index 00000000000..696f37c6f04 --- /dev/null +++ b/vendor/github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1/zz_generated.deepcopy.go @@ -0,0 +1,434 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "k8s.io/apimachinery/pkg/runtime" + apiv1beta1 "sigs.k8s.io/cluster-api/api/v1beta1" + "sigs.k8s.io/cluster-api/errors" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APIEndpoint) DeepCopyInto(out *APIEndpoint) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIEndpoint. +func (in *APIEndpoint) DeepCopy() *APIEndpoint { + if in == nil { + return nil + } + out := new(APIEndpoint) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MaasCluster) DeepCopyInto(out *MaasCluster) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MaasCluster. +func (in *MaasCluster) DeepCopy() *MaasCluster { + if in == nil { + return nil + } + out := new(MaasCluster) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MaasCluster) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MaasClusterList) DeepCopyInto(out *MaasClusterList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]MaasCluster, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MaasClusterList. +func (in *MaasClusterList) DeepCopy() *MaasClusterList { + if in == nil { + return nil + } + out := new(MaasClusterList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MaasClusterList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MaasClusterSpec) DeepCopyInto(out *MaasClusterSpec) { + *out = *in + out.ControlPlaneEndpoint = in.ControlPlaneEndpoint + if in.FailureDomains != nil { + in, out := &in.FailureDomains, &out.FailureDomains + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MaasClusterSpec. +func (in *MaasClusterSpec) DeepCopy() *MaasClusterSpec { + if in == nil { + return nil + } + out := new(MaasClusterSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MaasClusterStatus) DeepCopyInto(out *MaasClusterStatus) { + *out = *in + out.Network = in.Network + if in.FailureDomains != nil { + in, out := &in.FailureDomains, &out.FailureDomains + *out = make(apiv1beta1.FailureDomains, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(apiv1beta1.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MaasClusterStatus. +func (in *MaasClusterStatus) DeepCopy() *MaasClusterStatus { + if in == nil { + return nil + } + out := new(MaasClusterStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MaasMachine) DeepCopyInto(out *MaasMachine) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MaasMachine. +func (in *MaasMachine) DeepCopy() *MaasMachine { + if in == nil { + return nil + } + out := new(MaasMachine) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MaasMachine) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MaasMachineList) DeepCopyInto(out *MaasMachineList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]MaasMachine, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MaasMachineList. +func (in *MaasMachineList) DeepCopy() *MaasMachineList { + if in == nil { + return nil + } + out := new(MaasMachineList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MaasMachineList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MaasMachineSpec) DeepCopyInto(out *MaasMachineSpec) { + *out = *in + if in.FailureDomain != nil { + in, out := &in.FailureDomain, &out.FailureDomain + *out = new(string) + **out = **in + } + if in.SystemID != nil { + in, out := &in.SystemID, &out.SystemID + *out = new(string) + **out = **in + } + if in.ProviderID != nil { + in, out := &in.ProviderID, &out.ProviderID + *out = new(string) + **out = **in + } + if in.ResourcePool != nil { + in, out := &in.ResourcePool, &out.ResourcePool + *out = new(string) + **out = **in + } + if in.MinCPU != nil { + in, out := &in.MinCPU, &out.MinCPU + *out = new(int) + **out = **in + } + if in.MinMemoryInMB != nil { + in, out := &in.MinMemoryInMB, &out.MinMemoryInMB + *out = new(int) + **out = **in + } + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MaasMachineSpec. +func (in *MaasMachineSpec) DeepCopy() *MaasMachineSpec { + if in == nil { + return nil + } + out := new(MaasMachineSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MaasMachineStatus) DeepCopyInto(out *MaasMachineStatus) { + *out = *in + if in.MachineState != nil { + in, out := &in.MachineState, &out.MachineState + *out = new(MachineState) + **out = **in + } + if in.Hostname != nil { + in, out := &in.Hostname, &out.Hostname + *out = new(string) + **out = **in + } + if in.Addresses != nil { + in, out := &in.Addresses, &out.Addresses + *out = make([]apiv1beta1.MachineAddress, len(*in)) + copy(*out, *in) + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(apiv1beta1.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.FailureReason != nil { + in, out := &in.FailureReason, &out.FailureReason + *out = new(errors.MachineStatusError) + **out = **in + } + if in.FailureMessage != nil { + in, out := &in.FailureMessage, &out.FailureMessage + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MaasMachineStatus. +func (in *MaasMachineStatus) DeepCopy() *MaasMachineStatus { + if in == nil { + return nil + } + out := new(MaasMachineStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MaasMachineTemplate) DeepCopyInto(out *MaasMachineTemplate) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MaasMachineTemplate. +func (in *MaasMachineTemplate) DeepCopy() *MaasMachineTemplate { + if in == nil { + return nil + } + out := new(MaasMachineTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MaasMachineTemplate) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MaasMachineTemplateList) DeepCopyInto(out *MaasMachineTemplateList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]MaasMachineTemplate, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MaasMachineTemplateList. +func (in *MaasMachineTemplateList) DeepCopy() *MaasMachineTemplateList { + if in == nil { + return nil + } + out := new(MaasMachineTemplateList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MaasMachineTemplateList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MaasMachineTemplateResource) DeepCopyInto(out *MaasMachineTemplateResource) { + *out = *in + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MaasMachineTemplateResource. +func (in *MaasMachineTemplateResource) DeepCopy() *MaasMachineTemplateResource { + if in == nil { + return nil + } + out := new(MaasMachineTemplateResource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MaasMachineTemplateSpec) DeepCopyInto(out *MaasMachineTemplateSpec) { + *out = *in + in.Template.DeepCopyInto(&out.Template) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MaasMachineTemplateSpec. +func (in *MaasMachineTemplateSpec) DeepCopy() *MaasMachineTemplateSpec { + if in == nil { + return nil + } + out := new(MaasMachineTemplateSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Machine) DeepCopyInto(out *Machine) { + *out = *in + if in.Addresses != nil { + in, out := &in.Addresses, &out.Addresses + *out = make([]apiv1beta1.MachineAddress, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Machine. +func (in *Machine) DeepCopy() *Machine { + if in == nil { + return nil + } + out := new(Machine) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Network) DeepCopyInto(out *Network) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Network. +func (in *Network) DeepCopy() *Network { + if in == nil { + return nil + } + out := new(Network) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/modules.txt b/vendor/modules.txt index fd422a2626e..a9b11209ab7 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -979,6 +979,11 @@ github.com/sirupsen/logrus # github.com/soheilhy/cmux v0.1.5 ## explicit; go 1.11 github.com/soheilhy/cmux +# github.com/spectrocloud/cluster-api-provider-maas v0.5.1-0.20250512112717-769064ca22e9 +## explicit; go 1.23.0 +github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1 +# github.com/spectrocloud/maas-client-go v0.0.1-beta1.0.20230830132549-2f7491722359 +## explicit; go 1.15 # github.com/spf13/cobra v1.8.1 ## explicit; go 1.15 github.com/spf13/cobra @@ -2121,6 +2126,8 @@ k8s.io/client-go/util/keyutil k8s.io/client-go/util/retry k8s.io/client-go/util/watchlist k8s.io/client-go/util/workqueue +# k8s.io/cluster-bootstrap v0.31.3 +## explicit; go 1.22.0 # k8s.io/component-base v0.32.2 ## explicit; go 1.23.0 k8s.io/component-base/cli @@ -2444,7 +2451,7 @@ sigs.k8s.io/kustomize/kyaml/yaml/walk # sigs.k8s.io/secrets-store-csi-driver v1.4.8 ## explicit; go 1.21 sigs.k8s.io/secrets-store-csi-driver/apis/v1 -# sigs.k8s.io/structured-merge-diff/v4 v4.4.2 +# sigs.k8s.io/structured-merge-diff/v4 v4.4.3 ## explicit; go 1.13 sigs.k8s.io/structured-merge-diff/v4/fieldpath sigs.k8s.io/structured-merge-diff/v4/merge diff --git a/vendor/sigs.k8s.io/structured-merge-diff/v4/merge/update.go b/vendor/sigs.k8s.io/structured-merge-diff/v4/merge/update.go index 34ab2d6fb4f..455818ff858 100644 --- a/vendor/sigs.k8s.io/structured-merge-diff/v4/merge/update.go +++ b/vendor/sigs.k8s.io/structured-merge-diff/v4/merge/update.go @@ -33,6 +33,9 @@ type UpdaterBuilder struct { Converter Converter IgnoreFilter map[fieldpath.APIVersion]fieldpath.Filter + // IgnoredFields provides a set of fields to ignore for each + IgnoredFields map[fieldpath.APIVersion]*fieldpath.Set + // Stop comparing the new object with old object after applying. // This was initially used to avoid spurious etcd update, but // since that's vastly inefficient, we've come-up with a better @@ -46,6 +49,7 @@ func (u *UpdaterBuilder) BuildUpdater() *Updater { return &Updater{ Converter: u.Converter, IgnoreFilter: u.IgnoreFilter, + IgnoredFields: u.IgnoredFields, returnInputOnNoop: u.ReturnInputOnNoop, } } @@ -56,6 +60,9 @@ type Updater struct { // Deprecated: This will eventually become private. Converter Converter + // Deprecated: This will eventually become private. + IgnoredFields map[fieldpath.APIVersion]*fieldpath.Set + // Deprecated: This will eventually become private. IgnoreFilter map[fieldpath.APIVersion]fieldpath.Filter @@ -70,8 +77,19 @@ func (s *Updater) update(oldObject, newObject *typed.TypedValue, version fieldpa return nil, nil, fmt.Errorf("failed to compare objects: %v", err) } - versions := map[fieldpath.APIVersion]*typed.Comparison{ - version: compare.FilterFields(s.IgnoreFilter[version]), + var versions map[fieldpath.APIVersion]*typed.Comparison + + if s.IgnoredFields != nil && s.IgnoreFilter != nil { + return nil, nil, fmt.Errorf("IgnoreFilter and IgnoreFilter may not both be set") + } + if s.IgnoredFields != nil { + versions = map[fieldpath.APIVersion]*typed.Comparison{ + version: compare.ExcludeFields(s.IgnoredFields[version]), + } + } else { + versions = map[fieldpath.APIVersion]*typed.Comparison{ + version: compare.FilterFields(s.IgnoreFilter[version]), + } } for manager, managerSet := range managers { @@ -101,7 +119,12 @@ func (s *Updater) update(oldObject, newObject *typed.TypedValue, version fieldpa if err != nil { return nil, nil, fmt.Errorf("failed to compare objects: %v", err) } - versions[managerSet.APIVersion()] = compare.FilterFields(s.IgnoreFilter[managerSet.APIVersion()]) + + if s.IgnoredFields != nil { + versions[managerSet.APIVersion()] = compare.ExcludeFields(s.IgnoredFields[managerSet.APIVersion()]) + } else { + versions[managerSet.APIVersion()] = compare.FilterFields(s.IgnoreFilter[managerSet.APIVersion()]) + } } conflictSet := managerSet.Set().Intersection(compare.Modified.Union(compare.Added)) @@ -154,7 +177,16 @@ func (s *Updater) Update(liveObject, newObject *typed.TypedValue, version fieldp managers[manager] = fieldpath.NewVersionedSet(fieldpath.NewSet(), version, false) } set := managers[manager].Set().Difference(compare.Removed).Union(compare.Modified).Union(compare.Added) - ignoreFilter := s.IgnoreFilter[version] + + if s.IgnoredFields != nil && s.IgnoreFilter != nil { + return nil, nil, fmt.Errorf("IgnoreFilter and IgnoreFilter may not both be set") + } + var ignoreFilter fieldpath.Filter + if s.IgnoredFields != nil { + ignoreFilter = fieldpath.NewExcludeSetFilter(s.IgnoredFields[version]) + } else { + ignoreFilter = s.IgnoreFilter[version] + } if ignoreFilter != nil { set = ignoreFilter.Filter(set) } @@ -189,7 +221,15 @@ func (s *Updater) Apply(liveObject, configObject *typed.TypedValue, version fiel return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to get field set: %v", err) } - ignoreFilter := s.IgnoreFilter[version] + if s.IgnoredFields != nil && s.IgnoreFilter != nil { + return nil, nil, fmt.Errorf("IgnoreFilter and IgnoreFilter may not both be set") + } + var ignoreFilter fieldpath.Filter + if s.IgnoredFields != nil { + ignoreFilter = fieldpath.NewExcludeSetFilter(s.IgnoredFields[version]) + } else { + ignoreFilter = s.IgnoreFilter[version] + } if ignoreFilter != nil { set = ignoreFilter.Filter(set) }