Skip to content

Commit

Permalink
Add Nutanix MachinePool API support
Browse files Browse the repository at this point in the history
  • Loading branch information
eliorerz committed Feb 25, 2025
1 parent 5354dca commit bd300f9
Show file tree
Hide file tree
Showing 40 changed files with 4,479 additions and 34 deletions.
5 changes: 4 additions & 1 deletion apis/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ toolchain go1.23.5

require github.com/openshift/custom-resource-status v1.1.3-0.20220503160415-f2fdb4999d87

require sigs.k8s.io/yaml v1.4.0 // indirect
require (
github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)

require (
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
Expand Down
3 changes: 2 additions & 1 deletion apis/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,9 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace h1:9PNP1jnUjRhfmGMlkXHjYPishpcw4jpSt/V/xYY3FMA=
github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
Expand Down
2 changes: 2 additions & 0 deletions apis/vendor/modules.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ github.com/openshift/api/machine/v1beta1
# github.com/openshift/custom-resource-status v1.1.3-0.20220503160415-f2fdb4999d87
## explicit; go 1.12
github.com/openshift/custom-resource-status/conditions/v1
# github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace
## explicit; go 1.12
# github.com/x448/float16 v0.8.4
## explicit; go 1.11
github.com/x448/float16
Expand Down
109 changes: 109 additions & 0 deletions contrib/pkg/createcluster/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os/user"
"path/filepath"
"sort"
"strconv"
"strings"
"time"

Expand All @@ -32,6 +33,7 @@ import (
"github.com/openshift/hive/pkg/gcpclient"
"github.com/openshift/hive/pkg/util/scheme"
installertypes "github.com/openshift/installer/pkg/types"
installernutanix "github.com/openshift/installer/pkg/types/nutanix"
"github.com/openshift/installer/pkg/validate"
)

Expand Down Expand Up @@ -96,6 +98,7 @@ const (
cloudOpenStack = "openstack"
cloudOVirt = "ovirt"
cloudVSphere = "vsphere"
cloudNutanix = "nutanix"

testFailureManifest = `apiVersion: v1
kind: NotARealSecret
Expand All @@ -115,6 +118,7 @@ var (
cloudOpenStack: true,
cloudOVirt: true,
cloudVSphere: true,
cloudNutanix: true,
}
manualCCOModeClouds = map[string]bool{
cloudIBM: true,
Expand Down Expand Up @@ -221,6 +225,16 @@ type Options struct {
IBMAccountID string
IBMInstanceType string

// Nutanix
NutanixCACerts string
NutanixPrismCentralEndpoint string
NutanixPrismCentralPort int32
NutanixPrismElementAddress string
NutanixPrismElementPort int32
NutanixAPIVIP string
NutanixIngressVIP string
NutanixSubnetUUIDs []string

homeDir string
log log.FieldLogger
}
Expand Down Expand Up @@ -367,6 +381,15 @@ OpenShift Installer publishes all the services of the cluster like API server an
flags.StringVar(&opt.VSphereNetwork, "vsphere-network", "", "Name of the network to be used by the cluster")
flags.StringVar(&opt.VSphereCACerts, "vsphere-ca-certs", "", "Path to vSphere CA certificate, multiple CA paths can be : delimited")

// Nutanix
flags.StringVar(&opt.NutanixPrismCentralEndpoint, constants.CliNutanixPcAddressCMD, "", "Domain name or IP address of the Nutanix Prism Central endpoint")
flags.Int32Var(&opt.NutanixPrismCentralPort, constants.CliNutanixPcPortCMD, 0, "Port of the Nutanix Prism Central endpoint")
flags.StringVar(&opt.NutanixPrismElementAddress, constants.CliNutanixPeAddressCMD, "", "Domain name or IP address of the Nutanix Prism Element endpoint")
flags.Int32Var(&opt.NutanixPrismElementPort, constants.CliNutanixPePortCMD, 0, "Port of the Nutanix Prism Element endpoint")
flags.StringVar(&opt.NutanixAPIVIP, constants.CliNutanixApiVipCMD, "", "Virtual IP address for the api endpoint")
flags.StringVar(&opt.NutanixIngressVIP, constants.CliNutanixIngressVipCMD, "", "Virtual IP address for ingress application routing")
flags.StringSliceVar(&opt.NutanixSubnetUUIDs, constants.CliNutanixSubnetUUIDCmd, []string{}, "List of network subnets to be used by the cluster")

// oVirt flags
flags.StringVar(&opt.OvirtClusterID, "ovirt-cluster-id", "", "The oVirt cluster id (uuid) under which all VMs will run")
flags.StringVar(&opt.OvirtStorageDomainID, "ovirt-storage-domain-id", "", "oVirt storage domain id (uuid) under which all VM disk would be created")
Expand Down Expand Up @@ -829,6 +852,92 @@ func (o *Options) GenerateObjects() ([]runtime.Object, error) {
InstanceType: o.IBMInstanceType,
}
builder.CloudBuilder = ibmCloudProvider
case cloudNutanix:
nutanixUsername := os.Getenv(constants.NutanixUsernameEnvVar)
if nutanixUsername == "" {
return nil, fmt.Errorf("no %s env var set, cannot proceed", constants.NutanixUsernameEnvVar)
}
nutanixPassword := os.Getenv(constants.NutanixPasswordEnvVar)
if nutanixPassword == "" {
return nil, fmt.Errorf("no %s env var set, cannot proceed", constants.NutanixPasswordEnvVar)
}

nutanixPrismCentralEndpoint := os.Getenv(constants.NutanixPrismCentralEndpointEnvVar)
if o.NutanixPrismCentralEndpoint != "" {
nutanixPrismCentralEndpoint = o.NutanixPrismCentralEndpoint
}
if nutanixPrismCentralEndpoint == "" {
return nil, fmt.Errorf("must provide --%s or set %s env var", constants.CliNutanixPcAddressCMD, constants.NutanixPrismCentralEndpointEnvVar)
}

nutanixPrismCentralPortVal := os.Getenv(constants.NutanixPrismCentralPortEnvVar)
nutanixPortVal, err := strconv.Atoi(nutanixPrismCentralPortVal)
if err != nil {
return nil, fmt.Errorf("error converting %s port variable to int: %w", nutanixPrismCentralPortVal, err)
}

nutanixPrismCentralPort := int32(nutanixPortVal)
if o.NutanixPrismCentralPort != 0 {
nutanixPrismCentralPort = o.NutanixPrismCentralPort
}
if nutanixPrismCentralPort == 0 {
return nil, fmt.Errorf("must provide --%s or set %s env var", constants.CliNutanixPcPortCMD, constants.NutanixPrismCentralEndpointEnvVar)
}

nutanixPrismElementAddress := os.Getenv(constants.NutanixPrismElementEndpointEnvVar)
if o.NutanixPrismElementAddress != "" {
nutanixPrismElementAddress = o.NutanixPrismElementAddress
}
if nutanixPrismElementAddress == "" {
return nil, fmt.Errorf("must provide --%s or set %s env var", constants.CliNutanixPeAddressCMD, constants.NutanixPrismElementEndpointEnvVar)
}

nutanixPrismElementPortVar := os.Getenv(constants.NutanixPrismCentralPortEnvVar)
nutanixPortVal, err = strconv.Atoi(nutanixPrismElementPortVar)
if err != nil {
return nil, fmt.Errorf("error converting %s port variable to int: %w", nutanixPrismElementPortVar, err)
}

nutanixPrismElementPort := int32(nutanixPortVal)
if o.NutanixPrismElementPort != 0 {
nutanixPrismElementPort = o.NutanixPrismElementPort
}
if nutanixPrismElementPort == 0 {
return nil, fmt.Errorf("must provide --%s or set %s env var", constants.CliNutanixPePortCMD, constants.NutanixPrismElementPortEnvVar)
}

nutanixSubnetUUIds := o.NutanixSubnetUUIDs
if len(nutanixSubnetUUIds) == 0 {
return nil, fmt.Errorf("must provide --%s", constants.CliNutanixSubnetUUIDCmd)
}

nutanixBuilder := &clusterresource.NutanixCloudBuilder{
PrismCentral: installernutanix.PrismCentral{
Endpoint: installernutanix.PrismEndpoint{
Address: nutanixPrismCentralEndpoint,
Port: nutanixPrismCentralPort,
},
Username: nutanixUsername,
Password: nutanixPassword,
},
FailureDomains: []installernutanix.FailureDomain{
{
PrismElement: installernutanix.PrismElement{
Endpoint: installernutanix.PrismEndpoint{
Address: o.NutanixPrismElementAddress,
Port: o.NutanixPrismElementPort,
},
},

SubnetUUIDs: o.NutanixSubnetUUIDs,
StorageContainers: nil,
DataSourceImages: nil,
},
},
APIVIP: o.NutanixAPIVIP,
IngressVIP: o.NutanixIngressVIP,
}
builder.CloudBuilder = nutanixBuilder
}

if o.Internal {
Expand Down
4 changes: 2 additions & 2 deletions contrib/pkg/deprovision/nutanix.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ func NewDeprovisionNutanixCommand() *cobra.Command {
}
flags := cmd.Flags()
flags.StringVar(&opt.logLevel, "loglevel", "info", "log level, one of: debug, info, warn, error, fatal, panic")
flags.StringVar(&opt.endpoint, "nutanix-pc-address", "", "Domain name or IP address of the Nutanix Prism Central endpoint")
flags.StringVar(&opt.port, "nutanix-pc-port", "", "Port of the Nutanix Prism Central endpoint")
flags.StringVar(&opt.endpoint, constants.CliNutanixPcAddressCMD, "", "Domain name or IP address of the Nutanix Prism Central endpoint")
flags.StringVar(&opt.port, constants.CliNutanixPcPortCMD, "", "Port of the Nutanix Prism Central endpoint")
return cmd
}

Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ require (
github.com/yagipy/maintidx v1.0.0 // indirect
github.com/ykadowak/zerologlint v0.1.5 // indirect
gitlab.com/bosi/decorder v0.4.2 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
sigs.k8s.io/cluster-api v1.7.3 // indirect
sigs.k8s.io/cluster-api-provider-aws/v2 v2.6.1 // indirect
sigs.k8s.io/cluster-api-provider-gcp v1.7.1-0.20240724153512-c3b8b533143c // indirect
Expand Down Expand Up @@ -289,6 +289,7 @@ require (
github.com/nakabonne/nestif v0.3.1 // indirect
github.com/nishanths/exhaustive v0.12.0 // indirect
github.com/nishanths/predeclared v0.2.2 // indirect
github.com/nutanix-cloud-native/cluster-api-provider-nutanix v1.3.3-0.20240416171357-98239ba02cb2 // indirect
github.com/nutanix-cloud-native/prism-go-client v0.3.4 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/oklog/ulid v1.3.1 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,8 @@ github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm
github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c=
github.com/nunnatsa/ginkgolinter v0.16.2 h1:8iLqHIZvN4fTLDC0Ke9tbSZVcyVHoBs0HIbnVSxfHJk=
github.com/nunnatsa/ginkgolinter v0.16.2/go.mod h1:4tWRinDN1FeJgU+iJANW/kz7xKN5nYRAOfJDQUS9dOQ=
github.com/nutanix-cloud-native/cluster-api-provider-nutanix v1.3.3-0.20240416171357-98239ba02cb2 h1:0lm7ePdbKnoh/Bbq5TUxDFrZsycpm4FPSGhyuAG9iNs=
github.com/nutanix-cloud-native/cluster-api-provider-nutanix v1.3.3-0.20240416171357-98239ba02cb2/go.mod h1:fccHV2gkPKoA7vcXHUJmF8axdvf4eLGm+aaAE7rLroM=
github.com/nutanix-cloud-native/prism-go-client v0.3.4 h1:bHY3VPrHHYnbRtkpGaKK+2ZmvUjNVRC55CYZbXIfnOk=
github.com/nutanix-cloud-native/prism-go-client v0.3.4/go.mod h1:tTIH02E6o6AWSShr98QChoxuZl+jBhkXFixom9+fd1Y=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
Expand Down
60 changes: 60 additions & 0 deletions pkg/clusterresource/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/ghodss/yaml"
hivev1 "github.com/openshift/hive/apis/hive/v1"
hivev1azure "github.com/openshift/hive/apis/hive/v1/azure"
"github.com/openshift/installer/pkg/types/nutanix"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -155,6 +156,39 @@ func createVSphereClusterBuilder() *Builder {
return b
}

func createNutanixClusterBuilder() *Builder {
b := createTestBuilder()
b.CloudBuilder = &NutanixCloudBuilder{
PrismCentral: nutanix.PrismCentral{
Endpoint: nutanix.PrismEndpoint{
Address: "prism-central.nutanix-test.com",
Port: 9440,
},
Username: "nutanix-user",
Password: "nutanix-password",
},
FailureDomains: []nutanix.FailureDomain{
{
Name: "fd-name",
PrismElement: nutanix.PrismElement{
UUID: "0005de05-75a3-dacb-ba00-2c5da2ac4c1a",
Endpoint: nutanix.PrismEndpoint{
Address: "prism-element.nutanix-test.com",
Port: 9440,
},
Name: "PE-NAME",
},
SubnetUUIDs: []string{"0005de05-75a3-dacb-ba00-123456789012"},
StorageContainers: nil,
DataSourceImages: nil,
},
},
APIVIP: "192.168.0.2",
IngressVIP: "192.168.0.3",
}
return b
}

func TestBuildClusterResources(t *testing.T) {
tests := []struct {
name string
Expand Down Expand Up @@ -259,6 +293,32 @@ func TestBuildClusterResources(t *testing.T) {
assert.Equal(t, certSecret.Name, cd.Spec.Platform.VSphere.CertificatesSecretRef.Name)
},
},
{
name: "Nutanix cluster",
builder: createNutanixClusterBuilder(),
validate: func(t *testing.T, allObjects []runtime.Object) {
cd := findClusterDeployment(allObjects, clusterName)
workerPool := findMachinePool(allObjects, fmt.Sprintf("%s-%s", clusterName, "worker"))

credsSecretName := fmt.Sprintf("%s-nutanix-creds", clusterName)
credsSecret := findSecret(allObjects, credsSecretName)
require.NotNil(t, credsSecret, "Nutanix credentials secret should be generated")
assert.Equal(t, credsSecret.Name, cd.Spec.Platform.Nutanix.CredentialsSecretRef.Name)

// Validate PrismCentral endpoint
assert.Equal(t, "prism-central.nutanix-test.com", cd.Spec.Platform.Nutanix.PrismCentral.Address)
assert.Equal(t, int32(9440), cd.Spec.Platform.Nutanix.PrismCentral.Port)

// Validate Prism Elements
require.NotNil(t, cd.Spec.Platform.Nutanix)
assert.Len(t, cd.Spec.Platform.Nutanix.FailureDomains, 1, "Expected 1 Failure Domain")
assert.Equal(t, "0005de05-75a3-dacb-ba00-2c5da2ac4c1a", cd.Spec.Platform.Nutanix.FailureDomains[0].PrismElement.UUID)

// Validate machine pool
assert.NotNil(t, workerPool)
assert.Equal(t, int64(workerNodeCount), *workerPool.Spec.Replicas)
},
},
{
name: "merge InstallConfigTemplate",
builder: func() *Builder {
Expand Down
Loading

0 comments on commit bd300f9

Please sign in to comment.