From fe114a9a60d80e897999b35c7fb9fb9b9b1206fe Mon Sep 17 00:00:00 2001 From: danmanor Date: Sat, 23 Nov 2024 01:01:26 +0200 Subject: [PATCH 1/6] MGMT-19338: Add DB migration to set control_plane_count value in clusters table for all existing records --- internal/bminventory/inventory.go | 31 ++++- internal/cluster/validator.go | 7 - internal/common/common.go | 29 ----- internal/host/monitor.go | 7 +- ...ount_value_for_existing_cluster_records.go | 52 ++++++++ ...value_for_existing_cluster_records_test.go | 121 ++++++++++++++++++ internal/migrations/migrations.go | 1 + 7 files changed, 205 insertions(+), 43 deletions(-) create mode 100644 internal/migrations/20241122160000_update_new_column_control_plane_count_value_for_existing_cluster_records.go create mode 100644 internal/migrations/20241122160000_update_new_column_control_plane_count_value_for_existing_cluster_records_test.go diff --git a/internal/bminventory/inventory.go b/internal/bminventory/inventory.go index 3f4ba96954ed..d917328e5dc3 100644 --- a/internal/bminventory/inventory.go +++ b/internal/bminventory/inventory.go @@ -334,7 +334,7 @@ func (b *bareMetalInventory) setDefaultRegisterClusterParams(ctx context.Context params.NewClusterParams.SchedulableMasters = swag.Bool(false) } - params.NewClusterParams.HighAvailabilityMode, params.NewClusterParams.ControlPlaneCount = common.GetDefaultHighAvailabilityAndMasterCountParams( + params.NewClusterParams.HighAvailabilityMode, params.NewClusterParams.ControlPlaneCount = getDefaultHighAvailabilityAndMasterCountParams( params.NewClusterParams.HighAvailabilityMode, params.NewClusterParams.ControlPlaneCount, ) @@ -6663,3 +6663,32 @@ func (b *bareMetalInventory) HandleVerifyVipsResponse(ctx context.Context, host } return b.clusterApi.HandleVerifyVipsResponse(ctx, *host.ClusterID, stepReply) } + +func getDefaultHighAvailabilityAndMasterCountParams(highAvailabilityMode *string, controlPlaneCount *int64) (*string, *int64) { + // Both not set, multi node by default + if highAvailabilityMode == nil && controlPlaneCount == nil { + return swag.String(models.ClusterCreateParamsHighAvailabilityModeFull), + swag.Int64(common.MinMasterHostsNeededForInstallationInHaMode) + } + + // only highAvailabilityMode set + if controlPlaneCount == nil { + if *highAvailabilityMode == models.ClusterHighAvailabilityModeNone { + return highAvailabilityMode, swag.Int64(common.AllowedNumberOfMasterHostsInNoneHaMode) + } else { + return highAvailabilityMode, swag.Int64(common.MinMasterHostsNeededForInstallationInHaMode) + } + } + + // only controlPlaneCount set + if highAvailabilityMode == nil { + if *controlPlaneCount == common.AllowedNumberOfMasterHostsInNoneHaMode { + return swag.String(models.ClusterHighAvailabilityModeNone), controlPlaneCount + } else { + return swag.String(models.ClusterHighAvailabilityModeFull), controlPlaneCount + } + } + + // both are set + return highAvailabilityMode, controlPlaneCount +} diff --git a/internal/cluster/validator.go b/internal/cluster/validator.go index 92260add4a87..5b55d1ba77f5 100644 --- a/internal/cluster/validator.go +++ b/internal/cluster/validator.go @@ -315,13 +315,6 @@ func (v *clusterValidator) SufficientMastersCount(c *clusterPreprocessContext) ( message string ) - // Might be the case for already existing records (default value). In this case high availablity mode is set, we will get - // the corresponding control planes count - if c.cluster.ControlPlaneCount == 0 { - _, count := common.GetDefaultHighAvailabilityAndMasterCountParams(c.cluster.HighAvailabilityMode, nil) - c.cluster.ControlPlaneCount = swag.Int64Value(count) - } - masters, workers, autoAssignHosts := common.GetHostsByEachRole(&c.cluster.Cluster, true) for _, h := range autoAssignHosts { //if allocated masters count is less than the desired count, find eligible hosts diff --git a/internal/common/common.go b/internal/common/common.go index de34e378ddec..42edf79051de 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -688,35 +688,6 @@ func ShouldMastersBeSchedulable(cluster *models.Cluster) bool { return len(workers) < MinimumNumberOfWorkersForNonSchedulableMastersClusterInHaMode } -func GetDefaultHighAvailabilityAndMasterCountParams(highAvailabilityMode *string, controlPlaneCount *int64) (*string, *int64) { - // Both not set, multi node by default - if highAvailabilityMode == nil && controlPlaneCount == nil { - return swag.String(models.ClusterCreateParamsHighAvailabilityModeFull), - swag.Int64(MinMasterHostsNeededForInstallationInHaMode) - } - - // only highAvailabilityMode set - if controlPlaneCount == nil { - if *highAvailabilityMode == models.ClusterHighAvailabilityModeNone { - return highAvailabilityMode, swag.Int64(AllowedNumberOfMasterHostsInNoneHaMode) - } else { - return highAvailabilityMode, swag.Int64(MinMasterHostsNeededForInstallationInHaMode) - } - } - - // only controlPlaneCount set - if highAvailabilityMode == nil { - if *controlPlaneCount == AllowedNumberOfMasterHostsInNoneHaMode { - return swag.String(models.ClusterHighAvailabilityModeNone), controlPlaneCount - } else { - return swag.String(models.ClusterHighAvailabilityModeFull), controlPlaneCount - } - } - - // both are set - return highAvailabilityMode, controlPlaneCount -} - func IsMirrorConfigurationSet(conf *MirrorRegistryConfiguration) bool { if conf == nil { return false diff --git a/internal/host/monitor.go b/internal/host/monitor.go index 767a3d0c0a37..74e515132d53 100644 --- a/internal/host/monitor.go +++ b/internal/host/monitor.go @@ -152,11 +152,6 @@ func (m *Manager) clusterHostMonitoring() int64 { } for _, c := range clusters { - expectedMasterCount := c.ControlPlaneCount - if c.ControlPlaneCount == 0 { - expectedMasterCount = common.MinMasterHostsNeededForInstallationInHaMode - } - inventoryCache := make(InventoryCache) sortedHosts, canRefreshRoles := SortHosts(c.Hosts) @@ -176,7 +171,7 @@ func (m *Manager) clusterHostMonitoring() int64 { //all the hosts in the cluster has inventory to avoid race condition //with the reset auto-assign mechanism. if canRefreshRoles { - err = m.refreshRoleInternal(ctx, host, m.db, false, swag.Int(int(expectedMasterCount))) + err = m.refreshRoleInternal(ctx, host, m.db, false, swag.Int(int(c.ControlPlaneCount))) if err != nil { log.WithError(err).Errorf("failed to refresh host %s role", *host.ID) } diff --git a/internal/migrations/20241122160000_update_new_column_control_plane_count_value_for_existing_cluster_records.go b/internal/migrations/20241122160000_update_new_column_control_plane_count_value_for_existing_cluster_records.go new file mode 100644 index 000000000000..a663eb24223d --- /dev/null +++ b/internal/migrations/20241122160000_update_new_column_control_plane_count_value_for_existing_cluster_records.go @@ -0,0 +1,52 @@ +package migrations + +import ( + gormigrate "github.com/go-gormigrate/gormigrate/v2" + "github.com/openshift/assisted-service/internal/common" + "github.com/openshift/assisted-service/models" + "github.com/pkg/errors" + "gorm.io/gorm" +) + +// split to 2 migrations, one for each query. + +func updateNewColumnControlPlaneCountValueForExistingClusterRecords() *gormigrate.Migration { + migrate := func(tx *gorm.DB) error { + transaction := tx.Begin() + + cleanQuery := tx.Session(&gorm.Session{NewDB: true}) + err := transaction.Model(&common.Cluster{}). + // control_plane_count value in existing records can be NULL or 0 (default). We want to set the value in both cases + Where(cleanQuery.Where("control_plane_count IS NULL").Or("control_plane_count = ?", 0)). + Where("high_availability_mode = ?", models.ClusterCreateParamsHighAvailabilityModeNone). + Update("control_plane_count", "1").Error + if err != nil { + transaction.Rollback() + return errors.Wrap(err, "failed to update control_plane_count value of existing SNO clusters to 1") + } + + cleanQuery = tx.Session(&gorm.Session{NewDB: true}) + err = transaction.Model(&common.Cluster{}). + // control_plane_count value in existing records can be NULL or 0 (default). We want to set the value in both cases + Where(cleanQuery.Where("control_plane_count IS NULL").Or("control_plane_count = ?", 0)). + Where("high_availability_mode = ?", models.ClusterCreateParamsHighAvailabilityModeFull). + Update("control_plane_count", "3").Error + if err != nil { + transaction.Rollback() + return errors.Wrap(err, "failed to update control_plane_count value of existing multi-node clusters to 3") + } + + return transaction.Commit().Error + } + + rollback := func(tx *gorm.DB) error { + // No rollback as we can't roll back only the modified records + return nil + } + + return &gormigrate.Migration{ + ID: "20241122160000", + Migrate: gormigrate.MigrateFunc(migrate), + Rollback: gormigrate.RollbackFunc(rollback), + } +} diff --git a/internal/migrations/20241122160000_update_new_column_control_plane_count_value_for_existing_cluster_records_test.go b/internal/migrations/20241122160000_update_new_column_control_plane_count_value_for_existing_cluster_records_test.go new file mode 100644 index 000000000000..d4fc856a8657 --- /dev/null +++ b/internal/migrations/20241122160000_update_new_column_control_plane_count_value_for_existing_cluster_records_test.go @@ -0,0 +1,121 @@ +package migrations + +import ( + gormigrate "github.com/go-gormigrate/gormigrate/v2" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/google/uuid" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/openshift/assisted-service/internal/common" + "github.com/openshift/assisted-service/models" + "gorm.io/gorm" +) + +var ( + id1 = strfmt.UUID(uuid.New().String()) + id2 = strfmt.UUID(uuid.New().String()) + id3 = strfmt.UUID(uuid.New().String()) + id4 = strfmt.UUID(uuid.New().String()) +) + +var _ = Describe("updateNewColumnControlPlaneCountValueForExistingSNOClusterRecords", func() { + var ( + db *gorm.DB + dbName string + migration *gormigrate.Migration = updateNewColumnControlPlaneCountValueForExistingClusterRecords() + ) + + BeforeEach(func() { + db, dbName = common.PrepareTestDB() + }) + + AfterEach(func() { + common.DeleteTestDB(db, dbName) + }) + + Context("succeeds", func() { + It("changing only records with control_plane_count = 0", func() { + clusters := []*common.Cluster{ + { + ControlPlaneCount: 0, + Cluster: models.Cluster{ + ID: &id1, + HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeNone), + }, + }, + { + ControlPlaneCount: 1, + Cluster: models.Cluster{ + ID: &id2, + HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeNone), + }, + }, + { + ControlPlaneCount: 0, + Cluster: models.Cluster{ + ID: &id3, + HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeFull), + }, + }, + { + ControlPlaneCount: 3, + Cluster: models.Cluster{ + ID: &id4, + HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeFull), + }, + }, + } + + Expect(db.Create(&clusters).Error).To(Succeed()) + Expect(migrateToBefore(db, migration.ID)).To(Succeed()) + Expect(migrateTo(db, migration.ID)).To(Succeed()) + + var count int64 + + Expect(db.Model(&common.Cluster{}).Where("control_plane_count = ?", 0).Count(&count).Error).To(Succeed()) + Expect(count).To(BeEquivalentTo(0)) + + Expect(db.Model(&common.Cluster{}).Where("control_plane_count = ?", 1).Count(&count).Error).To(Succeed()) + Expect(count).To(BeEquivalentTo(2)) + + Expect(db.Model(&common.Cluster{}).Where("control_plane_count = ?", 3).Count(&count).Error).To(Succeed()) + Expect(count).To(BeEquivalentTo(2)) + }) + }) + + It("changing only records with control_plane_count = NULL", func() { + Expect( + db.Exec( + "INSERT INTO clusters (id, control_plane_count, high_availability_mode) VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?), (?, ?, ?)", + id1, nil, models.ClusterCreateParamsHighAvailabilityModeNone, + id2, 1, models.ClusterCreateParamsHighAvailabilityModeNone, + id3, 3, models.ClusterCreateParamsHighAvailabilityModeFull, + id4, nil, models.ClusterCreateParamsHighAvailabilityModeFull, + ).Error, + ).To(Succeed()) + Expect(migrateToBefore(db, migration.ID)).To(Succeed()) + Expect(migrateTo(db, migration.ID)).To(Succeed()) + + var count int64 + + Expect(db.Model(&common.Cluster{}).Where("control_plane_count IS NULL").Count(&count).Error).To(Succeed()) + Expect(count).To(BeEquivalentTo(0)) + + Expect(db.Model(&common.Cluster{}).Where("control_plane_count = ?", 1).Count(&count).Error).To(Succeed()) + Expect(count).To(BeEquivalentTo(2)) + + Expect(db.Model(&common.Cluster{}).Where("control_plane_count = ?", 3).Count(&count).Error).To(Succeed()) + Expect(count).To(BeEquivalentTo(2)) + }) + + It("with no existing records", func() { + Expect(migrateToBefore(db, migration.ID)).To(Succeed()) + Expect(migrateTo(db, migration.ID)).To(Succeed()) + + var count int64 + + Expect(db.Model(&common.Cluster{}).Count(&count).Error).To(Succeed()) + Expect(count).To(BeEquivalentTo(0)) + }) +}) diff --git a/internal/migrations/migrations.go b/internal/migrations/migrations.go index 3c2e5f8d49cd..610b5a4a1ca6 100644 --- a/internal/migrations/migrations.go +++ b/internal/migrations/migrations.go @@ -45,6 +45,7 @@ func post() []*gormigrate.Migration { dropClusterApiVipAndIngressVip(), updateOciToExternalPlatformType(), dropClusterPlatformIsExternal(), + updateNewColumnControlPlaneCountValueForExistingClusterRecords(), } sort.SliceStable(postMigrations, func(i, j int) bool { return postMigrations[i].ID < postMigrations[j].ID }) From f1484abdbb107580022cb6ff2dd522edf184e1fc Mon Sep 17 00:00:00 2001 From: danmanor Date: Sat, 23 Nov 2024 11:12:38 +0200 Subject: [PATCH 2/6] MGMT-19080, MGMT-18590: Fix typos --- internal/common/db.go | 2 +- .../controller/controllers/clusterdeployments_controller.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/common/db.go b/internal/common/db.go index 959562656a69..b71fc98f4a27 100644 --- a/internal/common/db.go +++ b/internal/common/db.go @@ -85,7 +85,7 @@ type Cluster struct { // A JSON blob in which cluster UI settings will be stored. UISettings string `json:"ui_settings"` - // The amount of control planes which should be part of the cluster in high availability 'Full' mode. + // The amount of control planes which should be part of the cluster. ControlPlaneCount int64 `json:"control_plane_count"` // A JSON blob in which holds the cluster mirror registry if set diff --git a/internal/controller/controllers/clusterdeployments_controller.go b/internal/controller/controllers/clusterdeployments_controller.go index 6928b28d8f2a..c5d726099890 100644 --- a/internal/controller/controllers/clusterdeployments_controller.go +++ b/internal/controller/controllers/clusterdeployments_controller.go @@ -709,7 +709,7 @@ func (r *ClusterDeploymentsReconciler) isReadyForInstallation( expectedWorkerCount := clusterInstall.Spec.ProvisionRequirements.WorkerAgents expectedHostCount := expectedMasterCount + expectedWorkerCount - masterCountPrt, workerCountPtr, err := getHostSuggestedRoleCount(r.ClusterApi, *c.ID) + masterCountPtr, workerCountPtr, err := getHostSuggestedRoleCount(r.ClusterApi, *c.ID) if err != nil { // will be shown as a SpecSynced error log.WithError(err).Error("failed to fetch host suggested role count") @@ -718,7 +718,7 @@ func (r *ClusterDeploymentsReconciler) isReadyForInstallation( return approvedHosts == expectedHostCount && registered == expectedHostCount && - int(swag.Int64Value(masterCountPrt)) == expectedMasterCount && + int(swag.Int64Value(masterCountPtr)) == expectedMasterCount && int(swag.Int64Value(workerCountPtr)) == expectedWorkerCount && unsyncedHosts == 0, nil } From 2161f6f1661fbaaa8ae9f0fa2b3977b1d2935dcd Mon Sep 17 00:00:00 2001 From: danmanor Date: Sat, 23 Nov 2024 11:19:49 +0200 Subject: [PATCH 3/6] NO-ISSUE: start from clean query while counting host roles --- internal/common/db.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/internal/common/db.go b/internal/common/db.go index b71fc98f4a27..0f684c1b89b2 100644 --- a/internal/common/db.go +++ b/internal/common/db.go @@ -364,6 +364,9 @@ func GetClusterFromDBWithVips(db *gorm.DB, clusterId strfmt.UUID) (*Cluster, err } func GetHostCountByRole(db *gorm.DB, clusterID strfmt.UUID, role models.HostRole, suggested bool) (*int64, error) { + // Start from empty query + cleanQuery := db.Session(&gorm.Session{NewDB: true}) + var count int64 field := "role" @@ -374,7 +377,7 @@ func GetHostCountByRole(db *gorm.DB, clusterID strfmt.UUID, role models.HostRole } condition := fmt.Sprintf("hosts.%s = ?", field) - err := db.Model(&Host{}). + err := cleanQuery.Model(&Host{}). Joins("INNER JOIN clusters ON hosts.cluster_id = clusters.id"). Where("clusters.id = ?", clusterID.String()). Where(condition, string(role)). From f377676e07ea5ad2190ad20a323f5cca18101e33 Mon Sep 17 00:00:00 2001 From: danmanor Date: Sun, 24 Nov 2024 15:53:08 +0200 Subject: [PATCH 4/6] NO-ISSUE: Add feature support mechanism for stretched clusters --- .../models/feature_support_level_id.go | 5 +- .../models/feature_support_level_id.go | 5 +- internal/cluster/cluster_test.go | 58 ++++---- internal/cluster/transition_test.go | 61 ++++++-- internal/cluster/validator_test.go | 133 +++--------------- .../featuresupport/feature_support_level.go | 1 + .../featuresupport/feature_support_test.go | 62 +++++++- internal/featuresupport/features_misc.go | 54 +++++++ .../featuresupport/features_olm_operators.go | 1 + internal/featuresupport/features_platforms.go | 5 + internal/operators/odf/validation_test.go | 1 + models/feature_support_level_id.go | 5 +- restapi/embedded_spec.go | 6 +- swagger.yaml | 1 + .../api/common/common_types.go | 1 - .../models/feature_support_level_id.go | 5 +- 16 files changed, 241 insertions(+), 163 deletions(-) diff --git a/api/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go b/api/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go index 2a7d4ad667ff..ae187e498d3c 100644 --- a/api/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go +++ b/api/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go @@ -125,6 +125,9 @@ const ( // FeatureSupportLevelIDOPENSHIFTAI captures enum value "OPENSHIFT_AI" FeatureSupportLevelIDOPENSHIFTAI FeatureSupportLevelID = "OPENSHIFT_AI" + + // FeatureSupportLevelIDSTRETCHEDCLUSTERS captures enum value "STRETCHED_CLUSTERS" + FeatureSupportLevelIDSTRETCHEDCLUSTERS FeatureSupportLevelID = "STRETCHED_CLUSTERS" ) // for schema @@ -132,7 +135,7 @@ var featureSupportLevelIdEnum []interface{} func init() { var res []FeatureSupportLevelID - if err := json.Unmarshal([]byte(`["SNO","VIP_AUTO_ALLOC","CUSTOM_MANIFEST","SINGLE_NODE_EXPANSION","LVM","ODF","LSO","CNV","MCE","MTV","NUTANIX_INTEGRATION","BAREMETAL_PLATFORM","NONE_PLATFORM","VSPHERE_INTEGRATION","DUAL_STACK_VIPS","CLUSTER_MANAGED_NETWORKING","USER_MANAGED_NETWORKING","MINIMAL_ISO","FULL_ISO","EXTERNAL_PLATFORM_OCI","DUAL_STACK","PLATFORM_MANAGED_NETWORKING","SKIP_MCO_REBOOT","EXTERNAL_PLATFORM","OVN_NETWORK_TYPE","SDN_NETWORK_TYPE","NODE_FEATURE_DISCOVERY","NVIDIA_GPU","PIPELINES","SERVICEMESH","SERVERLESS","OPENSHIFT_AI"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["SNO","VIP_AUTO_ALLOC","CUSTOM_MANIFEST","SINGLE_NODE_EXPANSION","LVM","ODF","LSO","CNV","MCE","MTV","NUTANIX_INTEGRATION","BAREMETAL_PLATFORM","NONE_PLATFORM","VSPHERE_INTEGRATION","DUAL_STACK_VIPS","CLUSTER_MANAGED_NETWORKING","USER_MANAGED_NETWORKING","MINIMAL_ISO","FULL_ISO","EXTERNAL_PLATFORM_OCI","DUAL_STACK","PLATFORM_MANAGED_NETWORKING","SKIP_MCO_REBOOT","EXTERNAL_PLATFORM","OVN_NETWORK_TYPE","SDN_NETWORK_TYPE","NODE_FEATURE_DISCOVERY","NVIDIA_GPU","PIPELINES","SERVICEMESH","SERVERLESS","OPENSHIFT_AI","STRETCHED_CLUSTERS"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/client/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go b/client/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go index 2a7d4ad667ff..ae187e498d3c 100644 --- a/client/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go +++ b/client/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go @@ -125,6 +125,9 @@ const ( // FeatureSupportLevelIDOPENSHIFTAI captures enum value "OPENSHIFT_AI" FeatureSupportLevelIDOPENSHIFTAI FeatureSupportLevelID = "OPENSHIFT_AI" + + // FeatureSupportLevelIDSTRETCHEDCLUSTERS captures enum value "STRETCHED_CLUSTERS" + FeatureSupportLevelIDSTRETCHEDCLUSTERS FeatureSupportLevelID = "STRETCHED_CLUSTERS" ) // for schema @@ -132,7 +135,7 @@ var featureSupportLevelIdEnum []interface{} func init() { var res []FeatureSupportLevelID - if err := json.Unmarshal([]byte(`["SNO","VIP_AUTO_ALLOC","CUSTOM_MANIFEST","SINGLE_NODE_EXPANSION","LVM","ODF","LSO","CNV","MCE","MTV","NUTANIX_INTEGRATION","BAREMETAL_PLATFORM","NONE_PLATFORM","VSPHERE_INTEGRATION","DUAL_STACK_VIPS","CLUSTER_MANAGED_NETWORKING","USER_MANAGED_NETWORKING","MINIMAL_ISO","FULL_ISO","EXTERNAL_PLATFORM_OCI","DUAL_STACK","PLATFORM_MANAGED_NETWORKING","SKIP_MCO_REBOOT","EXTERNAL_PLATFORM","OVN_NETWORK_TYPE","SDN_NETWORK_TYPE","NODE_FEATURE_DISCOVERY","NVIDIA_GPU","PIPELINES","SERVICEMESH","SERVERLESS","OPENSHIFT_AI"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["SNO","VIP_AUTO_ALLOC","CUSTOM_MANIFEST","SINGLE_NODE_EXPANSION","LVM","ODF","LSO","CNV","MCE","MTV","NUTANIX_INTEGRATION","BAREMETAL_PLATFORM","NONE_PLATFORM","VSPHERE_INTEGRATION","DUAL_STACK_VIPS","CLUSTER_MANAGED_NETWORKING","USER_MANAGED_NETWORKING","MINIMAL_ISO","FULL_ISO","EXTERNAL_PLATFORM_OCI","DUAL_STACK","PLATFORM_MANAGED_NETWORKING","SKIP_MCO_REBOOT","EXTERNAL_PLATFORM","OVN_NETWORK_TYPE","SDN_NETWORK_TYPE","NODE_FEATURE_DISCOVERY","NVIDIA_GPU","PIPELINES","SERVICEMESH","SERVERLESS","OPENSHIFT_AI","STRETCHED_CLUSTERS"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/internal/cluster/cluster_test.go b/internal/cluster/cluster_test.go index 18f9fb0bc946..217fac6e1fcf 100644 --- a/internal/cluster/cluster_test.go +++ b/internal/cluster/cluster_test.go @@ -456,6 +456,7 @@ var _ = Describe("TestClusterMonitoring", func() { BeforeEach(func() { c = common.Cluster{ + ControlPlaneCount: 3, Cluster: models.Cluster{ ID: &id, Status: swag.String("insufficient"), @@ -544,6 +545,7 @@ var _ = Describe("TestClusterMonitoring", func() { Context("from ready state", func() { BeforeEach(func() { c = common.Cluster{ + ControlPlaneCount: 3, Cluster: models.Cluster{ ID: &id, Status: swag.String(models.ClusterStatusReady), @@ -2253,19 +2255,21 @@ var _ = Describe("Majority groups", func() { apiVip := "1.2.3.5" ingressVip := "1.2.3.6" verificationSuccess := models.VipVerificationSucceeded - cluster = common.Cluster{Cluster: models.Cluster{ - ID: &id, - Status: swag.String(models.ClusterStatusReady), - ClusterNetworks: common.TestIPv4Networking.ClusterNetworks, - ServiceNetworks: common.TestIPv4Networking.ServiceNetworks, - MachineNetworks: common.TestIPv4Networking.MachineNetworks, - APIVips: []*models.APIVip{{IP: models.IP(apiVip), ClusterID: id, Verification: &verificationSuccess}}, - IngressVips: []*models.IngressVip{{IP: models.IP(ingressVip), ClusterID: id, Verification: &verificationSuccess}}, - BaseDNSDomain: "test.com", - PullSecretSet: true, - NetworkType: swag.String(models.ClusterNetworkTypeOVNKubernetes), - OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, - }} + cluster = common.Cluster{ + ControlPlaneCount: 3, + Cluster: models.Cluster{ + ID: &id, + Status: swag.String(models.ClusterStatusReady), + ClusterNetworks: common.TestIPv4Networking.ClusterNetworks, + ServiceNetworks: common.TestIPv4Networking.ServiceNetworks, + MachineNetworks: common.TestIPv4Networking.MachineNetworks, + APIVips: []*models.APIVip{{IP: models.IP(apiVip), ClusterID: id, Verification: &verificationSuccess}}, + IngressVips: []*models.IngressVip{{IP: models.IP(ingressVip), ClusterID: id, Verification: &verificationSuccess}}, + BaseDNSDomain: "test.com", + PullSecretSet: true, + NetworkType: swag.String(models.ClusterNetworkTypeOVNKubernetes), + OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, + }} Expect(db.Create(&cluster).Error).ShouldNot(HaveOccurred()) mockMetricApi.EXPECT().MonitoredClusterCount(int64(1)).AnyTimes() @@ -2572,19 +2576,21 @@ var _ = Describe("ready_state", func() { id = strfmt.UUID(uuid.New().String()) apiVip := "1.2.3.5" ingressVip := "1.2.3.6" - cluster = common.Cluster{Cluster: models.Cluster{ - ID: &id, - Status: swag.String(models.ClusterStatusReady), - ClusterNetworks: common.TestIPv4Networking.ClusterNetworks, - ServiceNetworks: common.TestIPv4Networking.ServiceNetworks, - MachineNetworks: common.TestIPv4Networking.MachineNetworks, - APIVips: []*models.APIVip{{IP: models.IP(apiVip), ClusterID: id, Verification: common.VipVerificationPtr(models.VipVerificationSucceeded)}}, - IngressVips: []*models.IngressVip{{IP: models.IP(ingressVip), ClusterID: id, Verification: common.VipVerificationPtr(models.VipVerificationSucceeded)}}, - BaseDNSDomain: "test.com", - PullSecretSet: true, - NetworkType: swag.String(models.ClusterNetworkTypeOVNKubernetes), - OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, - }} + cluster = common.Cluster{ + ControlPlaneCount: 3, + Cluster: models.Cluster{ + ID: &id, + Status: swag.String(models.ClusterStatusReady), + ClusterNetworks: common.TestIPv4Networking.ClusterNetworks, + ServiceNetworks: common.TestIPv4Networking.ServiceNetworks, + MachineNetworks: common.TestIPv4Networking.MachineNetworks, + APIVips: []*models.APIVip{{IP: models.IP(apiVip), ClusterID: id, Verification: common.VipVerificationPtr(models.VipVerificationSucceeded)}}, + IngressVips: []*models.IngressVip{{IP: models.IP(ingressVip), ClusterID: id, Verification: common.VipVerificationPtr(models.VipVerificationSucceeded)}}, + BaseDNSDomain: "test.com", + PullSecretSet: true, + NetworkType: swag.String(models.ClusterNetworkTypeOVNKubernetes), + OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, + }} Expect(db.Create(&cluster).Error).ShouldNot(HaveOccurred()) addInstallationRequirements(id, db) diff --git a/internal/cluster/transition_test.go b/internal/cluster/transition_test.go index b48b7693cdf8..d8dfd27f1010 100644 --- a/internal/cluster/transition_test.go +++ b/internal/cluster/transition_test.go @@ -1069,10 +1069,11 @@ var _ = Describe("Refresh Cluster - No DHCP", func() { IsDNSDomainDefined: {status: ValidationSuccess, messagePattern: "The base domain is defined"}, IsPullSecretSet: {status: ValidationSuccess, messagePattern: "The pull secret is set"}, isNetworkTypeValid: {status: ValidationSuccess, messagePattern: "The cluster has a valid network type"}, - SufficientMastersCount: {status: ValidationFailure, messagePattern: fmt.Sprintf("The cluster must have exactly %d dedicated control plane nodes. Add or remove hosts, or change their roles configurations to meet the requirement.", common.AllowedNumberOfMasterHostsForInstallationInHaModeOfOCP417OrOlder)}, + SufficientMastersCount: {status: ValidationFailure, messagePattern: "The cluster must have exactly 5 dedicated control plane nodes. Add or remove hosts, or change their roles configurations to meet the requirement."}, }), - errorExpected: false, - openshiftVersion: common.MinimumVersionForStretchedControlPlanesCluster, + errorExpected: false, + openshiftVersion: common.MinimumVersionForStretchedControlPlanesCluster, + controlPlaneCount: 5, }, { name: "pending-for-input to insufficient, not enough masters - stretched masters cluster available", @@ -1086,6 +1087,7 @@ var _ = Describe("Refresh Cluster - No DHCP", func() { hosts: []models.Host{ {ID: &hid1, Status: swag.String(models.HostStatusKnown), Inventory: common.GenerateTestDefaultInventory(), Role: models.HostRoleMaster}, {ID: &hid2, Status: swag.String(models.HostStatusKnown), Inventory: common.GenerateTestDefaultInventory(), Role: models.HostRoleMaster}, + {ID: &hid3, Status: swag.String(models.HostStatusKnown), Inventory: common.GenerateTestDefaultInventory(), Role: models.HostRoleMaster}, }, statusInfoChecker: makeValueChecker(StatusInfoInsufficient), validationsChecker: makeJsonChecker(map[ValidationID]validationCheckResult{ @@ -1099,10 +1101,11 @@ var _ = Describe("Refresh Cluster - No DHCP", func() { IsDNSDomainDefined: {status: ValidationSuccess, messagePattern: "The base domain is defined"}, IsPullSecretSet: {status: ValidationSuccess, messagePattern: "The pull secret is set"}, isNetworkTypeValid: {status: ValidationSuccess, messagePattern: "The cluster has a valid network type"}, - SufficientMastersCount: {status: ValidationFailure, messagePattern: fmt.Sprintf("The cluster must have exactly %d dedicated control plane nodes. Add or remove hosts, or change their roles configurations to meet the requirement.", common.AllowedNumberOfMasterHostsForInstallationInHaModeOfOCP417OrOlder)}, + SufficientMastersCount: {status: ValidationFailure, messagePattern: "The cluster must have exactly 4 dedicated control plane nodes. Add or remove hosts, or change their roles configurations to meet the requirement."}, }), - errorExpected: false, - openshiftVersion: common.MinimumVersionForStretchedControlPlanesCluster, + errorExpected: false, + openshiftVersion: common.MinimumVersionForStretchedControlPlanesCluster, + controlPlaneCount: 4, }, { name: "pending-for-input to ready, sufficient amount of potential masters - stretched masters cluster not available", @@ -1531,6 +1534,10 @@ var _ = Describe("Refresh Cluster - No DHCP", func() { cluster.Cluster.OpenshiftVersion = testing.ValidOCPVersionForNonStretchedClusters } + if cluster.ControlPlaneCount == 0 { + cluster.ControlPlaneCount = 3 + } + Expect(db.Create(&cluster).Error).ShouldNot(HaveOccurred()) for i := range t.hosts { t.hosts[i].InfraEnvID = clusterId @@ -1786,6 +1793,7 @@ var _ = Describe("Refresh Cluster - Same networks", func() { t := tests[i] It(t.name, func() { cluster = common.Cluster{ + ControlPlaneCount: 3, Cluster: models.Cluster{ APIVips: t.apiVips, ID: &clusterId, @@ -2037,6 +2045,7 @@ var _ = Describe("RefreshCluster - preparing for install", func() { t := tests[i] It(t.name, func() { cluster = common.Cluster{ + ControlPlaneCount: 3, Cluster: models.Cluster{ APIVips: t.apiVips, ID: &clusterId, @@ -2146,6 +2155,7 @@ var _ = Describe("Refresh Cluster - Advanced networking validations", func() { vipDhcpAllocation bool networkType string sno bool + controlPlaneCount int64 }{ { name: "pending-for-input to pending-for-input", @@ -2289,7 +2299,8 @@ var _ = Describe("Refresh Cluster - Advanced networking validations", func() { networkPrefixValid: {status: ValidationFailure, messagePattern: "Host prefix, now 0, must be a positive integer"}, isNetworkTypeValid: {status: ValidationSuccess, messagePattern: "The cluster has a valid network type"}, }), - errorExpected: false, + errorExpected: false, + controlPlaneCount: 1, }, { name: "pending-for-input to insufficient - overlapping", @@ -2573,7 +2584,8 @@ var _ = Describe("Refresh Cluster - Advanced networking validations", func() { networkPrefixValid: {status: ValidationSuccess, messagePattern: "Cluster Network prefix is valid."}, isNetworkTypeValid: {status: ValidationFailure, messagePattern: regexp.QuoteMeta("High-availability mode 'None' (SNO) is not supported by OpenShiftSDN; use another network type instead")}, }), - errorExpected: false, + errorExpected: false, + controlPlaneCount: 1, }, } @@ -2581,6 +2593,7 @@ var _ = Describe("Refresh Cluster - Advanced networking validations", func() { t := tests[i] It(t.name, func() { cluster = common.Cluster{ + ControlPlaneCount: t.controlPlaneCount, Cluster: models.Cluster{ APIVips: t.apiVips, ID: &clusterId, @@ -2598,6 +2611,11 @@ var _ = Describe("Refresh Cluster - Advanced networking validations", func() { OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, }, } + + if cluster.ControlPlaneCount == 0 { + cluster.ControlPlaneCount = 3 + } + if t.sno { ha := models.ClusterHighAvailabilityModeNone cluster.HighAvailabilityMode = &ha @@ -3050,6 +3068,7 @@ var _ = Describe("Refresh Cluster - Advanced networking validations", func() { t := tests[i] It(t.name, func() { cluster = common.Cluster{ + ControlPlaneCount: 3, Cluster: models.Cluster{ APIVips: t.apiVips, ID: &clusterId, @@ -3186,6 +3205,7 @@ var _ = Describe("Refresh Cluster - With DHCP", func() { vipDhcpAllocation bool errorExpected bool openshiftVersion string + controlPlaneCount int64 }{ { name: "pending-for-input to pending-for-input", @@ -3315,10 +3335,11 @@ var _ = Describe("Refresh Cluster - With DHCP", func() { AllHostsAreReadyToInstall: {status: ValidationSuccess, messagePattern: "All hosts in the cluster are ready to install"}, IsDNSDomainDefined: {status: ValidationSuccess, messagePattern: "The base domain is defined"}, IsPullSecretSet: {status: ValidationSuccess, messagePattern: "The pull secret is set."}, - SufficientMastersCount: {status: ValidationFailure, messagePattern: fmt.Sprintf("The cluster must have exactly %d dedicated control plane nodes. Add or remove hosts, or change their roles configurations to meet the requirement.", common.AllowedNumberOfMasterHostsForInstallationInHaModeOfOCP417OrOlder)}, + SufficientMastersCount: {status: ValidationFailure, messagePattern: "The cluster must have exactly 5 dedicated control plane nodes. Add or remove hosts, or change their roles configurations to meet the requirement."}, }), - errorExpected: false, - openshiftVersion: common.MinimumVersionForStretchedControlPlanesCluster, + errorExpected: false, + openshiftVersion: common.MinimumVersionForStretchedControlPlanesCluster, + controlPlaneCount: 5, }, { name: "pending-for-input to insufficient, not enough masters - stretched masters cluster available", @@ -3332,6 +3353,7 @@ var _ = Describe("Refresh Cluster - With DHCP", func() { hosts: []models.Host{ {ID: &hid1, Status: swag.String(models.HostStatusKnown), Inventory: common.GenerateTestDefaultInventory(), Role: models.HostRoleMaster}, {ID: &hid2, Status: swag.String(models.HostStatusKnown), Inventory: common.GenerateTestDefaultInventory(), Role: models.HostRoleMaster}, + {ID: &hid3, Status: swag.String(models.HostStatusKnown), Inventory: common.GenerateTestDefaultInventory(), Role: models.HostRoleMaster}, }, statusInfoChecker: makeValueChecker(StatusInfoInsufficient), validationsChecker: makeJsonChecker(map[ValidationID]validationCheckResult{ @@ -3344,10 +3366,11 @@ var _ = Describe("Refresh Cluster - With DHCP", func() { AllHostsAreReadyToInstall: {status: ValidationSuccess, messagePattern: "All hosts in the cluster are ready to install"}, IsDNSDomainDefined: {status: ValidationSuccess, messagePattern: "The base domain is defined"}, IsPullSecretSet: {status: ValidationSuccess, messagePattern: "The pull secret is set."}, - SufficientMastersCount: {status: ValidationFailure, messagePattern: fmt.Sprintf("The cluster must have exactly %d dedicated control plane nodes. Add or remove hosts, or change their roles configurations to meet the requirement.", common.AllowedNumberOfMasterHostsForInstallationInHaModeOfOCP417OrOlder)}, + SufficientMastersCount: {status: ValidationFailure, messagePattern: "The cluster must have exactly 4 dedicated control plane nodes. Add or remove hosts, or change their roles configurations to meet the requirement."}, }), - errorExpected: false, - openshiftVersion: common.MinimumVersionForStretchedControlPlanesCluster, + errorExpected: false, + openshiftVersion: common.MinimumVersionForStretchedControlPlanesCluster, + controlPlaneCount: 4, }, { name: "pending-for-input to insufficient - not all hosts are ready to install - not enough workers", @@ -3752,6 +3775,7 @@ var _ = Describe("Refresh Cluster - With DHCP", func() { t := tests[i] It(t.name, func() { cluster = common.Cluster{ + ControlPlaneCount: t.controlPlaneCount, Cluster: models.Cluster{ APIVips: t.apiVips, ID: &clusterId, @@ -3774,6 +3798,10 @@ var _ = Describe("Refresh Cluster - With DHCP", func() { cluster.Cluster.OpenshiftVersion = testing.ValidOCPVersionForNonStretchedClusters } + if cluster.ControlPlaneCount == 0 { + cluster.ControlPlaneCount = 3 + } + if t.setMachineCidrUpdatedAt { cluster.MachineNetworkCidrUpdatedAt = time.Now() } else { @@ -4194,6 +4222,7 @@ var _ = Describe("Refresh Cluster - Installing Cases", func() { t := tests[i] It(t.name, func() { cluster = common.Cluster{ + ControlPlaneCount: 3, Cluster: models.Cluster{ ClusterNetworks: common.TestIPv4Networking.ClusterNetworks, ServiceNetworks: common.TestIPv4Networking.ServiceNetworks, @@ -4686,6 +4715,7 @@ var _ = Describe("NTP refresh cluster", func() { t := tests[i] It(t.name, func() { cluster = common.Cluster{ + ControlPlaneCount: 3, Cluster: models.Cluster{ ClusterNetworks: common.TestIPv4Networking.ClusterNetworks, ServiceNetworks: common.TestIPv4Networking.ServiceNetworks, @@ -4792,6 +4822,7 @@ var _ = Describe("Single node", func() { validationsChecker *validationsChecker setMachineCidrUpdatedAt bool errorExpected bool + controlPlaneCount int64 }{ { name: "non ha mode, too many nodes", @@ -4969,6 +5000,7 @@ var _ = Describe("Single node", func() { haMode := models.ClusterHighAvailabilityModeNone It(t.name, func() { cluster = common.Cluster{ + ControlPlaneCount: 1, Cluster: models.Cluster{ ClusterNetworks: common.TestIPv4Networking.ClusterNetworks, ServiceNetworks: common.TestIPv4Networking.ServiceNetworks, @@ -4985,6 +5017,7 @@ var _ = Describe("Single node", func() { OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, }, } + if t.srcState == models.ClusterStatusPreparingForInstallation && t.dstState == models.ClusterStatusInstalling { cluster.Cluster.StatusUpdatedAt = strfmt.DateTime(time.Now()) cluster.LastInstallationPreparation = models.LastInstallationPreparation{ diff --git a/internal/cluster/validator_test.go b/internal/cluster/validator_test.go index 22ad864c2b34..75a761146244 100644 --- a/internal/cluster/validator_test.go +++ b/internal/cluster/validator_test.go @@ -692,37 +692,6 @@ var _ = Describe("SufficientMastersCount", func() { }) Context("pass validation", func() { - It("with matching counts, default ControlPlaneCount", func() { - mockHostAPI.EXPECT(). - IsValidMasterCandidate( - gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - ).Return(true, nil).AnyTimes() - - preprocessContext := &clusterPreprocessContext{ - clusterId: clusterID, - cluster: &common.Cluster{Cluster: models.Cluster{ - ID: &clusterID, - OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, - HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeFull), - Hosts: []*models.Host{ - { - Role: models.HostRoleMaster, - }, - { - Role: models.HostRoleMaster, - }, - { - Role: models.HostRoleMaster, - }, - }, - }}, - } - - status, message := validator.SufficientMastersCount(preprocessContext) - Expect(status).To(Equal(ValidationSuccess)) - Expect(message).To(Equal("The cluster has the exact amount of dedicated control plane nodes.")) - }) - It("with matching counts, set ControlPlaneCount", func() { mockHostAPI.EXPECT(). IsValidMasterCandidate( @@ -756,31 +725,6 @@ var _ = Describe("SufficientMastersCount", func() { Expect(message).To(Equal("The cluster has the exact amount of dedicated control plane nodes.")) }) - It("with SNO cluster, default controlPlaneCount", func() { - mockHostAPI.EXPECT(). - IsValidMasterCandidate( - gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - ).Return(true, nil).AnyTimes() - - preprocessContext := &clusterPreprocessContext{ - clusterId: clusterID, - cluster: &common.Cluster{Cluster: models.Cluster{ - ID: &clusterID, - OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, - HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeNone), - Hosts: []*models.Host{ - { - Role: models.HostRoleMaster, - }, - }, - }}, - } - - status, message := validator.SufficientMastersCount(preprocessContext) - Expect(status).To(Equal(ValidationSuccess)) - Expect(message).To(Equal("The cluster has the exact amount of dedicated control plane nodes.")) - }) - It("with SNO cluster, set controlPlaneCount", func() { mockHostAPI.EXPECT(). IsValidMasterCandidate( @@ -849,7 +793,7 @@ var _ = Describe("SufficientMastersCount", func() { }) Context("fails validation", func() { - It("with multi node cluster, 5 masters but expected 3 by default", func() { + It("with multi node cluster, 5 masters but expected 3", func() { mockHostAPI.EXPECT(). IsValidMasterCandidate( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), @@ -858,6 +802,7 @@ var _ = Describe("SufficientMastersCount", func() { preprocessContext := &clusterPreprocessContext{ clusterId: clusterID, cluster: &common.Cluster{ + ControlPlaneCount: 3, Cluster: models.Cluster{ ID: &clusterID, OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, @@ -884,13 +829,10 @@ var _ = Describe("SufficientMastersCount", func() { status, message := validator.SufficientMastersCount(preprocessContext) Expect(status).To(Equal(ValidationFailure)) - Expect(message).To(Equal(fmt.Sprintf( - "The cluster must have exactly %d dedicated control plane nodes. Add or remove hosts, or change their roles configurations to meet the requirement.", - common.MinMasterHostsNeededForInstallationInHaMode, - ))) + Expect(message).To(Equal("The cluster must have exactly 3 dedicated control plane nodes. Add or remove hosts, or change their roles configurations to meet the requirement.")) }) - It("with multi node cluster, 5 masters but expected 3", func() { + It("with SNO cluster, 2 masters 0 workers", func() { mockHostAPI.EXPECT(). IsValidMasterCandidate( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), @@ -899,11 +841,11 @@ var _ = Describe("SufficientMastersCount", func() { preprocessContext := &clusterPreprocessContext{ clusterId: clusterID, cluster: &common.Cluster{ - ControlPlaneCount: 3, + ControlPlaneCount: 1, Cluster: models.Cluster{ ID: &clusterID, OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, - HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeFull), + HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeNone), Hosts: []*models.Host{ { Role: models.HostRoleMaster, @@ -911,47 +853,10 @@ var _ = Describe("SufficientMastersCount", func() { { Role: models.HostRoleMaster, }, - { - Role: models.HostRoleMaster, - }, - { - Role: models.HostRoleMaster, - }, - { - Role: models.HostRoleMaster, - }, }, }}, } - status, message := validator.SufficientMastersCount(preprocessContext) - Expect(status).To(Equal(ValidationFailure)) - Expect(message).To(Equal("The cluster must have exactly 3 dedicated control plane nodes. Add or remove hosts, or change their roles configurations to meet the requirement.")) - }) - - It("with SNO cluster, 2 masters 0 workers", func() { - mockHostAPI.EXPECT(). - IsValidMasterCandidate( - gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - ).Return(true, nil).AnyTimes() - - preprocessContext := &clusterPreprocessContext{ - clusterId: clusterID, - cluster: &common.Cluster{Cluster: models.Cluster{ - ID: &clusterID, - OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, - HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeNone), - Hosts: []*models.Host{ - { - Role: models.HostRoleMaster, - }, - { - Role: models.HostRoleMaster, - }, - }, - }}, - } - status, message := validator.SufficientMastersCount(preprocessContext) Expect(status).To(Equal(ValidationFailure)) Expect(message).To(Equal("Single-node clusters must have a single control plane node and no workers.")) @@ -965,19 +870,21 @@ var _ = Describe("SufficientMastersCount", func() { preprocessContext := &clusterPreprocessContext{ clusterId: clusterID, - cluster: &common.Cluster{Cluster: models.Cluster{ - ID: &clusterID, - OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, - HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeNone), - Hosts: []*models.Host{ - { - Role: models.HostRoleMaster, - }, - { - Role: models.HostRoleWorker, + cluster: &common.Cluster{ + ControlPlaneCount: 1, + Cluster: models.Cluster{ + ID: &clusterID, + OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, + HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeNone), + Hosts: []*models.Host{ + { + Role: models.HostRoleMaster, + }, + { + Role: models.HostRoleWorker, + }, }, - }, - }}, + }}, } status, message := validator.SufficientMastersCount(preprocessContext) diff --git a/internal/featuresupport/feature_support_level.go b/internal/featuresupport/feature_support_level.go index c55b0bf2d76d..5c677f072d1d 100644 --- a/internal/featuresupport/feature_support_level.go +++ b/internal/featuresupport/feature_support_level.go @@ -17,6 +17,7 @@ var featuresList = map[models.FeatureSupportLevelID]SupportLevelFeature{ models.FeatureSupportLevelIDMINIMALISO: (&MinimalIso{}).New(), models.FeatureSupportLevelIDFULLISO: (&FullIso{}).New(), models.FeatureSupportLevelIDSKIPMCOREBOOT: &skipMcoReboot{}, + models.FeatureSupportLevelIDSTRETCHEDCLUSTERS: (&StretchedCluster{}).New(), // Network features models.FeatureSupportLevelIDVIPAUTOALLOC: (&VipAutoAllocFeature{}).New(), diff --git a/internal/featuresupport/feature_support_test.go b/internal/featuresupport/feature_support_test.go index 7a31b8801c82..30da134525e3 100644 --- a/internal/featuresupport/feature_support_test.go +++ b/internal/featuresupport/feature_support_test.go @@ -177,6 +177,62 @@ var _ = Describe("V2ListFeatureSupportLevels API", func() { ) }) + Context("Test Stretched Clusters", func() { + feature := models.FeatureSupportLevelIDSTRETCHEDCLUSTERS + arch := "DoesNotMatter" + + It("test feature availability", func() { + Expect(IsFeatureAvailable(feature, common.MinimumVersionForStretchedControlPlanesCluster, swag.String(arch))).To(BeTrue()) + Expect(IsFeatureAvailable(feature, "4.17", swag.String(arch))).To(BeFalse()) + }) + + DescribeTable("test feature compatability with other features", func(activeFeatures []SupportLevelFeature, shouldSucceed bool) { + activeFeatures = append(activeFeatures, &StretchedCluster{}) + + if shouldSucceed { + Expect(isFeaturesCompatibleWithFeatures(common.MinimumVersionForStretchedControlPlanesCluster, activeFeatures)).ToNot(HaveOccurred()) + } else { + Expect(isFeaturesCompatibleWithFeatures(common.MinimumVersionForStretchedControlPlanesCluster, activeFeatures)).To(HaveOccurred()) + } + }, + Entry( + "platform baremetal", + []SupportLevelFeature{&BaremetalPlatformFeature{}}, + true, + ), + + Entry( + "external platform", + []SupportLevelFeature{&ExternalPlatformFeature{}}, + false, + ), + + Entry( + "nutanix platform", + []SupportLevelFeature{&NutanixIntegrationFeature{}}, + false, + ), + + Entry( + "vsphere platform", + []SupportLevelFeature{&VsphereIntegrationFeature{}}, + false, + ), + + Entry( + "none platform", + []SupportLevelFeature{&NonePlatformFeature{}}, + false, + ), + + Entry( + "odf operator", + []SupportLevelFeature{&OdfFeature{}}, + false, + ), + ) + }) + Context("Test MCE not supported under 4.10", func() { feature := models.FeatureSupportLevelIDMCE It(fmt.Sprintf("%s test", feature), func() { @@ -269,19 +325,19 @@ var _ = Describe("V2ListFeatureSupportLevels API", func() { When("GetFeatureSupportList 4.12 with Platform", func() { It(string(*filters.PlatformType)+" "+swag.StringValue(filters.ExternalPlatformName), func() { list := GetFeatureSupportList("dummy", nil, filters.PlatformType, filters.ExternalPlatformName) - Expect(len(list)).To(Equal(26)) + Expect(len(list)).To(Equal(27)) }) }) } It("GetFeatureSupportList 4.12", func() { list := GetFeatureSupportList("4.12", nil, nil, nil) - Expect(len(list)).To(Equal(31)) + Expect(len(list)).To(Equal(32)) }) It("GetFeatureSupportList 4.13", func() { list := GetFeatureSupportList("4.13", nil, nil, nil) - Expect(len(list)).To(Equal(31)) + Expect(len(list)).To(Equal(32)) }) It("GetCpuArchitectureSupportList 4.12", func() { diff --git a/internal/featuresupport/features_misc.go b/internal/featuresupport/features_misc.go index 9f0e285c1463..cbfc57ae179a 100644 --- a/internal/featuresupport/features_misc.go +++ b/internal/featuresupport/features_misc.go @@ -300,3 +300,57 @@ func (f *skipMcoReboot) getFeatureActiveLevel(cluster *common.Cluster, infraEnv } return activeLevelActive } + +// Stretched Cluster +type StretchedCluster struct{} + +func (f *StretchedCluster) New() SupportLevelFeature { + return &StretchedCluster{} +} + +func (f *StretchedCluster) getId() models.FeatureSupportLevelID { + return models.FeatureSupportLevelIDSTRETCHEDCLUSTERS +} + +func (f *StretchedCluster) GetName() string { + return "Stretched Cluster" +} + +func (f *StretchedCluster) getSupportLevel(filters SupportLevelFilters) models.SupportLevel { + supported, err := common.BaseVersionGreaterOrEqual(common.MinimumVersionForStretchedControlPlanesCluster, filters.OpenshiftVersion) + if !supported || err != nil { + return models.SupportLevelUnavailable + } + + if filters.PlatformType != nil && *filters.PlatformType != models.PlatformTypeBaremetal { + return models.SupportLevelSupported + } + + return models.SupportLevelSupported +} + +func (f *StretchedCluster) getIncompatibleFeatures(openshiftVersion string) *[]models.FeatureSupportLevelID { + return &[]models.FeatureSupportLevelID{ + models.FeatureSupportLevelIDODF, + + // only baremetal platform is supported + models.FeatureSupportLevelIDEXTERNALPLATFORM, + models.FeatureSupportLevelIDNUTANIXINTEGRATION, + models.FeatureSupportLevelIDVSPHEREINTEGRATION, + models.FeatureSupportLevelIDNONEPLATFORM, + models.FeatureSupportLevelIDEXTERNALPLATFORMOCI, + } +} + +func (f *StretchedCluster) getIncompatibleArchitectures(openshiftVersion *string) *[]models.ArchitectureSupportLevelID { + return nil +} + +func (f *StretchedCluster) getFeatureActiveLevel(cluster *common.Cluster, infraEnv *models.InfraEnv, + clusterUpdateParams *models.V2ClusterUpdateParams, infraenvUpdateParams *models.InfraEnvUpdateParams) featureActiveLevel { + if cluster != nil && cluster.ControlPlaneCount > 3 { + return activeLevelActive + } + + return activeLevelNotActive +} diff --git a/internal/featuresupport/features_olm_operators.go b/internal/featuresupport/features_olm_operators.go index 7caba37dcacb..0ffb78a508a4 100644 --- a/internal/featuresupport/features_olm_operators.go +++ b/internal/featuresupport/features_olm_operators.go @@ -134,6 +134,7 @@ func (feature *OdfFeature) getIncompatibleFeatures(string) *[]models.FeatureSupp return &[]models.FeatureSupportLevelID{ models.FeatureSupportLevelIDSNO, models.FeatureSupportLevelIDLVM, + models.FeatureSupportLevelIDSTRETCHEDCLUSTERS, } } diff --git a/internal/featuresupport/features_platforms.go b/internal/featuresupport/features_platforms.go index eae4ad4cca8c..40e4d7ee0881 100644 --- a/internal/featuresupport/features_platforms.go +++ b/internal/featuresupport/features_platforms.go @@ -125,6 +125,7 @@ func (feature *NonePlatformFeature) getIncompatibleFeatures(string) *[]models.Fe return &[]models.FeatureSupportLevelID{ models.FeatureSupportLevelIDVIPAUTOALLOC, models.FeatureSupportLevelIDCLUSTERMANAGEDNETWORKING, + models.FeatureSupportLevelIDSTRETCHEDCLUSTERS, } } @@ -183,6 +184,7 @@ func (feature *NutanixIntegrationFeature) getIncompatibleFeatures(string) *[]mod models.FeatureSupportLevelIDCNV, models.FeatureSupportLevelIDPLATFORMMANAGEDNETWORKING, models.FeatureSupportLevelIDMTV, + models.FeatureSupportLevelIDSTRETCHEDCLUSTERS, } } @@ -236,6 +238,7 @@ func (feature *VsphereIntegrationFeature) getIncompatibleFeatures(openshiftVersi models.FeatureSupportLevelIDPLATFORMMANAGEDNETWORKING, models.FeatureSupportLevelIDCNV, models.FeatureSupportLevelIDMTV, + models.FeatureSupportLevelIDSTRETCHEDCLUSTERS, } if isNotSupported, err := common.BaseVersionLessThan("4.13", openshiftVersion); isNotSupported || err != nil { @@ -289,6 +292,7 @@ func (feature *OciIntegrationFeature) getIncompatibleFeatures(string) *[]models. models.FeatureSupportLevelIDVIPAUTOALLOC, models.FeatureSupportLevelIDDUALSTACKVIPS, models.FeatureSupportLevelIDFULLISO, + models.FeatureSupportLevelIDSTRETCHEDCLUSTERS, } } @@ -338,6 +342,7 @@ func (feature *ExternalPlatformFeature) getIncompatibleFeatures(string) *[]model return &[]models.FeatureSupportLevelID{ models.FeatureSupportLevelIDCLUSTERMANAGEDNETWORKING, models.FeatureSupportLevelIDVIPAUTOALLOC, + models.FeatureSupportLevelIDSTRETCHEDCLUSTERS, } } diff --git a/internal/operators/odf/validation_test.go b/internal/operators/odf/validation_test.go index d8b63ba001ad..fb4d843881c3 100644 --- a/internal/operators/odf/validation_test.go +++ b/internal/operators/odf/validation_test.go @@ -877,6 +877,7 @@ var _ = Describe("Ocs Operator use-cases", func() { } cluster = common.Cluster{ + ControlPlaneCount: 3, Cluster: models.Cluster{ ID: &clusterId, ClusterNetworks: common.TestIPv4Networking.ClusterNetworks, diff --git a/models/feature_support_level_id.go b/models/feature_support_level_id.go index 2a7d4ad667ff..ae187e498d3c 100644 --- a/models/feature_support_level_id.go +++ b/models/feature_support_level_id.go @@ -125,6 +125,9 @@ const ( // FeatureSupportLevelIDOPENSHIFTAI captures enum value "OPENSHIFT_AI" FeatureSupportLevelIDOPENSHIFTAI FeatureSupportLevelID = "OPENSHIFT_AI" + + // FeatureSupportLevelIDSTRETCHEDCLUSTERS captures enum value "STRETCHED_CLUSTERS" + FeatureSupportLevelIDSTRETCHEDCLUSTERS FeatureSupportLevelID = "STRETCHED_CLUSTERS" ) // for schema @@ -132,7 +135,7 @@ var featureSupportLevelIdEnum []interface{} func init() { var res []FeatureSupportLevelID - if err := json.Unmarshal([]byte(`["SNO","VIP_AUTO_ALLOC","CUSTOM_MANIFEST","SINGLE_NODE_EXPANSION","LVM","ODF","LSO","CNV","MCE","MTV","NUTANIX_INTEGRATION","BAREMETAL_PLATFORM","NONE_PLATFORM","VSPHERE_INTEGRATION","DUAL_STACK_VIPS","CLUSTER_MANAGED_NETWORKING","USER_MANAGED_NETWORKING","MINIMAL_ISO","FULL_ISO","EXTERNAL_PLATFORM_OCI","DUAL_STACK","PLATFORM_MANAGED_NETWORKING","SKIP_MCO_REBOOT","EXTERNAL_PLATFORM","OVN_NETWORK_TYPE","SDN_NETWORK_TYPE","NODE_FEATURE_DISCOVERY","NVIDIA_GPU","PIPELINES","SERVICEMESH","SERVERLESS","OPENSHIFT_AI"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["SNO","VIP_AUTO_ALLOC","CUSTOM_MANIFEST","SINGLE_NODE_EXPANSION","LVM","ODF","LSO","CNV","MCE","MTV","NUTANIX_INTEGRATION","BAREMETAL_PLATFORM","NONE_PLATFORM","VSPHERE_INTEGRATION","DUAL_STACK_VIPS","CLUSTER_MANAGED_NETWORKING","USER_MANAGED_NETWORKING","MINIMAL_ISO","FULL_ISO","EXTERNAL_PLATFORM_OCI","DUAL_STACK","PLATFORM_MANAGED_NETWORKING","SKIP_MCO_REBOOT","EXTERNAL_PLATFORM","OVN_NETWORK_TYPE","SDN_NETWORK_TYPE","NODE_FEATURE_DISCOVERY","NVIDIA_GPU","PIPELINES","SERVICEMESH","SERVERLESS","OPENSHIFT_AI","STRETCHED_CLUSTERS"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/restapi/embedded_spec.go b/restapi/embedded_spec.go index b33874b2983c..8db0890f1a9d 100644 --- a/restapi/embedded_spec.go +++ b/restapi/embedded_spec.go @@ -7773,7 +7773,8 @@ func init() { "PIPELINES", "SERVICEMESH", "SERVERLESS", - "OPENSHIFT_AI" + "OPENSHIFT_AI", + "STRETCHED_CLUSTERS" ] }, "finalizing-stage": { @@ -18634,7 +18635,8 @@ func init() { "PIPELINES", "SERVICEMESH", "SERVERLESS", - "OPENSHIFT_AI" + "OPENSHIFT_AI", + "STRETCHED_CLUSTERS" ] }, "finalizing-stage": { diff --git a/swagger.yaml b/swagger.yaml index 6d368ec5d145..158a7a8d524f 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -4159,6 +4159,7 @@ definitions: - 'SERVICEMESH' - 'SERVERLESS' - 'OPENSHIFT_AI' + - 'STRETCHED_CLUSTERS' architecture-support-level-id: type: string diff --git a/vendor/github.com/openshift/assisted-service/api/common/common_types.go b/vendor/github.com/openshift/assisted-service/api/common/common_types.go index 6eff7525735c..19afd847d2e2 100644 --- a/vendor/github.com/openshift/assisted-service/api/common/common_types.go +++ b/vendor/github.com/openshift/assisted-service/api/common/common_types.go @@ -16,4 +16,3 @@ type ValidationsStatus map[string]ValidationResults // +kubebuilder:object:generate=true type ValidationResults []ValidationResult - diff --git a/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go b/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go index 2a7d4ad667ff..ae187e498d3c 100644 --- a/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go +++ b/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go @@ -125,6 +125,9 @@ const ( // FeatureSupportLevelIDOPENSHIFTAI captures enum value "OPENSHIFT_AI" FeatureSupportLevelIDOPENSHIFTAI FeatureSupportLevelID = "OPENSHIFT_AI" + + // FeatureSupportLevelIDSTRETCHEDCLUSTERS captures enum value "STRETCHED_CLUSTERS" + FeatureSupportLevelIDSTRETCHEDCLUSTERS FeatureSupportLevelID = "STRETCHED_CLUSTERS" ) // for schema @@ -132,7 +135,7 @@ var featureSupportLevelIdEnum []interface{} func init() { var res []FeatureSupportLevelID - if err := json.Unmarshal([]byte(`["SNO","VIP_AUTO_ALLOC","CUSTOM_MANIFEST","SINGLE_NODE_EXPANSION","LVM","ODF","LSO","CNV","MCE","MTV","NUTANIX_INTEGRATION","BAREMETAL_PLATFORM","NONE_PLATFORM","VSPHERE_INTEGRATION","DUAL_STACK_VIPS","CLUSTER_MANAGED_NETWORKING","USER_MANAGED_NETWORKING","MINIMAL_ISO","FULL_ISO","EXTERNAL_PLATFORM_OCI","DUAL_STACK","PLATFORM_MANAGED_NETWORKING","SKIP_MCO_REBOOT","EXTERNAL_PLATFORM","OVN_NETWORK_TYPE","SDN_NETWORK_TYPE","NODE_FEATURE_DISCOVERY","NVIDIA_GPU","PIPELINES","SERVICEMESH","SERVERLESS","OPENSHIFT_AI"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["SNO","VIP_AUTO_ALLOC","CUSTOM_MANIFEST","SINGLE_NODE_EXPANSION","LVM","ODF","LSO","CNV","MCE","MTV","NUTANIX_INTEGRATION","BAREMETAL_PLATFORM","NONE_PLATFORM","VSPHERE_INTEGRATION","DUAL_STACK_VIPS","CLUSTER_MANAGED_NETWORKING","USER_MANAGED_NETWORKING","MINIMAL_ISO","FULL_ISO","EXTERNAL_PLATFORM_OCI","DUAL_STACK","PLATFORM_MANAGED_NETWORKING","SKIP_MCO_REBOOT","EXTERNAL_PLATFORM","OVN_NETWORK_TYPE","SDN_NETWORK_TYPE","NODE_FEATURE_DISCOVERY","NVIDIA_GPU","PIPELINES","SERVICEMESH","SERVERLESS","OPENSHIFT_AI","STRETCHED_CLUSTERS"]`), &res); err != nil { panic(err) } for _, v := range res { From 1115854becf36968ef0ecd6d422048c6bf507ff5 Mon Sep 17 00:00:00 2001 From: danmanor Date: Mon, 25 Nov 2024 17:07:40 +0200 Subject: [PATCH 5/6] NO-ISSUE: Change the feature name from 'stretched clusters' to 'non-standard HA OCP Control Plane' --- .../models/feature_support_level_id.go | 6 +- .../models/feature_support_level_id.go | 6 +- internal/bminventory/inventory.go | 8 +-- internal/bminventory/inventory_test.go | 56 ++++++++--------- internal/cluster/cluster_test.go | 10 ++-- internal/cluster/transition_test.go | 60 +++++++++---------- internal/cluster/validator_test.go | 12 ++-- internal/common/common.go | 2 +- .../featuresupport/feature_support_level.go | 14 ++--- .../featuresupport/feature_support_test.go | 20 +++++-- internal/featuresupport/features_misc.go | 28 ++++----- .../featuresupport/features_olm_operators.go | 2 +- internal/featuresupport/features_platforms.go | 10 ++-- internal/operators/odf/odf_operator.go | 4 +- internal/operators/odf/validation_test.go | 2 +- internal/operators/odf/validations.go | 2 +- internal/testing/common.go | 4 +- models/feature_support_level_id.go | 6 +- restapi/embedded_spec.go | 4 +- subsystem/cluster_test.go | 4 +- swagger.yaml | 2 +- .../models/feature_support_level_id.go | 6 +- 22 files changed, 138 insertions(+), 130 deletions(-) diff --git a/api/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go b/api/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go index ae187e498d3c..4f19525b80b3 100644 --- a/api/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go +++ b/api/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go @@ -126,8 +126,8 @@ const ( // FeatureSupportLevelIDOPENSHIFTAI captures enum value "OPENSHIFT_AI" FeatureSupportLevelIDOPENSHIFTAI FeatureSupportLevelID = "OPENSHIFT_AI" - // FeatureSupportLevelIDSTRETCHEDCLUSTERS captures enum value "STRETCHED_CLUSTERS" - FeatureSupportLevelIDSTRETCHEDCLUSTERS FeatureSupportLevelID = "STRETCHED_CLUSTERS" + // FeatureSupportLevelIDNONSTANDARDHACONTROLPLANE captures enum value "NON_STANDARD_HA_CONTROL_PLANE" + FeatureSupportLevelIDNONSTANDARDHACONTROLPLANE FeatureSupportLevelID = "NON_STANDARD_HA_CONTROL_PLANE" ) // for schema @@ -135,7 +135,7 @@ var featureSupportLevelIdEnum []interface{} func init() { var res []FeatureSupportLevelID - if err := json.Unmarshal([]byte(`["SNO","VIP_AUTO_ALLOC","CUSTOM_MANIFEST","SINGLE_NODE_EXPANSION","LVM","ODF","LSO","CNV","MCE","MTV","NUTANIX_INTEGRATION","BAREMETAL_PLATFORM","NONE_PLATFORM","VSPHERE_INTEGRATION","DUAL_STACK_VIPS","CLUSTER_MANAGED_NETWORKING","USER_MANAGED_NETWORKING","MINIMAL_ISO","FULL_ISO","EXTERNAL_PLATFORM_OCI","DUAL_STACK","PLATFORM_MANAGED_NETWORKING","SKIP_MCO_REBOOT","EXTERNAL_PLATFORM","OVN_NETWORK_TYPE","SDN_NETWORK_TYPE","NODE_FEATURE_DISCOVERY","NVIDIA_GPU","PIPELINES","SERVICEMESH","SERVERLESS","OPENSHIFT_AI","STRETCHED_CLUSTERS"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["SNO","VIP_AUTO_ALLOC","CUSTOM_MANIFEST","SINGLE_NODE_EXPANSION","LVM","ODF","LSO","CNV","MCE","MTV","NUTANIX_INTEGRATION","BAREMETAL_PLATFORM","NONE_PLATFORM","VSPHERE_INTEGRATION","DUAL_STACK_VIPS","CLUSTER_MANAGED_NETWORKING","USER_MANAGED_NETWORKING","MINIMAL_ISO","FULL_ISO","EXTERNAL_PLATFORM_OCI","DUAL_STACK","PLATFORM_MANAGED_NETWORKING","SKIP_MCO_REBOOT","EXTERNAL_PLATFORM","OVN_NETWORK_TYPE","SDN_NETWORK_TYPE","NODE_FEATURE_DISCOVERY","NVIDIA_GPU","PIPELINES","SERVICEMESH","SERVERLESS","OPENSHIFT_AI","NON_STANDARD_HA_CONTROL_PLANE"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/client/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go b/client/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go index ae187e498d3c..4f19525b80b3 100644 --- a/client/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go +++ b/client/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go @@ -126,8 +126,8 @@ const ( // FeatureSupportLevelIDOPENSHIFTAI captures enum value "OPENSHIFT_AI" FeatureSupportLevelIDOPENSHIFTAI FeatureSupportLevelID = "OPENSHIFT_AI" - // FeatureSupportLevelIDSTRETCHEDCLUSTERS captures enum value "STRETCHED_CLUSTERS" - FeatureSupportLevelIDSTRETCHEDCLUSTERS FeatureSupportLevelID = "STRETCHED_CLUSTERS" + // FeatureSupportLevelIDNONSTANDARDHACONTROLPLANE captures enum value "NON_STANDARD_HA_CONTROL_PLANE" + FeatureSupportLevelIDNONSTANDARDHACONTROLPLANE FeatureSupportLevelID = "NON_STANDARD_HA_CONTROL_PLANE" ) // for schema @@ -135,7 +135,7 @@ var featureSupportLevelIdEnum []interface{} func init() { var res []FeatureSupportLevelID - if err := json.Unmarshal([]byte(`["SNO","VIP_AUTO_ALLOC","CUSTOM_MANIFEST","SINGLE_NODE_EXPANSION","LVM","ODF","LSO","CNV","MCE","MTV","NUTANIX_INTEGRATION","BAREMETAL_PLATFORM","NONE_PLATFORM","VSPHERE_INTEGRATION","DUAL_STACK_VIPS","CLUSTER_MANAGED_NETWORKING","USER_MANAGED_NETWORKING","MINIMAL_ISO","FULL_ISO","EXTERNAL_PLATFORM_OCI","DUAL_STACK","PLATFORM_MANAGED_NETWORKING","SKIP_MCO_REBOOT","EXTERNAL_PLATFORM","OVN_NETWORK_TYPE","SDN_NETWORK_TYPE","NODE_FEATURE_DISCOVERY","NVIDIA_GPU","PIPELINES","SERVICEMESH","SERVERLESS","OPENSHIFT_AI","STRETCHED_CLUSTERS"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["SNO","VIP_AUTO_ALLOC","CUSTOM_MANIFEST","SINGLE_NODE_EXPANSION","LVM","ODF","LSO","CNV","MCE","MTV","NUTANIX_INTEGRATION","BAREMETAL_PLATFORM","NONE_PLATFORM","VSPHERE_INTEGRATION","DUAL_STACK_VIPS","CLUSTER_MANAGED_NETWORKING","USER_MANAGED_NETWORKING","MINIMAL_ISO","FULL_ISO","EXTERNAL_PLATFORM_OCI","DUAL_STACK","PLATFORM_MANAGED_NETWORKING","SKIP_MCO_REBOOT","EXTERNAL_PLATFORM","OVN_NETWORK_TYPE","SDN_NETWORK_TYPE","NODE_FEATURE_DISCOVERY","NVIDIA_GPU","PIPELINES","SERVICEMESH","SERVERLESS","OPENSHIFT_AI","NON_STANDARD_HA_CONTROL_PLANE"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/internal/bminventory/inventory.go b/internal/bminventory/inventory.go index d917328e5dc3..26a5a3ede08c 100644 --- a/internal/bminventory/inventory.go +++ b/internal/bminventory/inventory.go @@ -1947,21 +1947,21 @@ func validateHighAvailabilityWithControlPlaneCount(highAvailabilityMode string, ) } - stretchedClustersNotSuported, err := common.BaseVersionLessThan(common.MinimumVersionForStretchedControlPlanesCluster, openshiftVersion) + nonStandardHAOCPControlPlaneNotSuported, err := common.BaseVersionLessThan(common.MinimumVersionForNonStandardHAOCPControlPlane, openshiftVersion) if err != nil { return err } if highAvailabilityMode == models.ClusterCreateParamsHighAvailabilityModeFull && controlPlaneCount != common.AllowedNumberOfMasterHostsForInstallationInHaModeOfOCP417OrOlder && - stretchedClustersNotSuported { + nonStandardHAOCPControlPlaneNotSuported { return common.NewApiError( http.StatusBadRequest, fmt.Errorf( "there should be exactly %d dedicated control plane nodes for high availability mode %s in openshift version older than %s", common.AllowedNumberOfMasterHostsForInstallationInHaModeOfOCP417OrOlder, highAvailabilityMode, - common.MinimumVersionForStretchedControlPlanesCluster, + common.MinimumVersionForNonStandardHAOCPControlPlane, ), ) } @@ -1976,7 +1976,7 @@ func validateHighAvailabilityWithControlPlaneCount(highAvailabilityMode string, common.MinMasterHostsNeededForInstallationInHaMode, common.MaxMasterHostsNeededForInstallationInHaModeOfOCP418OrNewer, highAvailabilityMode, - common.MinimumVersionForStretchedControlPlanesCluster, + common.MinimumVersionForNonStandardHAOCPControlPlane, ), ) } diff --git a/internal/bminventory/inventory_test.go b/internal/bminventory/inventory_test.go index e0cf9cf25bf7..68edca858694 100644 --- a/internal/bminventory/inventory_test.go +++ b/internal/bminventory/inventory_test.go @@ -7929,7 +7929,7 @@ var _ = Describe("V2UpdateCluster", func() { Expect(newCluster.ControlPlaneCount).To(BeEquivalentTo(4)) }) - It(fmt.Sprintf("descreasing to 3 control planes with OCP >= %s the value and multi-node", common.MinimumVersionForStretchedControlPlanesCluster), func() { + It(fmt.Sprintf("descreasing to 3 control planes with OCP >= %s the value and multi-node", common.MinimumVersionForNonStandardHAOCPControlPlane), func() { cluster := &common.Cluster{ Cluster: models.Cluster{ ID: &clusterID, @@ -7962,7 +7962,7 @@ var _ = Describe("V2UpdateCluster", func() { }) Context("should fail", func() { - It("update to invalid value, stretched clusters not supported", func() { + It("update to invalid value, non-standad HA OCP Control Plane not supported", func() { cluster := &common.Cluster{ Cluster: models.Cluster{ ID: &clusterID, @@ -7985,7 +7985,7 @@ var _ = Describe("V2UpdateCluster", func() { verifyApiErrorString(reply, http.StatusBadRequest, "there should be exactly 3 dedicated control plane nodes for high availability mode Full in openshift version older than 4.18") }) - It("update to invalid value, stretched clusters supported", func() { + It("update to invalid value, non-standad HA OCP Control Plane supported", func() { cluster := &common.Cluster{ Cluster: models.Cluster{ ID: &clusterID, @@ -8031,7 +8031,7 @@ var _ = Describe("V2UpdateCluster", func() { verifyApiErrorString(reply, http.StatusBadRequest, "single-node clusters must have a single control plane node") }) - It(fmt.Sprintf("update amount to != 3 when multi-node, OCP version < %s", common.MinimumVersionForStretchedControlPlanesCluster), func() { + It(fmt.Sprintf("update amount to != 3 when multi-node, OCP version < %s", common.MinimumVersionForNonStandardHAOCPControlPlane), func() { cluster := &common.Cluster{ Cluster: models.Cluster{ ID: &clusterID, @@ -8058,7 +8058,7 @@ var _ = Describe("V2UpdateCluster", func() { ) }) - It(fmt.Sprintf("update amount to != 3 when multi-node, OCP version >= %s", common.MinimumVersionForStretchedControlPlanesCluster), func() { + It(fmt.Sprintf("update amount to != 3 when multi-node, OCP version >= %s", common.MinimumVersionForNonStandardHAOCPControlPlane), func() { cluster := &common.Cluster{ Cluster: models.Cluster{ ID: &clusterID, @@ -15650,11 +15650,11 @@ location = "%s" Context("using defaults", func() { It("high_availability mode is set to Full", func() { - mockClusterRegisterSuccessWithVersion(common.X86CPUArchitecture, testutils.ValidOCPVersionForNonStretchedClusters) + mockClusterRegisterSuccessWithVersion(common.X86CPUArchitecture, testutils.ValidOCPVersionForNonStandardHAOCPControlPlane) reply := bm.V2RegisterCluster(ctx, installer.V2RegisterClusterParams{ NewClusterParams: &models.ClusterCreateParams{ - OpenshiftVersion: swag.String(testutils.ValidOCPVersionForNonStretchedClusters), + OpenshiftVersion: swag.String(testutils.ValidOCPVersionForNonStandardHAOCPControlPlane), HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeFull), }, }) @@ -15670,11 +15670,11 @@ location = "%s" }) It("high_availability mode is set to None", func() { - mockClusterRegisterSuccessWithVersion(common.X86CPUArchitecture, testutils.ValidOCPVersionForNonStretchedClusters) + mockClusterRegisterSuccessWithVersion(common.X86CPUArchitecture, testutils.ValidOCPVersionForNonStandardHAOCPControlPlane) reply := bm.V2RegisterCluster(ctx, installer.V2RegisterClusterParams{ NewClusterParams: &models.ClusterCreateParams{ - OpenshiftVersion: swag.String(testutils.ValidOCPVersionForNonStretchedClusters), + OpenshiftVersion: swag.String(testutils.ValidOCPVersionForNonStandardHAOCPControlPlane), HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeNone), }, }) @@ -15690,11 +15690,11 @@ location = "%s" }) It("control_plane_count is set to 3", func() { - mockClusterRegisterSuccessWithVersion(common.X86CPUArchitecture, testutils.ValidOCPVersionForNonStretchedClusters) + mockClusterRegisterSuccessWithVersion(common.X86CPUArchitecture, testutils.ValidOCPVersionForNonStandardHAOCPControlPlane) reply := bm.V2RegisterCluster(ctx, installer.V2RegisterClusterParams{ NewClusterParams: &models.ClusterCreateParams{ - OpenshiftVersion: swag.String(testutils.ValidOCPVersionForNonStretchedClusters), + OpenshiftVersion: swag.String(testutils.ValidOCPVersionForNonStandardHAOCPControlPlane), ControlPlaneCount: swag.Int64(3), }, }) @@ -15710,11 +15710,11 @@ location = "%s" }) It("control_plane_count is set to 1", func() { - mockClusterRegisterSuccessWithVersion(common.X86CPUArchitecture, testutils.ValidOCPVersionForNonStretchedClusters) + mockClusterRegisterSuccessWithVersion(common.X86CPUArchitecture, testutils.ValidOCPVersionForNonStandardHAOCPControlPlane) reply := bm.V2RegisterCluster(ctx, installer.V2RegisterClusterParams{ NewClusterParams: &models.ClusterCreateParams{ - OpenshiftVersion: swag.String(testutils.ValidOCPVersionForNonStretchedClusters), + OpenshiftVersion: swag.String(testutils.ValidOCPVersionForNonStandardHAOCPControlPlane), ControlPlaneCount: swag.Int64(1), }, }) @@ -15730,11 +15730,11 @@ location = "%s" }) It("not set", func() { - mockClusterRegisterSuccessWithVersion(common.X86CPUArchitecture, testutils.ValidOCPVersionForNonStretchedClusters) + mockClusterRegisterSuccessWithVersion(common.X86CPUArchitecture, testutils.ValidOCPVersionForNonStandardHAOCPControlPlane) reply := bm.V2RegisterCluster(ctx, installer.V2RegisterClusterParams{ NewClusterParams: &models.ClusterCreateParams{ - OpenshiftVersion: swag.String(testutils.ValidOCPVersionForNonStretchedClusters), + OpenshiftVersion: swag.String(testutils.ValidOCPVersionForNonStandardHAOCPControlPlane), }, }) @@ -15750,12 +15750,12 @@ location = "%s" }) }) - It(fmt.Sprintf("setting 5 control planes, multi-node with OCP version >= %s", common.MinimumVersionForStretchedControlPlanesCluster), func() { - mockClusterRegisterSuccessWithVersion(common.X86CPUArchitecture, common.MinimumVersionForStretchedControlPlanesCluster) + It(fmt.Sprintf("setting 5 control planes, multi-node with OCP version >= %s", common.MinimumVersionForNonStandardHAOCPControlPlane), func() { + mockClusterRegisterSuccessWithVersion(common.X86CPUArchitecture, common.MinimumVersionForNonStandardHAOCPControlPlane) reply := bm.V2RegisterCluster(ctx, installer.V2RegisterClusterParams{ NewClusterParams: &models.ClusterCreateParams{ - OpenshiftVersion: swag.String(common.MinimumVersionForStretchedControlPlanesCluster), + OpenshiftVersion: swag.String(common.MinimumVersionForNonStandardHAOCPControlPlane), ControlPlaneCount: swag.Int64(5), HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeFull), }, @@ -15773,11 +15773,11 @@ location = "%s" }) It("setting 1 control plane, single-node", func() { - mockClusterRegisterSuccessWithVersion(common.X86CPUArchitecture, testutils.ValidOCPVersionForNonStretchedClusters) + mockClusterRegisterSuccessWithVersion(common.X86CPUArchitecture, testutils.ValidOCPVersionForNonStandardHAOCPControlPlane) reply := bm.V2RegisterCluster(ctx, installer.V2RegisterClusterParams{ NewClusterParams: &models.ClusterCreateParams{ - OpenshiftVersion: swag.String(testutils.ValidOCPVersionForNonStretchedClusters), + OpenshiftVersion: swag.String(testutils.ValidOCPVersionForNonStandardHAOCPControlPlane), ControlPlaneCount: swag.Int64(1), HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeNone), }, @@ -15796,10 +15796,10 @@ location = "%s" }) Context("should fail", func() { - It("setting 6 control planes, multi-node, stretched clusters not supported", func() { + It("setting 6 control planes, multi-node, non-standad HA OCP Control Plane not supported", func() { reply := bm.V2RegisterCluster(ctx, installer.V2RegisterClusterParams{ NewClusterParams: &models.ClusterCreateParams{ - OpenshiftVersion: swag.String(testutils.ValidOCPVersionForNonStretchedClusters), + OpenshiftVersion: swag.String(testutils.ValidOCPVersionForNonStandardHAOCPControlPlane), ControlPlaneCount: swag.Int64(6), HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeFull), }, @@ -15812,10 +15812,10 @@ location = "%s" ) }) - It("setting 6 control planes, multi-node, stretched clusters supported", func() { + It("setting 6 control planes, multi-node, non-standad HA OCP Control Plane supported", func() { reply := bm.V2RegisterCluster(ctx, installer.V2RegisterClusterParams{ NewClusterParams: &models.ClusterCreateParams{ - OpenshiftVersion: swag.String(common.MinimumVersionForStretchedControlPlanesCluster), + OpenshiftVersion: swag.String(common.MinimumVersionForNonStandardHAOCPControlPlane), ControlPlaneCount: swag.Int64(6), HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeFull), }, @@ -15831,7 +15831,7 @@ location = "%s" It("setting 3 control planes, single-node", func() { reply := bm.V2RegisterCluster(ctx, installer.V2RegisterClusterParams{ NewClusterParams: &models.ClusterCreateParams{ - OpenshiftVersion: swag.String(testutils.ValidOCPVersionForNonStretchedClusters), + OpenshiftVersion: swag.String(testutils.ValidOCPVersionForNonStandardHAOCPControlPlane), ControlPlaneCount: swag.Int64(3), HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeNone), }, @@ -15847,7 +15847,7 @@ location = "%s" It("setting 1 control plane, mutli-node", func() { reply := bm.V2RegisterCluster(ctx, installer.V2RegisterClusterParams{ NewClusterParams: &models.ClusterCreateParams{ - OpenshiftVersion: swag.String(testutils.ValidOCPVersionForNonStretchedClusters), + OpenshiftVersion: swag.String(testutils.ValidOCPVersionForNonStandardHAOCPControlPlane), ControlPlaneCount: swag.Int64(1), HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeFull), }, @@ -15860,10 +15860,10 @@ location = "%s" ) }) - It("setting 4 control planes, multi-node, stretched clusters not supported", func() { + It("setting 4 control planes, multi-node, non-standad HA OCP Control Plane not supported", func() { reply := bm.V2RegisterCluster(ctx, installer.V2RegisterClusterParams{ NewClusterParams: &models.ClusterCreateParams{ - OpenshiftVersion: swag.String(testutils.ValidOCPVersionForNonStretchedClusters), + OpenshiftVersion: swag.String(testutils.ValidOCPVersionForNonStandardHAOCPControlPlane), ControlPlaneCount: swag.Int64(4), HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeFull), }, diff --git a/internal/cluster/cluster_test.go b/internal/cluster/cluster_test.go index 217fac6e1fcf..a3687943f0f9 100644 --- a/internal/cluster/cluster_test.go +++ b/internal/cluster/cluster_test.go @@ -196,7 +196,7 @@ var _ = Describe("TestClusterMonitoring", func() { PullSecretSet: true, MonitoredOperators: []*models.MonitoredOperator{&common.TestDefaultConfig.MonitoredOperator}, StatusUpdatedAt: strfmt.DateTime(time.Now()), - OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, + OpenshiftVersion: testing.ValidOCPVersionForNonStandardHAOCPControlPlane, }, TriggerMonitorTimestamp: time.Now(), } @@ -469,7 +469,7 @@ var _ = Describe("TestClusterMonitoring", func() { PullSecretSet: true, StatusInfo: swag.String(StatusInfoInsufficient), NetworkType: swag.String(models.ClusterNetworkTypeOVNKubernetes), - OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, + OpenshiftVersion: testing.ValidOCPVersionForNonStandardHAOCPControlPlane, }, TriggerMonitorTimestamp: time.Now(), } @@ -558,7 +558,7 @@ var _ = Describe("TestClusterMonitoring", func() { BaseDNSDomain: "test.com", PullSecretSet: true, NetworkType: swag.String(models.ClusterNetworkTypeOVNKubernetes), - OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, + OpenshiftVersion: testing.ValidOCPVersionForNonStandardHAOCPControlPlane, }, TriggerMonitorTimestamp: time.Now(), } @@ -2268,7 +2268,7 @@ var _ = Describe("Majority groups", func() { BaseDNSDomain: "test.com", PullSecretSet: true, NetworkType: swag.String(models.ClusterNetworkTypeOVNKubernetes), - OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, + OpenshiftVersion: testing.ValidOCPVersionForNonStandardHAOCPControlPlane, }} Expect(db.Create(&cluster).Error).ShouldNot(HaveOccurred()) @@ -2589,7 +2589,7 @@ var _ = Describe("ready_state", func() { BaseDNSDomain: "test.com", PullSecretSet: true, NetworkType: swag.String(models.ClusterNetworkTypeOVNKubernetes), - OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, + OpenshiftVersion: testing.ValidOCPVersionForNonStandardHAOCPControlPlane, }} Expect(db.Create(&cluster).Error).ShouldNot(HaveOccurred()) addInstallationRequirements(id, db) diff --git a/internal/cluster/transition_test.go b/internal/cluster/transition_test.go index d8dfd27f1010..452b9a7fd2bd 100644 --- a/internal/cluster/transition_test.go +++ b/internal/cluster/transition_test.go @@ -974,7 +974,7 @@ var _ = Describe("Refresh Cluster - No DHCP", func() { errorExpected: false, }, { - name: "pending-for-input to insufficient, too much masters - stretched masters cluster not available", + name: "pending-for-input to insufficient, too much masters - non-standard HA OCP Control Plane not available", srcState: models.ClusterStatusPendingForInput, dstState: models.ClusterStatusInsufficient, machineNetworks: common.TestIPv4Networking.MachineNetworks, @@ -1009,7 +1009,7 @@ var _ = Describe("Refresh Cluster - No DHCP", func() { errorExpected: false, }, { - name: "pending-for-input to insufficient, not enough masters - stretched masters cluster not available", + name: "pending-for-input to insufficient, not enough masters - non-standard HA OCP Control Plane not available", srcState: models.ClusterStatusPendingForInput, dstState: models.ClusterStatusInsufficient, machineNetworks: common.TestIPv4Networking.MachineNetworks, @@ -1042,7 +1042,7 @@ var _ = Describe("Refresh Cluster - No DHCP", func() { errorExpected: false, }, { - name: "pending-for-input to insufficient, too much masters - stretched masters cluster available", + name: "pending-for-input to insufficient, too much masters - non-standard HA OCP Control Plane available", srcState: models.ClusterStatusPendingForInput, dstState: models.ClusterStatusInsufficient, machineNetworks: common.TestIPv4Networking.MachineNetworks, @@ -1072,11 +1072,11 @@ var _ = Describe("Refresh Cluster - No DHCP", func() { SufficientMastersCount: {status: ValidationFailure, messagePattern: "The cluster must have exactly 5 dedicated control plane nodes. Add or remove hosts, or change their roles configurations to meet the requirement."}, }), errorExpected: false, - openshiftVersion: common.MinimumVersionForStretchedControlPlanesCluster, + openshiftVersion: common.MinimumVersionForNonStandardHAOCPControlPlane, controlPlaneCount: 5, }, { - name: "pending-for-input to insufficient, not enough masters - stretched masters cluster available", + name: "pending-for-input to insufficient, not enough masters - non-standard HA OCP Control Plane available", srcState: models.ClusterStatusPendingForInput, dstState: models.ClusterStatusInsufficient, machineNetworks: common.TestIPv4Networking.MachineNetworks, @@ -1104,11 +1104,11 @@ var _ = Describe("Refresh Cluster - No DHCP", func() { SufficientMastersCount: {status: ValidationFailure, messagePattern: "The cluster must have exactly 4 dedicated control plane nodes. Add or remove hosts, or change their roles configurations to meet the requirement."}, }), errorExpected: false, - openshiftVersion: common.MinimumVersionForStretchedControlPlanesCluster, + openshiftVersion: common.MinimumVersionForNonStandardHAOCPControlPlane, controlPlaneCount: 4, }, { - name: "pending-for-input to ready, sufficient amount of potential masters - stretched masters cluster not available", + name: "pending-for-input to ready, sufficient amount of potential masters - non-standard HA OCP Control Plane not available", srcState: models.ClusterStatusPendingForInput, dstState: models.ClusterStatusReady, machineNetworks: common.TestIPv4Networking.MachineNetworks, @@ -1138,7 +1138,7 @@ var _ = Describe("Refresh Cluster - No DHCP", func() { errorExpected: false, }, { - name: "pending-for-input to ready, sufficient amount of potential masters - stretched masters cluster available", + name: "pending-for-input to ready, sufficient amount of potential masters - non-standard HA OCP Control Plane available", srcState: models.ClusterStatusPendingForInput, dstState: models.ClusterStatusReady, machineNetworks: common.TestIPv4Networking.MachineNetworks, @@ -1168,7 +1168,7 @@ var _ = Describe("Refresh Cluster - No DHCP", func() { SufficientMastersCount: {status: ValidationSuccess, messagePattern: "The cluster has the exact amount of dedicated control plane nodes."}, }), errorExpected: false, - openshiftVersion: common.MinimumVersionForStretchedControlPlanesCluster, + openshiftVersion: common.MinimumVersionForNonStandardHAOCPControlPlane, controlPlaneCount: 5, }, { @@ -1531,7 +1531,7 @@ var _ = Describe("Refresh Cluster - No DHCP", func() { } if cluster.Cluster.OpenshiftVersion == "" { - cluster.Cluster.OpenshiftVersion = testing.ValidOCPVersionForNonStretchedClusters + cluster.Cluster.OpenshiftVersion = testing.ValidOCPVersionForNonStandardHAOCPControlPlane } if cluster.ControlPlaneCount == 0 { @@ -1807,7 +1807,7 @@ var _ = Describe("Refresh Cluster - Same networks", func() { ServiceNetworks: t.serviceNetworks, NetworkType: swag.String(models.ClusterNetworkTypeOVNKubernetes), StatusUpdatedAt: strfmt.DateTime(time.Now()), - OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, + OpenshiftVersion: testing.ValidOCPVersionForNonStandardHAOCPControlPlane, }, } Expect(db.Create(&cluster).Error).ShouldNot(HaveOccurred()) @@ -2059,7 +2059,7 @@ var _ = Describe("RefreshCluster - preparing for install", func() { Status: t.lastInstallationPreparationStatus, Reason: t.lastInstallationPreparationReason, }, - OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, + OpenshiftVersion: testing.ValidOCPVersionForNonStandardHAOCPControlPlane, }, } Expect(db.Create(&cluster).Error).ShouldNot(HaveOccurred()) @@ -2608,7 +2608,7 @@ var _ = Describe("Refresh Cluster - Advanced networking validations", func() { UserManagedNetworking: &t.userManagedNetworking, NetworkType: &t.networkType, VipDhcpAllocation: &t.vipDhcpAllocation, - OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, + OpenshiftVersion: testing.ValidOCPVersionForNonStandardHAOCPControlPlane, }, } @@ -3082,7 +3082,7 @@ var _ = Describe("Refresh Cluster - Advanced networking validations", func() { BaseDNSDomain: "test.com", UserManagedNetworking: &t.userManagedNetworking, NetworkType: &t.networkType, - OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, + OpenshiftVersion: testing.ValidOCPVersionForNonStandardHAOCPControlPlane, }, } Expect(db.Create(&cluster).Error).ShouldNot(HaveOccurred()) @@ -3242,7 +3242,7 @@ var _ = Describe("Refresh Cluster - With DHCP", func() { errorExpected: false, }, { - name: "pending-for-input to insufficient, too much masters - stretched masters cluster not available", + name: "pending-for-input to insufficient, too much masters - non-standard HA OCP Control Plane not available", srcState: models.ClusterStatusPendingForInput, dstState: models.ClusterStatusInsufficient, machineNetworks: common.TestIPv4Networking.MachineNetworks, @@ -3276,7 +3276,7 @@ var _ = Describe("Refresh Cluster - With DHCP", func() { errorExpected: false, }, { - name: "pending-for-input to insufficient, not enough masters - stretched masters cluster not available", + name: "pending-for-input to insufficient, not enough masters - non-standard HA OCP Control Plane not available", srcState: models.ClusterStatusPendingForInput, dstState: models.ClusterStatusInsufficient, machineNetworks: common.TestIPv4Networking.MachineNetworks, @@ -3308,7 +3308,7 @@ var _ = Describe("Refresh Cluster - With DHCP", func() { errorExpected: false, }, { - name: "pending-for-input to insufficient, too much masters - stretched masters cluster available", + name: "pending-for-input to insufficient, too much masters - non-standard HA OCP Control Plane available", srcState: models.ClusterStatusPendingForInput, dstState: models.ClusterStatusInsufficient, machineNetworks: common.TestIPv4Networking.MachineNetworks, @@ -3338,11 +3338,11 @@ var _ = Describe("Refresh Cluster - With DHCP", func() { SufficientMastersCount: {status: ValidationFailure, messagePattern: "The cluster must have exactly 5 dedicated control plane nodes. Add or remove hosts, or change their roles configurations to meet the requirement."}, }), errorExpected: false, - openshiftVersion: common.MinimumVersionForStretchedControlPlanesCluster, + openshiftVersion: common.MinimumVersionForNonStandardHAOCPControlPlane, controlPlaneCount: 5, }, { - name: "pending-for-input to insufficient, not enough masters - stretched masters cluster available", + name: "pending-for-input to insufficient, not enough masters - non-standard HA OCP Control Plane available", srcState: models.ClusterStatusPendingForInput, dstState: models.ClusterStatusInsufficient, machineNetworks: common.TestIPv4Networking.MachineNetworks, @@ -3369,7 +3369,7 @@ var _ = Describe("Refresh Cluster - With DHCP", func() { SufficientMastersCount: {status: ValidationFailure, messagePattern: "The cluster must have exactly 4 dedicated control plane nodes. Add or remove hosts, or change their roles configurations to meet the requirement."}, }), errorExpected: false, - openshiftVersion: common.MinimumVersionForStretchedControlPlanesCluster, + openshiftVersion: common.MinimumVersionForNonStandardHAOCPControlPlane, controlPlaneCount: 4, }, { @@ -3795,7 +3795,7 @@ var _ = Describe("Refresh Cluster - With DHCP", func() { } if cluster.Cluster.OpenshiftVersion == "" { - cluster.Cluster.OpenshiftVersion = testing.ValidOCPVersionForNonStretchedClusters + cluster.Cluster.OpenshiftVersion = testing.ValidOCPVersionForNonStandardHAOCPControlPlane } if cluster.ControlPlaneCount == 0 { @@ -3910,7 +3910,7 @@ var _ = Describe("Refresh Cluster - Installing Cases", func() { openshiftVersion string }{ { - name: "installing to installing - non stretched cluster", + name: "installing to installing - non non-standard HA OCP Control Plane", srcState: models.ClusterStatusInstalling, srcStatusInfo: statusInfoInstalling, dstState: models.ClusterStatusInstalling, @@ -3924,7 +3924,7 @@ var _ = Describe("Refresh Cluster - Installing Cases", func() { statusInfoChecker: makeValueChecker(statusInfoInstalling), }, { - name: "installing to installing - stretched cluster", + name: "installing to installing - non-standard HA OCP Control Plane", srcState: models.ClusterStatusInstalling, srcStatusInfo: statusInfoInstalling, dstState: models.ClusterStatusInstalling, @@ -3937,7 +3937,7 @@ var _ = Describe("Refresh Cluster - Installing Cases", func() { {ID: &hid6, Status: swag.String(models.ClusterStatusInstalling), Inventory: common.GenerateTestDefaultInventory(), Role: models.HostRoleWorker}, }, statusInfoChecker: makeValueChecker(statusInfoInstalling), - openshiftVersion: common.MinimumVersionForStretchedControlPlanesCluster, + openshiftVersion: common.MinimumVersionForNonStandardHAOCPControlPlane, }, { name: "installing to installing-pending-user-action", @@ -4098,7 +4098,7 @@ var _ = Describe("Refresh Cluster - Installing Cases", func() { statusInfoChecker: makeValueChecker(statusInfoFinalizing), }, { - name: "installing to finalizing - non stretched cluster", + name: "installing to finalizing - non non-standard HA OCP Control Plane", srcState: models.ClusterStatusInstalling, srcStatusInfo: statusInfoInstalling, dstState: models.ClusterStatusFinalizing, @@ -4112,7 +4112,7 @@ var _ = Describe("Refresh Cluster - Installing Cases", func() { statusInfoChecker: makeValueChecker(statusInfoFinalizing), }, { - name: "installing to finalizing - stretched cluster", + name: "installing to finalizing - non-standard HA OCP Control Plane", srcState: models.ClusterStatusInstalling, srcStatusInfo: statusInfoInstalling, dstState: models.ClusterStatusFinalizing, @@ -4124,7 +4124,7 @@ var _ = Describe("Refresh Cluster - Installing Cases", func() { {ID: &hid5, Status: swag.String(models.HostStatusInstalled), Inventory: common.GenerateTestDefaultInventory(), Role: models.HostRoleWorker}, }, statusInfoChecker: makeValueChecker(statusInfoFinalizing), - openshiftVersion: common.MinimumVersionForStretchedControlPlanesCluster, + openshiftVersion: common.MinimumVersionForNonStandardHAOCPControlPlane, }, { name: "installing to error - failing master", @@ -4237,7 +4237,7 @@ var _ = Describe("Refresh Cluster - Installing Cases", func() { PullSecretSet: t.pullSecretSet, MonitoredOperators: t.operators, StatusUpdatedAt: strfmt.DateTime(time.Now()), - OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, + OpenshiftVersion: testing.ValidOCPVersionForNonStandardHAOCPControlPlane, }, } if t.openshiftVersion != "" { @@ -4727,7 +4727,7 @@ var _ = Describe("NTP refresh cluster", func() { StatusInfo: &t.srcStatusInfo, BaseDNSDomain: "test.com", PullSecretSet: t.pullSecretSet, - OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, + OpenshiftVersion: testing.ValidOCPVersionForNonStandardHAOCPControlPlane, NetworkType: swag.String(models.ClusterNetworkTypeOVNKubernetes), }, } @@ -5014,7 +5014,7 @@ var _ = Describe("Single node", func() { PullSecretSet: t.pullSecretSet, NetworkType: swag.String(models.ClusterNetworkTypeOVNKubernetes), HighAvailabilityMode: &haMode, - OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, + OpenshiftVersion: testing.ValidOCPVersionForNonStandardHAOCPControlPlane, }, } diff --git a/internal/cluster/validator_test.go b/internal/cluster/validator_test.go index 75a761146244..3ed5c0ee50a9 100644 --- a/internal/cluster/validator_test.go +++ b/internal/cluster/validator_test.go @@ -704,7 +704,7 @@ var _ = Describe("SufficientMastersCount", func() { ControlPlaneCount: 3, Cluster: models.Cluster{ ID: &clusterID, - OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, + OpenshiftVersion: testing.ValidOCPVersionForNonStandardHAOCPControlPlane, HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeFull), Hosts: []*models.Host{ { @@ -737,7 +737,7 @@ var _ = Describe("SufficientMastersCount", func() { ControlPlaneCount: 1, Cluster: models.Cluster{ ID: &clusterID, - OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, + OpenshiftVersion: testing.ValidOCPVersionForNonStandardHAOCPControlPlane, HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeNone), Hosts: []*models.Host{ { @@ -764,7 +764,7 @@ var _ = Describe("SufficientMastersCount", func() { ControlPlaneCount: 5, Cluster: models.Cluster{ ID: &clusterID, - OpenshiftVersion: common.MinimumVersionForStretchedControlPlanesCluster, + OpenshiftVersion: common.MinimumVersionForNonStandardHAOCPControlPlane, HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeFull), Hosts: []*models.Host{ { @@ -805,7 +805,7 @@ var _ = Describe("SufficientMastersCount", func() { ControlPlaneCount: 3, Cluster: models.Cluster{ ID: &clusterID, - OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, + OpenshiftVersion: testing.ValidOCPVersionForNonStandardHAOCPControlPlane, HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeFull), Hosts: []*models.Host{ { @@ -844,7 +844,7 @@ var _ = Describe("SufficientMastersCount", func() { ControlPlaneCount: 1, Cluster: models.Cluster{ ID: &clusterID, - OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, + OpenshiftVersion: testing.ValidOCPVersionForNonStandardHAOCPControlPlane, HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeNone), Hosts: []*models.Host{ { @@ -874,7 +874,7 @@ var _ = Describe("SufficientMastersCount", func() { ControlPlaneCount: 1, Cluster: models.Cluster{ ID: &clusterID, - OpenshiftVersion: testing.ValidOCPVersionForNonStretchedClusters, + OpenshiftVersion: testing.ValidOCPVersionForNonStandardHAOCPControlPlane, HighAvailabilityMode: swag.String(models.ClusterCreateParamsHighAvailabilityModeNone), Hosts: []*models.Host{ { diff --git a/internal/common/common.go b/internal/common/common.go index 42edf79051de..07b994d2ea43 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -54,7 +54,7 @@ const ( AllowedNumberOfMasterHostsForInstallationInHaModeOfOCP417OrOlder = 3 AllowedNumberOfMasterHostsInNoneHaMode = 1 AllowedNumberOfWorkersInNoneHaMode = 0 - MinimumVersionForStretchedControlPlanesCluster = "4.18" + MinimumVersionForNonStandardHAOCPControlPlane = "4.18" MinimumNumberOfWorkersForNonSchedulableMastersClusterInHaMode = 2 ) diff --git a/internal/featuresupport/feature_support_level.go b/internal/featuresupport/feature_support_level.go index 5c677f072d1d..3198fa9845f8 100644 --- a/internal/featuresupport/feature_support_level.go +++ b/internal/featuresupport/feature_support_level.go @@ -11,13 +11,13 @@ import ( var featuresList = map[models.FeatureSupportLevelID]SupportLevelFeature{ // Generic features - models.FeatureSupportLevelIDSNO: (&SnoFeature{}).New(), - models.FeatureSupportLevelIDCUSTOMMANIFEST: (&CustomManifestFeature{}).New(), - models.FeatureSupportLevelIDSINGLENODEEXPANSION: (&SingleNodeExpansionFeature{}).New(), - models.FeatureSupportLevelIDMINIMALISO: (&MinimalIso{}).New(), - models.FeatureSupportLevelIDFULLISO: (&FullIso{}).New(), - models.FeatureSupportLevelIDSKIPMCOREBOOT: &skipMcoReboot{}, - models.FeatureSupportLevelIDSTRETCHEDCLUSTERS: (&StretchedCluster{}).New(), + models.FeatureSupportLevelIDSNO: (&SnoFeature{}).New(), + models.FeatureSupportLevelIDCUSTOMMANIFEST: (&CustomManifestFeature{}).New(), + models.FeatureSupportLevelIDSINGLENODEEXPANSION: (&SingleNodeExpansionFeature{}).New(), + models.FeatureSupportLevelIDMINIMALISO: (&MinimalIso{}).New(), + models.FeatureSupportLevelIDFULLISO: (&FullIso{}).New(), + models.FeatureSupportLevelIDSKIPMCOREBOOT: &skipMcoReboot{}, + models.FeatureSupportLevelIDNONSTANDARDHACONTROLPLANE: (&NonStandardHAControlPlane{}).New(), // Network features models.FeatureSupportLevelIDVIPAUTOALLOC: (&VipAutoAllocFeature{}).New(), diff --git a/internal/featuresupport/feature_support_test.go b/internal/featuresupport/feature_support_test.go index 30da134525e3..3722d1d36686 100644 --- a/internal/featuresupport/feature_support_test.go +++ b/internal/featuresupport/feature_support_test.go @@ -177,22 +177,30 @@ var _ = Describe("V2ListFeatureSupportLevels API", func() { ) }) - Context("Test Stretched Clusters", func() { - feature := models.FeatureSupportLevelIDSTRETCHEDCLUSTERS + Context("Test non-standad HA OCP Control Plane", func() { + feature := models.FeatureSupportLevelIDNONSTANDARDHACONTROLPLANE arch := "DoesNotMatter" It("test feature availability", func() { - Expect(IsFeatureAvailable(feature, common.MinimumVersionForStretchedControlPlanesCluster, swag.String(arch))).To(BeTrue()) + Expect(IsFeatureAvailable(feature, common.MinimumVersionForNonStandardHAOCPControlPlane, swag.String(arch))).To(BeTrue()) Expect(IsFeatureAvailable(feature, "4.17", swag.String(arch))).To(BeFalse()) }) DescribeTable("test feature compatability with other features", func(activeFeatures []SupportLevelFeature, shouldSucceed bool) { - activeFeatures = append(activeFeatures, &StretchedCluster{}) + activeFeatures = append(activeFeatures, &NonStandardHAControlPlane{}) if shouldSucceed { - Expect(isFeaturesCompatibleWithFeatures(common.MinimumVersionForStretchedControlPlanesCluster, activeFeatures)).ToNot(HaveOccurred()) + Expect( + isFeaturesCompatibleWithFeatures( + common.MinimumVersionForNonStandardHAOCPControlPlane, + activeFeatures), + ).ToNot(HaveOccurred()) } else { - Expect(isFeaturesCompatibleWithFeatures(common.MinimumVersionForStretchedControlPlanesCluster, activeFeatures)).To(HaveOccurred()) + Expect( + isFeaturesCompatibleWithFeatures( + common.MinimumVersionForNonStandardHAOCPControlPlane, + activeFeatures), + ).To(HaveOccurred()) } }, Entry( diff --git a/internal/featuresupport/features_misc.go b/internal/featuresupport/features_misc.go index cbfc57ae179a..a3d77245c15b 100644 --- a/internal/featuresupport/features_misc.go +++ b/internal/featuresupport/features_misc.go @@ -301,35 +301,35 @@ func (f *skipMcoReboot) getFeatureActiveLevel(cluster *common.Cluster, infraEnv return activeLevelActive } -// Stretched Cluster -type StretchedCluster struct{} +// Non-standard HA OCP Control Plane +type NonStandardHAControlPlane struct{} -func (f *StretchedCluster) New() SupportLevelFeature { - return &StretchedCluster{} +func (f *NonStandardHAControlPlane) New() SupportLevelFeature { + return &NonStandardHAControlPlane{} } -func (f *StretchedCluster) getId() models.FeatureSupportLevelID { - return models.FeatureSupportLevelIDSTRETCHEDCLUSTERS +func (f *NonStandardHAControlPlane) getId() models.FeatureSupportLevelID { + return models.FeatureSupportLevelIDNONSTANDARDHACONTROLPLANE } -func (f *StretchedCluster) GetName() string { - return "Stretched Cluster" +func (f *NonStandardHAControlPlane) GetName() string { + return "Non-standard HA OCP Control Plane" } -func (f *StretchedCluster) getSupportLevel(filters SupportLevelFilters) models.SupportLevel { - supported, err := common.BaseVersionGreaterOrEqual(common.MinimumVersionForStretchedControlPlanesCluster, filters.OpenshiftVersion) +func (f *NonStandardHAControlPlane) getSupportLevel(filters SupportLevelFilters) models.SupportLevel { + supported, err := common.BaseVersionGreaterOrEqual(common.MinimumVersionForNonStandardHAOCPControlPlane, filters.OpenshiftVersion) if !supported || err != nil { return models.SupportLevelUnavailable } if filters.PlatformType != nil && *filters.PlatformType != models.PlatformTypeBaremetal { - return models.SupportLevelSupported + return models.SupportLevelUnavailable } return models.SupportLevelSupported } -func (f *StretchedCluster) getIncompatibleFeatures(openshiftVersion string) *[]models.FeatureSupportLevelID { +func (f *NonStandardHAControlPlane) getIncompatibleFeatures(openshiftVersion string) *[]models.FeatureSupportLevelID { return &[]models.FeatureSupportLevelID{ models.FeatureSupportLevelIDODF, @@ -342,11 +342,11 @@ func (f *StretchedCluster) getIncompatibleFeatures(openshiftVersion string) *[]m } } -func (f *StretchedCluster) getIncompatibleArchitectures(openshiftVersion *string) *[]models.ArchitectureSupportLevelID { +func (f *NonStandardHAControlPlane) getIncompatibleArchitectures(openshiftVersion *string) *[]models.ArchitectureSupportLevelID { return nil } -func (f *StretchedCluster) getFeatureActiveLevel(cluster *common.Cluster, infraEnv *models.InfraEnv, +func (f *NonStandardHAControlPlane) getFeatureActiveLevel(cluster *common.Cluster, infraEnv *models.InfraEnv, clusterUpdateParams *models.V2ClusterUpdateParams, infraenvUpdateParams *models.InfraEnvUpdateParams) featureActiveLevel { if cluster != nil && cluster.ControlPlaneCount > 3 { return activeLevelActive diff --git a/internal/featuresupport/features_olm_operators.go b/internal/featuresupport/features_olm_operators.go index 0ffb78a508a4..042f8c298448 100644 --- a/internal/featuresupport/features_olm_operators.go +++ b/internal/featuresupport/features_olm_operators.go @@ -134,7 +134,7 @@ func (feature *OdfFeature) getIncompatibleFeatures(string) *[]models.FeatureSupp return &[]models.FeatureSupportLevelID{ models.FeatureSupportLevelIDSNO, models.FeatureSupportLevelIDLVM, - models.FeatureSupportLevelIDSTRETCHEDCLUSTERS, + models.FeatureSupportLevelIDNONSTANDARDHACONTROLPLANE, } } diff --git a/internal/featuresupport/features_platforms.go b/internal/featuresupport/features_platforms.go index 40e4d7ee0881..3cb5ba514899 100644 --- a/internal/featuresupport/features_platforms.go +++ b/internal/featuresupport/features_platforms.go @@ -125,7 +125,7 @@ func (feature *NonePlatformFeature) getIncompatibleFeatures(string) *[]models.Fe return &[]models.FeatureSupportLevelID{ models.FeatureSupportLevelIDVIPAUTOALLOC, models.FeatureSupportLevelIDCLUSTERMANAGEDNETWORKING, - models.FeatureSupportLevelIDSTRETCHEDCLUSTERS, + models.FeatureSupportLevelIDNONSTANDARDHACONTROLPLANE, } } @@ -184,7 +184,7 @@ func (feature *NutanixIntegrationFeature) getIncompatibleFeatures(string) *[]mod models.FeatureSupportLevelIDCNV, models.FeatureSupportLevelIDPLATFORMMANAGEDNETWORKING, models.FeatureSupportLevelIDMTV, - models.FeatureSupportLevelIDSTRETCHEDCLUSTERS, + models.FeatureSupportLevelIDNONSTANDARDHACONTROLPLANE, } } @@ -238,7 +238,7 @@ func (feature *VsphereIntegrationFeature) getIncompatibleFeatures(openshiftVersi models.FeatureSupportLevelIDPLATFORMMANAGEDNETWORKING, models.FeatureSupportLevelIDCNV, models.FeatureSupportLevelIDMTV, - models.FeatureSupportLevelIDSTRETCHEDCLUSTERS, + models.FeatureSupportLevelIDNONSTANDARDHACONTROLPLANE, } if isNotSupported, err := common.BaseVersionLessThan("4.13", openshiftVersion); isNotSupported || err != nil { @@ -292,7 +292,7 @@ func (feature *OciIntegrationFeature) getIncompatibleFeatures(string) *[]models. models.FeatureSupportLevelIDVIPAUTOALLOC, models.FeatureSupportLevelIDDUALSTACKVIPS, models.FeatureSupportLevelIDFULLISO, - models.FeatureSupportLevelIDSTRETCHEDCLUSTERS, + models.FeatureSupportLevelIDNONSTANDARDHACONTROLPLANE, } } @@ -342,7 +342,7 @@ func (feature *ExternalPlatformFeature) getIncompatibleFeatures(string) *[]model return &[]models.FeatureSupportLevelID{ models.FeatureSupportLevelIDCLUSTERMANAGEDNETWORKING, models.FeatureSupportLevelIDVIPAUTOALLOC, - models.FeatureSupportLevelIDSTRETCHEDCLUSTERS, + models.FeatureSupportLevelIDNONSTANDARDHACONTROLPLANE, } } diff --git a/internal/operators/odf/odf_operator.go b/internal/operators/odf/odf_operator.go index e30114b586dd..07eea9289f48 100644 --- a/internal/operators/odf/odf_operator.go +++ b/internal/operators/odf/odf_operator.go @@ -129,7 +129,7 @@ func (o *operator) getValidDiskCount(disks []*models.Disk, installationDiskID st // ValidateHost verifies whether this operator is valid for given host func (o *operator) ValidateHost(_ context.Context, cluster *common.Cluster, host *models.Host, additionalOperatorRequirements *models.ClusterHostRequirementsDetails) (api.ValidationResult, error) { - // temporary disabling ODF for stretched clusters until it will be clear how ODF will work in this scenario. + // temporary disabling ODF for non-standad HA OCP Control Plane until it will be clear how ODF will work in this scenario. // We pass host validation to avoid false validation messages. The cluster validation will fail masters, _, _ := common.GetHostsByEachRole(&cluster.Cluster, true) if masterCount := len(masters); masterCount > 3 { @@ -199,7 +199,7 @@ func (o *operator) GetMonitoredOperator() *models.MonitoredOperator { // GetHostRequirements provides operator's requirements towards the host func (o *operator) GetHostRequirements(_ context.Context, cluster *common.Cluster, host *models.Host) (*models.ClusterHostRequirementsDetails, error) { - // temporary disabling ODF for stretched clusters until it will be clear how ODF will work in this scenario. + // temporary disabling ODF for non-standad HA OCP Control Plane until it will be clear how ODF will work in this scenario. // We pass host validation to avoid false validation messages. The cluster validation will fail masters, _, _ := common.GetHostsByEachRole(&cluster.Cluster, true) if masterCount := len(masters); masterCount > 3 { diff --git a/internal/operators/odf/validation_test.go b/internal/operators/odf/validation_test.go index fb4d843881c3..ca0feeefd3a1 100644 --- a/internal/operators/odf/validation_test.go +++ b/internal/operators/odf/validation_test.go @@ -896,7 +896,7 @@ var _ = Describe("Ocs Operator use-cases", func() { } if cluster.Cluster.OpenshiftVersion == "" { - cluster.Cluster.OpenshiftVersion = testing.ValidOCPVersionForNonStretchedClusters + cluster.Cluster.OpenshiftVersion = testing.ValidOCPVersionForNonStandardHAOCPControlPlane } Expect(db.Create(&cluster).Error).ShouldNot(HaveOccurred()) diff --git a/internal/operators/odf/validations.go b/internal/operators/odf/validations.go index 3897f96cfba7..9f307835fb86 100644 --- a/internal/operators/odf/validations.go +++ b/internal/operators/odf/validations.go @@ -34,7 +34,7 @@ type odfClusterResourcesInfo struct { func (o *operator) validateRequirements(cluster *models.Cluster) (api.ValidationStatus, string) { var status string - // temporary disabling ODF for stretched clusters until it will be clear how ODF will work in this scenario. + // temporary disabling ODF for non-standad HA OCP Control Plane until it will be clear how ODF will work in this scenario. masters, _, _ := common.GetHostsByEachRole(cluster, true) if masterCount := len(masters); masterCount > 3 { status = "There are currently more than 3 hosts designated to be control planes. ODF currently supports clusters with exactly three control plane nodes." diff --git a/internal/testing/common.go b/internal/testing/common.go index 87697fa07339..2cef0995c195 100644 --- a/internal/testing/common.go +++ b/internal/testing/common.go @@ -8,8 +8,8 @@ import ( "github.com/openshift/assisted-service/internal/common" ) -var ValidOCPVersionForNonStretchedClusters = func(majorMinorOCPVersion string) string { +var ValidOCPVersionForNonStandardHAOCPControlPlane = func(majorMinorOCPVersion string) string { splittedVersion := strings.Split(majorMinorOCPVersion, ".") intVersion, _ := strconv.Atoi(splittedVersion[1]) return fmt.Sprintf("%s.%d", splittedVersion[0], intVersion-1) -}(common.MinimumVersionForStretchedControlPlanesCluster) +}(common.MinimumVersionForNonStandardHAOCPControlPlane) diff --git a/models/feature_support_level_id.go b/models/feature_support_level_id.go index ae187e498d3c..4f19525b80b3 100644 --- a/models/feature_support_level_id.go +++ b/models/feature_support_level_id.go @@ -126,8 +126,8 @@ const ( // FeatureSupportLevelIDOPENSHIFTAI captures enum value "OPENSHIFT_AI" FeatureSupportLevelIDOPENSHIFTAI FeatureSupportLevelID = "OPENSHIFT_AI" - // FeatureSupportLevelIDSTRETCHEDCLUSTERS captures enum value "STRETCHED_CLUSTERS" - FeatureSupportLevelIDSTRETCHEDCLUSTERS FeatureSupportLevelID = "STRETCHED_CLUSTERS" + // FeatureSupportLevelIDNONSTANDARDHACONTROLPLANE captures enum value "NON_STANDARD_HA_CONTROL_PLANE" + FeatureSupportLevelIDNONSTANDARDHACONTROLPLANE FeatureSupportLevelID = "NON_STANDARD_HA_CONTROL_PLANE" ) // for schema @@ -135,7 +135,7 @@ var featureSupportLevelIdEnum []interface{} func init() { var res []FeatureSupportLevelID - if err := json.Unmarshal([]byte(`["SNO","VIP_AUTO_ALLOC","CUSTOM_MANIFEST","SINGLE_NODE_EXPANSION","LVM","ODF","LSO","CNV","MCE","MTV","NUTANIX_INTEGRATION","BAREMETAL_PLATFORM","NONE_PLATFORM","VSPHERE_INTEGRATION","DUAL_STACK_VIPS","CLUSTER_MANAGED_NETWORKING","USER_MANAGED_NETWORKING","MINIMAL_ISO","FULL_ISO","EXTERNAL_PLATFORM_OCI","DUAL_STACK","PLATFORM_MANAGED_NETWORKING","SKIP_MCO_REBOOT","EXTERNAL_PLATFORM","OVN_NETWORK_TYPE","SDN_NETWORK_TYPE","NODE_FEATURE_DISCOVERY","NVIDIA_GPU","PIPELINES","SERVICEMESH","SERVERLESS","OPENSHIFT_AI","STRETCHED_CLUSTERS"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["SNO","VIP_AUTO_ALLOC","CUSTOM_MANIFEST","SINGLE_NODE_EXPANSION","LVM","ODF","LSO","CNV","MCE","MTV","NUTANIX_INTEGRATION","BAREMETAL_PLATFORM","NONE_PLATFORM","VSPHERE_INTEGRATION","DUAL_STACK_VIPS","CLUSTER_MANAGED_NETWORKING","USER_MANAGED_NETWORKING","MINIMAL_ISO","FULL_ISO","EXTERNAL_PLATFORM_OCI","DUAL_STACK","PLATFORM_MANAGED_NETWORKING","SKIP_MCO_REBOOT","EXTERNAL_PLATFORM","OVN_NETWORK_TYPE","SDN_NETWORK_TYPE","NODE_FEATURE_DISCOVERY","NVIDIA_GPU","PIPELINES","SERVICEMESH","SERVERLESS","OPENSHIFT_AI","NON_STANDARD_HA_CONTROL_PLANE"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/restapi/embedded_spec.go b/restapi/embedded_spec.go index 8db0890f1a9d..a382792290d7 100644 --- a/restapi/embedded_spec.go +++ b/restapi/embedded_spec.go @@ -7774,7 +7774,7 @@ func init() { "SERVICEMESH", "SERVERLESS", "OPENSHIFT_AI", - "STRETCHED_CLUSTERS" + "NON_STANDARD_HA_CONTROL_PLANE" ] }, "finalizing-stage": { @@ -18636,7 +18636,7 @@ func init() { "SERVICEMESH", "SERVERLESS", "OPENSHIFT_AI", - "STRETCHED_CLUSTERS" + "NON_STANDARD_HA_CONTROL_PLANE" ] }, "finalizing-stage": { diff --git a/subsystem/cluster_test.go b/subsystem/cluster_test.go index 6f7357b02cbd..7271aeeeb207 100644 --- a/subsystem/cluster_test.go +++ b/subsystem/cluster_test.go @@ -5171,7 +5171,7 @@ var _ = Describe("Verify install-config manifest", func() { ) }) -var _ = Describe("Verify role assignment for stretched control plane cluster", func() { +var _ = Describe("Verify role assignment for non-standard HA OCP Control Plane cluster", func() { var ctx = context.TODO() It("with 4 masters, 1 worker", func() { @@ -5179,7 +5179,7 @@ var _ = Describe("Verify role assignment for stretched control plane cluster", f Context: ctx, NewClusterParams: &models.ClusterCreateParams{ Name: swag.String("test-cluster"), - OpenshiftVersion: swag.String(common.MinimumVersionForStretchedControlPlanesCluster), + OpenshiftVersion: swag.String(common.MinimumVersionForNonStandardHAOCPControlPlane), PullSecret: swag.String(pullSecret), ControlPlaneCount: swag.Int64(4), }, diff --git a/swagger.yaml b/swagger.yaml index 158a7a8d524f..8ff45071bfc1 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -4159,7 +4159,7 @@ definitions: - 'SERVICEMESH' - 'SERVERLESS' - 'OPENSHIFT_AI' - - 'STRETCHED_CLUSTERS' + - 'NON_STANDARD_HA_CONTROL_PLANE' architecture-support-level-id: type: string diff --git a/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go b/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go index ae187e498d3c..4f19525b80b3 100644 --- a/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go +++ b/vendor/github.com/openshift/assisted-service/models/feature_support_level_id.go @@ -126,8 +126,8 @@ const ( // FeatureSupportLevelIDOPENSHIFTAI captures enum value "OPENSHIFT_AI" FeatureSupportLevelIDOPENSHIFTAI FeatureSupportLevelID = "OPENSHIFT_AI" - // FeatureSupportLevelIDSTRETCHEDCLUSTERS captures enum value "STRETCHED_CLUSTERS" - FeatureSupportLevelIDSTRETCHEDCLUSTERS FeatureSupportLevelID = "STRETCHED_CLUSTERS" + // FeatureSupportLevelIDNONSTANDARDHACONTROLPLANE captures enum value "NON_STANDARD_HA_CONTROL_PLANE" + FeatureSupportLevelIDNONSTANDARDHACONTROLPLANE FeatureSupportLevelID = "NON_STANDARD_HA_CONTROL_PLANE" ) // for schema @@ -135,7 +135,7 @@ var featureSupportLevelIdEnum []interface{} func init() { var res []FeatureSupportLevelID - if err := json.Unmarshal([]byte(`["SNO","VIP_AUTO_ALLOC","CUSTOM_MANIFEST","SINGLE_NODE_EXPANSION","LVM","ODF","LSO","CNV","MCE","MTV","NUTANIX_INTEGRATION","BAREMETAL_PLATFORM","NONE_PLATFORM","VSPHERE_INTEGRATION","DUAL_STACK_VIPS","CLUSTER_MANAGED_NETWORKING","USER_MANAGED_NETWORKING","MINIMAL_ISO","FULL_ISO","EXTERNAL_PLATFORM_OCI","DUAL_STACK","PLATFORM_MANAGED_NETWORKING","SKIP_MCO_REBOOT","EXTERNAL_PLATFORM","OVN_NETWORK_TYPE","SDN_NETWORK_TYPE","NODE_FEATURE_DISCOVERY","NVIDIA_GPU","PIPELINES","SERVICEMESH","SERVERLESS","OPENSHIFT_AI","STRETCHED_CLUSTERS"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["SNO","VIP_AUTO_ALLOC","CUSTOM_MANIFEST","SINGLE_NODE_EXPANSION","LVM","ODF","LSO","CNV","MCE","MTV","NUTANIX_INTEGRATION","BAREMETAL_PLATFORM","NONE_PLATFORM","VSPHERE_INTEGRATION","DUAL_STACK_VIPS","CLUSTER_MANAGED_NETWORKING","USER_MANAGED_NETWORKING","MINIMAL_ISO","FULL_ISO","EXTERNAL_PLATFORM_OCI","DUAL_STACK","PLATFORM_MANAGED_NETWORKING","SKIP_MCO_REBOOT","EXTERNAL_PLATFORM","OVN_NETWORK_TYPE","SDN_NETWORK_TYPE","NODE_FEATURE_DISCOVERY","NVIDIA_GPU","PIPELINES","SERVICEMESH","SERVERLESS","OPENSHIFT_AI","NON_STANDARD_HA_CONTROL_PLANE"]`), &res); err != nil { panic(err) } for _, v := range res { From 1db54cb3d98b8ef3270e7c4fe7ac90284a4e57e2 Mon Sep 17 00:00:00 2001 From: danmanor Date: Tue, 26 Nov 2024 16:29:24 +0200 Subject: [PATCH 6/6] NO-ISSUE: PR fixes --- internal/bminventory/inventory.go | 8 ++-- internal/common/db.go | 5 +- ...ount_value_for_existing_cluster_records.go | 48 +++++++++---------- 3 files changed, 27 insertions(+), 34 deletions(-) diff --git a/internal/bminventory/inventory.go b/internal/bminventory/inventory.go index 26a5a3ede08c..15018a524efe 100644 --- a/internal/bminventory/inventory.go +++ b/internal/bminventory/inventory.go @@ -6675,18 +6675,18 @@ func getDefaultHighAvailabilityAndMasterCountParams(highAvailabilityMode *string if controlPlaneCount == nil { if *highAvailabilityMode == models.ClusterHighAvailabilityModeNone { return highAvailabilityMode, swag.Int64(common.AllowedNumberOfMasterHostsInNoneHaMode) - } else { - return highAvailabilityMode, swag.Int64(common.MinMasterHostsNeededForInstallationInHaMode) } + + return highAvailabilityMode, swag.Int64(common.MinMasterHostsNeededForInstallationInHaMode) } // only controlPlaneCount set if highAvailabilityMode == nil { if *controlPlaneCount == common.AllowedNumberOfMasterHostsInNoneHaMode { return swag.String(models.ClusterHighAvailabilityModeNone), controlPlaneCount - } else { - return swag.String(models.ClusterHighAvailabilityModeFull), controlPlaneCount } + + return swag.String(models.ClusterHighAvailabilityModeFull), controlPlaneCount } // both are set diff --git a/internal/common/db.go b/internal/common/db.go index 0f684c1b89b2..b71fc98f4a27 100644 --- a/internal/common/db.go +++ b/internal/common/db.go @@ -364,9 +364,6 @@ func GetClusterFromDBWithVips(db *gorm.DB, clusterId strfmt.UUID) (*Cluster, err } func GetHostCountByRole(db *gorm.DB, clusterID strfmt.UUID, role models.HostRole, suggested bool) (*int64, error) { - // Start from empty query - cleanQuery := db.Session(&gorm.Session{NewDB: true}) - var count int64 field := "role" @@ -377,7 +374,7 @@ func GetHostCountByRole(db *gorm.DB, clusterID strfmt.UUID, role models.HostRole } condition := fmt.Sprintf("hosts.%s = ?", field) - err := cleanQuery.Model(&Host{}). + err := db.Model(&Host{}). Joins("INNER JOIN clusters ON hosts.cluster_id = clusters.id"). Where("clusters.id = ?", clusterID.String()). Where(condition, string(role)). diff --git a/internal/migrations/20241122160000_update_new_column_control_plane_count_value_for_existing_cluster_records.go b/internal/migrations/20241122160000_update_new_column_control_plane_count_value_for_existing_cluster_records.go index a663eb24223d..58ac13d41fb1 100644 --- a/internal/migrations/20241122160000_update_new_column_control_plane_count_value_for_existing_cluster_records.go +++ b/internal/migrations/20241122160000_update_new_column_control_plane_count_value_for_existing_cluster_records.go @@ -11,32 +11,28 @@ import ( // split to 2 migrations, one for each query. func updateNewColumnControlPlaneCountValueForExistingClusterRecords() *gormigrate.Migration { - migrate := func(tx *gorm.DB) error { - transaction := tx.Begin() - - cleanQuery := tx.Session(&gorm.Session{NewDB: true}) - err := transaction.Model(&common.Cluster{}). - // control_plane_count value in existing records can be NULL or 0 (default). We want to set the value in both cases - Where(cleanQuery.Where("control_plane_count IS NULL").Or("control_plane_count = ?", 0)). - Where("high_availability_mode = ?", models.ClusterCreateParamsHighAvailabilityModeNone). - Update("control_plane_count", "1").Error - if err != nil { - transaction.Rollback() - return errors.Wrap(err, "failed to update control_plane_count value of existing SNO clusters to 1") - } - - cleanQuery = tx.Session(&gorm.Session{NewDB: true}) - err = transaction.Model(&common.Cluster{}). - // control_plane_count value in existing records can be NULL or 0 (default). We want to set the value in both cases - Where(cleanQuery.Where("control_plane_count IS NULL").Or("control_plane_count = ?", 0)). - Where("high_availability_mode = ?", models.ClusterCreateParamsHighAvailabilityModeFull). - Update("control_plane_count", "3").Error - if err != nil { - transaction.Rollback() - return errors.Wrap(err, "failed to update control_plane_count value of existing multi-node clusters to 3") - } - - return transaction.Commit().Error + migrate := func(db *gorm.DB) error { + return db.Transaction(func(tx *gorm.DB) error { + err := tx.Model(&common.Cluster{}). + // control_plane_count value in existing records can be NULL or 0 (default). We want to set the value in both cases + Where(tx.Where("control_plane_count IS NULL OR control_plane_count = ?", 0)). + Where("high_availability_mode = ?", models.ClusterCreateParamsHighAvailabilityModeNone). + Update("control_plane_count", "1").Error + if err != nil { + return errors.Wrap(err, "failed to update control_plane_count value of existing SNO clusters to 1") + } + + err = tx.Model(&common.Cluster{}). + // control_plane_count value in existing records can be NULL or 0 (default). We want to set the value in both cases + Where(tx.Where("control_plane_count IS NULL OR control_plane_count = ?", 0)). + Where("high_availability_mode = ?", models.ClusterCreateParamsHighAvailabilityModeFull). + Update("control_plane_count", "3").Error + if err != nil { + return errors.Wrap(err, "failed to update control_plane_count value of existing multi-node clusters to 3") + } + + return nil + }) } rollback := func(tx *gorm.DB) error {