From 867aafa1256f7e835a1c9f7802b0fea5a63de46b Mon Sep 17 00:00:00 2001 From: Juan Hernandez Date: Fri, 4 Oct 2024 12:36:42 +0200 Subject: [PATCH] MGMT-18913: Add support for external load balancer This patch adds support for using an external load balancer. It will only be available for OpenShift 4.16 or newer. To enable it the user will have to use the new `load_balancer.type` field of the `cluster` type, for example: ```json { ... "load_balancer": { "type": "user-managed" } } ``` To preserve backwards compatibility this will be optional, and the default value will be `cluster-managed`. When the value is `user-managed` the following will be added to the generated `install-config.yaml` file: ```yaml platform: baremetal: loadBalancer: type: UserManaged ``` The database will be automatically migrated so that for existing clusters the value of the `load_balancer_type` column will automatically be set to `cluster-managed`. Related: https://issues.redhat.com/browse/MGMT-18913 Signed-off-by: Juan Hernandez --- .../assisted-service/models/cluster.go | 46 +++++++ .../models/feature_support_level_id.go | 5 +- .../assisted-service/models/load_balancer.go | 114 ++++++++++++++++++ .../assisted-service/models/cluster.go | 46 +++++++ .../models/feature_support_level_id.go | 5 +- .../assisted-service/models/load_balancer.go | 114 ++++++++++++++++++ internal/cluster/validator.go | 7 ++ .../featuresupport/feature_support_level.go | 13 +- .../featuresupport/feature_support_test.go | 37 +++++- internal/featuresupport/features_platforms.go | 42 +++++++ internal/installcfg/installcfg.go | 27 +++-- internal/provider/baremetal/installConfig.go | 38 ++++++ models/cluster.go | 46 +++++++ models/feature_support_level_id.go | 5 +- models/load_balancer.go | 114 ++++++++++++++++++ restapi/embedded_spec.go | 42 ++++++- swagger.yaml | 25 +++- .../assisted-service/models/cluster.go | 46 +++++++ .../models/feature_support_level_id.go | 5 +- .../assisted-service/models/load_balancer.go | 114 ++++++++++++++++++ 20 files changed, 865 insertions(+), 26 deletions(-) create mode 100644 api/vendor/github.com/openshift/assisted-service/models/load_balancer.go create mode 100644 client/vendor/github.com/openshift/assisted-service/models/load_balancer.go create mode 100644 models/load_balancer.go create mode 100644 vendor/github.com/openshift/assisted-service/models/load_balancer.go diff --git a/api/vendor/github.com/openshift/assisted-service/models/cluster.go b/api/vendor/github.com/openshift/assisted-service/models/cluster.go index 2ef415d2019..d7522a47680 100644 --- a/api/vendor/github.com/openshift/assisted-service/models/cluster.go +++ b/api/vendor/github.com/openshift/assisted-service/models/cluster.go @@ -171,6 +171,9 @@ type Cluster struct { // last installation preparation LastInstallationPreparation LastInstallationPreparation `json:"last-installation-preparation,omitempty" gorm:"embedded;embeddedPrefix:last_installation_preparation_"` + // load balancer + LoadBalancer *LoadBalancer `json:"load_balancer,omitempty" gorm:"embedded;embeddedPrefix:load_balancer_"` + // The progress of log collection or empty if logs are not applicable LogsInfo LogsState `json:"logs_info,omitempty" gorm:"type:varchar(2048)"` @@ -372,6 +375,10 @@ func (m *Cluster) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateLoadBalancer(formats); err != nil { + res = append(res, err) + } + if err := m.validateLogsInfo(formats); err != nil { res = append(res, err) } @@ -948,6 +955,25 @@ func (m *Cluster) validateLastInstallationPreparation(formats strfmt.Registry) e return nil } +func (m *Cluster) validateLoadBalancer(formats strfmt.Registry) error { + if swag.IsZero(m.LoadBalancer) { // not required + return nil + } + + if m.LoadBalancer != nil { + if err := m.LoadBalancer.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("load_balancer") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("load_balancer") + } + return err + } + } + + return nil +} + func (m *Cluster) validateLogsInfo(formats strfmt.Registry) error { if swag.IsZero(m.LogsInfo) { // not required return nil @@ -1302,6 +1328,10 @@ func (m *Cluster) ContextValidate(ctx context.Context, formats strfmt.Registry) res = append(res, err) } + if err := m.contextValidateLoadBalancer(ctx, formats); err != nil { + res = append(res, err) + } + if err := m.contextValidateLogsInfo(ctx, formats); err != nil { res = append(res, err) } @@ -1494,6 +1524,22 @@ func (m *Cluster) contextValidateLastInstallationPreparation(ctx context.Context return nil } +func (m *Cluster) contextValidateLoadBalancer(ctx context.Context, formats strfmt.Registry) error { + + if m.LoadBalancer != nil { + if err := m.LoadBalancer.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("load_balancer") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("load_balancer") + } + return err + } + } + + return nil +} + func (m *Cluster) contextValidateLogsInfo(ctx context.Context, formats strfmt.Registry) error { if err := m.LogsInfo.ContextValidate(ctx, formats); err != nil { 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 c5909e0af54..85ac9e9aae6 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 @@ -104,6 +104,9 @@ const ( // FeatureSupportLevelIDSDNNETWORKTYPE captures enum value "SDN_NETWORK_TYPE" FeatureSupportLevelIDSDNNETWORKTYPE FeatureSupportLevelID = "SDN_NETWORK_TYPE" + + // FeatureSupportLevelIDEXTERNALLOADBALANCER captures enum value "EXTERNAL_LOAD_BALANCER" + FeatureSupportLevelIDEXTERNALLOADBALANCER FeatureSupportLevelID = "EXTERNAL_LOAD_BALANCER" ) // for schema @@ -111,7 +114,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","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"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["SNO","VIP_AUTO_ALLOC","CUSTOM_MANIFEST","SINGLE_NODE_EXPANSION","LVM","ODF","LSO","CNV","MCE","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","EXTERNAL_LOAD_BALANCER"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/api/vendor/github.com/openshift/assisted-service/models/load_balancer.go b/api/vendor/github.com/openshift/assisted-service/models/load_balancer.go new file mode 100644 index 00000000000..8d4a4fe21da --- /dev/null +++ b/api/vendor/github.com/openshift/assisted-service/models/load_balancer.go @@ -0,0 +1,114 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "encoding/json" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// LoadBalancer load balancer +// +// swagger:model load_balancer +type LoadBalancer struct { + + // Indicates if the load balancer will be managed by the cluster or by the user. This is optional and The + // default is `cluster-managed`. + // + // `cluster-managed` means that the cluster will start the components that assign the API and ingress VIPs to the + // nodes of the cluster automatically. + // + // `user-managed` means that the user is responsible for configuring an external load balancer and assign the + // API and ingress VIPs to it. Note that this configuration needs to be completed before starting the + // installation of the cluster, as it is needed during the installation process. + // + // Enum: [cluster-managed user-managed] + Type string `json:"type,omitempty" gorm:"not null;check:load_balancer_type in ('cluster-managed', 'user-managed');default:'cluster-managed'"` +} + +// Validate validates this load balancer +func (m *LoadBalancer) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateType(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +var loadBalancerTypeTypePropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["cluster-managed","user-managed"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + loadBalancerTypeTypePropEnum = append(loadBalancerTypeTypePropEnum, v) + } +} + +const ( + + // LoadBalancerTypeClusterManaged captures enum value "cluster-managed" + LoadBalancerTypeClusterManaged string = "cluster-managed" + + // LoadBalancerTypeUserManaged captures enum value "user-managed" + LoadBalancerTypeUserManaged string = "user-managed" +) + +// prop value enum +func (m *LoadBalancer) validateTypeEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, loadBalancerTypeTypePropEnum, true); err != nil { + return err + } + return nil +} + +func (m *LoadBalancer) validateType(formats strfmt.Registry) error { + if swag.IsZero(m.Type) { // not required + return nil + } + + // value enum + if err := m.validateTypeEnum("type", "body", m.Type); err != nil { + return err + } + + return nil +} + +// ContextValidate validates this load balancer based on context it is used +func (m *LoadBalancer) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *LoadBalancer) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *LoadBalancer) UnmarshalBinary(b []byte) error { + var res LoadBalancer + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/client/vendor/github.com/openshift/assisted-service/models/cluster.go b/client/vendor/github.com/openshift/assisted-service/models/cluster.go index 2ef415d2019..d7522a47680 100644 --- a/client/vendor/github.com/openshift/assisted-service/models/cluster.go +++ b/client/vendor/github.com/openshift/assisted-service/models/cluster.go @@ -171,6 +171,9 @@ type Cluster struct { // last installation preparation LastInstallationPreparation LastInstallationPreparation `json:"last-installation-preparation,omitempty" gorm:"embedded;embeddedPrefix:last_installation_preparation_"` + // load balancer + LoadBalancer *LoadBalancer `json:"load_balancer,omitempty" gorm:"embedded;embeddedPrefix:load_balancer_"` + // The progress of log collection or empty if logs are not applicable LogsInfo LogsState `json:"logs_info,omitempty" gorm:"type:varchar(2048)"` @@ -372,6 +375,10 @@ func (m *Cluster) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateLoadBalancer(formats); err != nil { + res = append(res, err) + } + if err := m.validateLogsInfo(formats); err != nil { res = append(res, err) } @@ -948,6 +955,25 @@ func (m *Cluster) validateLastInstallationPreparation(formats strfmt.Registry) e return nil } +func (m *Cluster) validateLoadBalancer(formats strfmt.Registry) error { + if swag.IsZero(m.LoadBalancer) { // not required + return nil + } + + if m.LoadBalancer != nil { + if err := m.LoadBalancer.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("load_balancer") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("load_balancer") + } + return err + } + } + + return nil +} + func (m *Cluster) validateLogsInfo(formats strfmt.Registry) error { if swag.IsZero(m.LogsInfo) { // not required return nil @@ -1302,6 +1328,10 @@ func (m *Cluster) ContextValidate(ctx context.Context, formats strfmt.Registry) res = append(res, err) } + if err := m.contextValidateLoadBalancer(ctx, formats); err != nil { + res = append(res, err) + } + if err := m.contextValidateLogsInfo(ctx, formats); err != nil { res = append(res, err) } @@ -1494,6 +1524,22 @@ func (m *Cluster) contextValidateLastInstallationPreparation(ctx context.Context return nil } +func (m *Cluster) contextValidateLoadBalancer(ctx context.Context, formats strfmt.Registry) error { + + if m.LoadBalancer != nil { + if err := m.LoadBalancer.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("load_balancer") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("load_balancer") + } + return err + } + } + + return nil +} + func (m *Cluster) contextValidateLogsInfo(ctx context.Context, formats strfmt.Registry) error { if err := m.LogsInfo.ContextValidate(ctx, formats); err != nil { 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 c5909e0af54..85ac9e9aae6 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 @@ -104,6 +104,9 @@ const ( // FeatureSupportLevelIDSDNNETWORKTYPE captures enum value "SDN_NETWORK_TYPE" FeatureSupportLevelIDSDNNETWORKTYPE FeatureSupportLevelID = "SDN_NETWORK_TYPE" + + // FeatureSupportLevelIDEXTERNALLOADBALANCER captures enum value "EXTERNAL_LOAD_BALANCER" + FeatureSupportLevelIDEXTERNALLOADBALANCER FeatureSupportLevelID = "EXTERNAL_LOAD_BALANCER" ) // for schema @@ -111,7 +114,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","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"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["SNO","VIP_AUTO_ALLOC","CUSTOM_MANIFEST","SINGLE_NODE_EXPANSION","LVM","ODF","LSO","CNV","MCE","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","EXTERNAL_LOAD_BALANCER"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/client/vendor/github.com/openshift/assisted-service/models/load_balancer.go b/client/vendor/github.com/openshift/assisted-service/models/load_balancer.go new file mode 100644 index 00000000000..8d4a4fe21da --- /dev/null +++ b/client/vendor/github.com/openshift/assisted-service/models/load_balancer.go @@ -0,0 +1,114 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "encoding/json" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// LoadBalancer load balancer +// +// swagger:model load_balancer +type LoadBalancer struct { + + // Indicates if the load balancer will be managed by the cluster or by the user. This is optional and The + // default is `cluster-managed`. + // + // `cluster-managed` means that the cluster will start the components that assign the API and ingress VIPs to the + // nodes of the cluster automatically. + // + // `user-managed` means that the user is responsible for configuring an external load balancer and assign the + // API and ingress VIPs to it. Note that this configuration needs to be completed before starting the + // installation of the cluster, as it is needed during the installation process. + // + // Enum: [cluster-managed user-managed] + Type string `json:"type,omitempty" gorm:"not null;check:load_balancer_type in ('cluster-managed', 'user-managed');default:'cluster-managed'"` +} + +// Validate validates this load balancer +func (m *LoadBalancer) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateType(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +var loadBalancerTypeTypePropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["cluster-managed","user-managed"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + loadBalancerTypeTypePropEnum = append(loadBalancerTypeTypePropEnum, v) + } +} + +const ( + + // LoadBalancerTypeClusterManaged captures enum value "cluster-managed" + LoadBalancerTypeClusterManaged string = "cluster-managed" + + // LoadBalancerTypeUserManaged captures enum value "user-managed" + LoadBalancerTypeUserManaged string = "user-managed" +) + +// prop value enum +func (m *LoadBalancer) validateTypeEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, loadBalancerTypeTypePropEnum, true); err != nil { + return err + } + return nil +} + +func (m *LoadBalancer) validateType(formats strfmt.Registry) error { + if swag.IsZero(m.Type) { // not required + return nil + } + + // value enum + if err := m.validateTypeEnum("type", "body", m.Type); err != nil { + return err + } + + return nil +} + +// ContextValidate validates this load balancer based on context it is used +func (m *LoadBalancer) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *LoadBalancer) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *LoadBalancer) UnmarshalBinary(b []byte) error { + var res LoadBalancer + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/internal/cluster/validator.go b/internal/cluster/validator.go index 553418ded5b..0b689374e05 100644 --- a/internal/cluster/validator.go +++ b/internal/cluster/validator.go @@ -231,6 +231,13 @@ func (v *clusterValidator) areVipsValid(c *clusterPreprocessContext, vipsWrapper return ValidationPending, fmt.Sprintf("%s virtual IPs are undefined.", vipsWrapper.Name()) } + // If the load balancer is managed by the user then the VIPs are required, but they don't need to be in the + // machine network, and they may already be in use, so we can't do any of the usual validations. + if c.cluster.LoadBalancer != nil && c.cluster.LoadBalancer.Type == models.LoadBalancerTypeUserManaged { + return ValidationSuccess, "Virtual IPs don't need to be validated because the load balancer is" + + " managed by the user" + } + machineCidrDefined, _ := v.isMachineCidrDefined(c) if !c.hasHostsWithInventories || !validationStatusToBool(machineCidrDefined) { return ValidationPending, "Hosts have not been discovered yet" diff --git a/internal/featuresupport/feature_support_level.go b/internal/featuresupport/feature_support_level.go index 254300f8673..964897f2ef1 100644 --- a/internal/featuresupport/feature_support_level.go +++ b/internal/featuresupport/feature_support_level.go @@ -36,12 +36,13 @@ var featuresList = map[models.FeatureSupportLevelID]SupportLevelFeature{ models.FeatureSupportLevelIDODF: (&OdfFeature{}).New(), // Platform features - models.FeatureSupportLevelIDNUTANIXINTEGRATION: (&NutanixIntegrationFeature{}).New(), - models.FeatureSupportLevelIDVSPHEREINTEGRATION: (&VsphereIntegrationFeature{}).New(), - models.FeatureSupportLevelIDEXTERNALPLATFORMOCI: (&OciIntegrationFeature{}).New(), - models.FeatureSupportLevelIDBAREMETALPLATFORM: (&BaremetalPlatformFeature{}).New(), - models.FeatureSupportLevelIDNONEPLATFORM: (&NonePlatformFeature{}).New(), - models.FeatureSupportLevelIDEXTERNALPLATFORM: (&ExternalPlatformFeature{}).New(), + models.FeatureSupportLevelIDNUTANIXINTEGRATION: (&NutanixIntegrationFeature{}).New(), + models.FeatureSupportLevelIDVSPHEREINTEGRATION: (&VsphereIntegrationFeature{}).New(), + models.FeatureSupportLevelIDEXTERNALPLATFORMOCI: (&OciIntegrationFeature{}).New(), + models.FeatureSupportLevelIDBAREMETALPLATFORM: (&BaremetalPlatformFeature{}).New(), + models.FeatureSupportLevelIDNONEPLATFORM: (&NonePlatformFeature{}).New(), + models.FeatureSupportLevelIDEXTERNALPLATFORM: (&ExternalPlatformFeature{}).New(), + models.FeatureSupportLevelIDEXTERNALLOADBALANCER: (&ExternalLoadBalancerFeature{}).New(), } func GetFeatureByID(featureID models.FeatureSupportLevelID) SupportLevelFeature { diff --git a/internal/featuresupport/feature_support_test.go b/internal/featuresupport/feature_support_test.go index 9b6dedfdf06..9334365ee71 100644 --- a/internal/featuresupport/feature_support_test.go +++ b/internal/featuresupport/feature_support_test.go @@ -276,12 +276,12 @@ var _ = Describe("V2ListFeatureSupportLevels API", func() { It("GetFeatureSupportList 4.12", func() { list := GetFeatureSupportList("4.12", nil, nil, nil) - Expect(len(list)).To(Equal(24)) + Expect(len(list)).To(Equal(25)) }) It("GetFeatureSupportList 4.13", func() { list := GetFeatureSupportList("4.13", nil, nil, nil) - Expect(len(list)).To(Equal(24)) + Expect(len(list)).To(Equal(25)) }) It("GetCpuArchitectureSupportList 4.12", func() { @@ -822,6 +822,39 @@ var _ = Describe("V2ListFeatureSupportLevels API", func() { }) }) + + DescribeTable( + "External load balancer support", + func(version string, expected bool) { + arch := "x86_64" + actual := IsFeatureAvailable( + models.FeatureSupportLevelIDEXTERNALLOADBALANCER, + version, + &arch, + ) + Expect(actual).To(Equal(expected)) + }, + Entry( + "Not in 4.14", + "4.14", + false, + ), + Entry( + "Not in 4.15", + "4.14", + false, + ), + Entry( + "Yes in 4.16", + "4.16", + true, + ), + Entry( + "Yes in 4.17", + "4.17", + true, + ), + ) }) func TestOperators(t *testing.T) { diff --git a/internal/featuresupport/features_platforms.go b/internal/featuresupport/features_platforms.go index f79ae1e5887..98e677c6bb7 100644 --- a/internal/featuresupport/features_platforms.go +++ b/internal/featuresupport/features_platforms.go @@ -350,3 +350,45 @@ func (feature *ExternalPlatformFeature) getFeatureActiveLevel(cluster *common.Cl return activeLevelNotActive } + +// ExternalLoadBalancerFeature +type ExternalLoadBalancerFeature struct{} + +func (feature *ExternalLoadBalancerFeature) New() SupportLevelFeature { + return &ExternalLoadBalancerFeature{} +} + +func (feature *ExternalLoadBalancerFeature) getId() models.FeatureSupportLevelID { + return models.FeatureSupportLevelIDEXTERNALLOADBALANCER +} + +func (feature *ExternalLoadBalancerFeature) GetName() string { + return "External Load Balancer" +} + +func (feature *ExternalLoadBalancerFeature) getSupportLevel(filters SupportLevelFilters) models.SupportLevel { + if isPlatformSet(filters) { + return "" + } + + if isNotSupported, err := common.BaseVersionLessThan("4.16", filters.OpenshiftVersion); isNotSupported || err != nil { + return models.SupportLevelUnavailable + } + + return models.SupportLevelSupported +} + +func (feature *ExternalLoadBalancerFeature) getIncompatibleFeatures(string) *[]models.FeatureSupportLevelID { + return nil +} + +func (feature *ExternalLoadBalancerFeature) getIncompatibleArchitectures(_ *string) *[]models.ArchitectureSupportLevelID { + return nil +} + +func (feature *ExternalLoadBalancerFeature) getFeatureActiveLevel(cluster *common.Cluster, _ *models.InfraEnv, clusterUpdateParams *models.V2ClusterUpdateParams, _ *models.InfraEnvUpdateParams) featureActiveLevel { + if cluster != nil && cluster.LoadBalancer != nil && cluster.LoadBalancer.Type == models.LoadBalancerTypeUserManaged { + return activeLevelActive + } + return activeLevelNotActive +} diff --git a/internal/installcfg/installcfg.go b/internal/installcfg/installcfg.go index 98d52091a96..99c8b5a389e 100644 --- a/internal/installcfg/installcfg.go +++ b/internal/installcfg/installcfg.go @@ -31,17 +31,18 @@ type Host struct { } type BareMetalInstallConfigPlatform struct { - ProvisioningNetwork string `json:"provisioningNetwork"` - APIVIPs []string `json:"apiVIPs,omitempty"` - DeprecatedAPIVIP string `json:"apiVIP,omitempty"` - IngressVIPs []string `json:"ingressVIPs,omitempty"` - DeprecatedIngressVIP string `json:"ingressVIP,omitempty"` - Hosts []Host `json:"hosts"` - ClusterOSImage string `json:"clusterOSImage,omitempty"` - ClusterProvisioningIP string `json:"clusterProvisioningIP,omitempty"` - ProvisioningNetworkInterface string `json:"provisioningNetworkInterface,omitempty"` - ProvisioningNetworkCIDR *string `json:"provisioningNetworkCIDR,omitempty"` - ProvisioningDHCPRange string `json:"provisioningDHCPRange,omitempty"` + ProvisioningNetwork string `json:"provisioningNetwork"` + APIVIPs []string `json:"apiVIPs,omitempty"` + DeprecatedAPIVIP string `json:"apiVIP,omitempty"` + IngressVIPs []string `json:"ingressVIPs,omitempty"` + DeprecatedIngressVIP string `json:"ingressVIP,omitempty"` + Hosts []Host `json:"hosts"` + ClusterOSImage string `json:"clusterOSImage,omitempty"` + ClusterProvisioningIP string `json:"clusterProvisioningIP,omitempty"` + ProvisioningNetworkInterface string `json:"provisioningNetworkInterface,omitempty"` + ProvisioningNetworkCIDR *string `json:"provisioningNetworkCIDR,omitempty"` + ProvisioningDHCPRange string `json:"provisioningDHCPRange,omitempty"` + LoadBalancer *configv1.BareMetalPlatformLoadBalancer `json:"loadBalancer,omitempty"` } type VsphereFailureDomainTopology struct { @@ -255,3 +256,7 @@ func (c *InstallerConfigBaremetal) Validate() error { return nil } + +type LoadBalancer struct { + Type string `json:"type,omitempty"` +} diff --git a/internal/provider/baremetal/installConfig.go b/internal/provider/baremetal/installConfig.go index 424023bfd96..ce600591566 100644 --- a/internal/provider/baremetal/installConfig.go +++ b/internal/provider/baremetal/installConfig.go @@ -2,9 +2,11 @@ package baremetal import ( "encoding/json" + "fmt" "sort" "github.com/go-openapi/swag" + configv1 "github.com/openshift/api/config/v1" "github.com/openshift/assisted-service/internal/common" "github.com/openshift/assisted-service/internal/featuresupport" "github.com/openshift/assisted-service/internal/host/hostutil" @@ -12,6 +14,7 @@ import ( "github.com/openshift/assisted-service/internal/network" "github.com/openshift/assisted-service/models" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) func (p baremetalProvider) AddPlatformToInstallConfig( @@ -111,5 +114,40 @@ func (p baremetalProvider) AddPlatformToInstallConfig( }, } } + + if cluster.LoadBalancer != nil { + switch cluster.LoadBalancer.Type { + case models.LoadBalancerTypeClusterManaged: + // Nothing, this is the default, we don't even need to check if the feature is available in + // this version of OpenShift. + case models.LoadBalancerTypeUserManaged: + featureAvailable := featuresupport.IsFeatureAvailable( + models.FeatureSupportLevelIDEXTERNALLOADBALANCER, + cluster.OpenshiftVersion, &cluster.CPUArchitecture, + ) + if !featureAvailable { + return fmt.Errorf( + "load balancer type is set to '%s', but the external load balancer feature is "+ + "not available for OpenShift version '%s' and arhitecture '%s'", + cluster.LoadBalancer.Type, cluster.OpenshiftVersion, cluster.CPUArchitecture, + ) + } + cfg.Platform.Baremetal.LoadBalancer = &configv1.BareMetalPlatformLoadBalancer{ + Type: configv1.LoadBalancerTypeUserManaged, + } + p.Log.WithFields(logrus.Fields{ + "cluster": cluster.ID, + }).Info("Enabled external load balancer") + default: + return fmt.Errorf( + "load balancer type is set to unsupported value '%s', supported values are "+ + "'%s' and '%s'", + cluster.LoadBalancer.Type, + models.LoadBalancerTypeClusterManaged, + models.LoadBalancerTypeUserManaged, + ) + } + } + return nil } diff --git a/models/cluster.go b/models/cluster.go index 2ef415d2019..d7522a47680 100644 --- a/models/cluster.go +++ b/models/cluster.go @@ -171,6 +171,9 @@ type Cluster struct { // last installation preparation LastInstallationPreparation LastInstallationPreparation `json:"last-installation-preparation,omitempty" gorm:"embedded;embeddedPrefix:last_installation_preparation_"` + // load balancer + LoadBalancer *LoadBalancer `json:"load_balancer,omitempty" gorm:"embedded;embeddedPrefix:load_balancer_"` + // The progress of log collection or empty if logs are not applicable LogsInfo LogsState `json:"logs_info,omitempty" gorm:"type:varchar(2048)"` @@ -372,6 +375,10 @@ func (m *Cluster) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateLoadBalancer(formats); err != nil { + res = append(res, err) + } + if err := m.validateLogsInfo(formats); err != nil { res = append(res, err) } @@ -948,6 +955,25 @@ func (m *Cluster) validateLastInstallationPreparation(formats strfmt.Registry) e return nil } +func (m *Cluster) validateLoadBalancer(formats strfmt.Registry) error { + if swag.IsZero(m.LoadBalancer) { // not required + return nil + } + + if m.LoadBalancer != nil { + if err := m.LoadBalancer.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("load_balancer") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("load_balancer") + } + return err + } + } + + return nil +} + func (m *Cluster) validateLogsInfo(formats strfmt.Registry) error { if swag.IsZero(m.LogsInfo) { // not required return nil @@ -1302,6 +1328,10 @@ func (m *Cluster) ContextValidate(ctx context.Context, formats strfmt.Registry) res = append(res, err) } + if err := m.contextValidateLoadBalancer(ctx, formats); err != nil { + res = append(res, err) + } + if err := m.contextValidateLogsInfo(ctx, formats); err != nil { res = append(res, err) } @@ -1494,6 +1524,22 @@ func (m *Cluster) contextValidateLastInstallationPreparation(ctx context.Context return nil } +func (m *Cluster) contextValidateLoadBalancer(ctx context.Context, formats strfmt.Registry) error { + + if m.LoadBalancer != nil { + if err := m.LoadBalancer.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("load_balancer") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("load_balancer") + } + return err + } + } + + return nil +} + func (m *Cluster) contextValidateLogsInfo(ctx context.Context, formats strfmt.Registry) error { if err := m.LogsInfo.ContextValidate(ctx, formats); err != nil { diff --git a/models/feature_support_level_id.go b/models/feature_support_level_id.go index c5909e0af54..85ac9e9aae6 100644 --- a/models/feature_support_level_id.go +++ b/models/feature_support_level_id.go @@ -104,6 +104,9 @@ const ( // FeatureSupportLevelIDSDNNETWORKTYPE captures enum value "SDN_NETWORK_TYPE" FeatureSupportLevelIDSDNNETWORKTYPE FeatureSupportLevelID = "SDN_NETWORK_TYPE" + + // FeatureSupportLevelIDEXTERNALLOADBALANCER captures enum value "EXTERNAL_LOAD_BALANCER" + FeatureSupportLevelIDEXTERNALLOADBALANCER FeatureSupportLevelID = "EXTERNAL_LOAD_BALANCER" ) // for schema @@ -111,7 +114,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","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"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["SNO","VIP_AUTO_ALLOC","CUSTOM_MANIFEST","SINGLE_NODE_EXPANSION","LVM","ODF","LSO","CNV","MCE","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","EXTERNAL_LOAD_BALANCER"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/models/load_balancer.go b/models/load_balancer.go new file mode 100644 index 00000000000..8d4a4fe21da --- /dev/null +++ b/models/load_balancer.go @@ -0,0 +1,114 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "encoding/json" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// LoadBalancer load balancer +// +// swagger:model load_balancer +type LoadBalancer struct { + + // Indicates if the load balancer will be managed by the cluster or by the user. This is optional and The + // default is `cluster-managed`. + // + // `cluster-managed` means that the cluster will start the components that assign the API and ingress VIPs to the + // nodes of the cluster automatically. + // + // `user-managed` means that the user is responsible for configuring an external load balancer and assign the + // API and ingress VIPs to it. Note that this configuration needs to be completed before starting the + // installation of the cluster, as it is needed during the installation process. + // + // Enum: [cluster-managed user-managed] + Type string `json:"type,omitempty" gorm:"not null;check:load_balancer_type in ('cluster-managed', 'user-managed');default:'cluster-managed'"` +} + +// Validate validates this load balancer +func (m *LoadBalancer) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateType(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +var loadBalancerTypeTypePropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["cluster-managed","user-managed"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + loadBalancerTypeTypePropEnum = append(loadBalancerTypeTypePropEnum, v) + } +} + +const ( + + // LoadBalancerTypeClusterManaged captures enum value "cluster-managed" + LoadBalancerTypeClusterManaged string = "cluster-managed" + + // LoadBalancerTypeUserManaged captures enum value "user-managed" + LoadBalancerTypeUserManaged string = "user-managed" +) + +// prop value enum +func (m *LoadBalancer) validateTypeEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, loadBalancerTypeTypePropEnum, true); err != nil { + return err + } + return nil +} + +func (m *LoadBalancer) validateType(formats strfmt.Registry) error { + if swag.IsZero(m.Type) { // not required + return nil + } + + // value enum + if err := m.validateTypeEnum("type", "body", m.Type); err != nil { + return err + } + + return nil +} + +// ContextValidate validates this load balancer based on context it is used +func (m *LoadBalancer) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *LoadBalancer) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *LoadBalancer) UnmarshalBinary(b []byte) error { + var res LoadBalancer + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/restapi/embedded_spec.go b/restapi/embedded_spec.go index abc95d19d5f..0b2c63449f1 100644 --- a/restapi/embedded_spec.go +++ b/restapi/embedded_spec.go @@ -6385,6 +6385,9 @@ func init() { "last-installation-preparation": { "$ref": "#/definitions/last-installation-preparation" }, + "load_balancer": { + "$ref": "#/definitions/load_balancer" + }, "logs_info": { "description": "The progress of log collection or empty if logs are not applicable", "$ref": "#/definitions/logs_state" @@ -7736,7 +7739,8 @@ func init() { "SKIP_MCO_REBOOT", "EXTERNAL_PLATFORM", "OVN_NETWORK_TYPE", - "SDN_NETWORK_TYPE" + "SDN_NETWORK_TYPE", + "EXTERNAL_LOAD_BALANCER" ] }, "finalizing-stage": { @@ -9261,6 +9265,21 @@ func init() { } } }, + "load_balancer": { + "type": "object", + "properties": { + "type": { + "description": "Indicates if the load balancer will be managed by the cluster or by the user. This is optional and The\ndefault is ` + "`" + `cluster-managed` + "`" + `.\n\n` + "`" + `cluster-managed` + "`" + ` means that the cluster will start the components that assign the API and ingress VIPs to the\nnodes of the cluster automatically.\n\n` + "`" + `user-managed` + "`" + ` means that the user is responsible for configuring an external load balancer and assign the\nAPI and ingress VIPs to it. Note that this configuration needs to be completed before starting the\ninstallation of the cluster, as it is needed during the installation process.\n", + "type": "string", + "enum": [ + "cluster-managed", + "user-managed" + ], + "x-go-custom-tag": "gorm:\"not null;check:load_balancer_type in ('cluster-managed', 'user-managed');default:'cluster-managed'\"" + } + }, + "x-go-custom-tag": "gorm:\"embedded;embeddedPrefix:load_balancer_\"" + }, "logs-progress-params": { "type": "object", "required": [ @@ -17209,6 +17228,9 @@ func init() { "last-installation-preparation": { "$ref": "#/definitions/last-installation-preparation" }, + "load_balancer": { + "$ref": "#/definitions/load_balancer" + }, "logs_info": { "description": "The progress of log collection or empty if logs are not applicable", "$ref": "#/definitions/logs_state" @@ -18527,7 +18549,8 @@ func init() { "SKIP_MCO_REBOOT", "EXTERNAL_PLATFORM", "OVN_NETWORK_TYPE", - "SDN_NETWORK_TYPE" + "SDN_NETWORK_TYPE", + "EXTERNAL_LOAD_BALANCER" ] }, "finalizing-stage": { @@ -20054,6 +20077,21 @@ func init() { } } }, + "load_balancer": { + "type": "object", + "properties": { + "type": { + "description": "Indicates if the load balancer will be managed by the cluster or by the user. This is optional and The\ndefault is ` + "`" + `cluster-managed` + "`" + `.\n\n` + "`" + `cluster-managed` + "`" + ` means that the cluster will start the components that assign the API and ingress VIPs to the\nnodes of the cluster automatically.\n\n` + "`" + `user-managed` + "`" + ` means that the user is responsible for configuring an external load balancer and assign the\nAPI and ingress VIPs to it. Note that this configuration needs to be completed before starting the\ninstallation of the cluster, as it is needed during the installation process.\n", + "type": "string", + "enum": [ + "cluster-managed", + "user-managed" + ], + "x-go-custom-tag": "gorm:\"not null;check:load_balancer_type in ('cluster-managed', 'user-managed');default:'cluster-managed'\"" + } + }, + "x-go-custom-tag": "gorm:\"embedded;embeddedPrefix:load_balancer_\"" + }, "logs-progress-params": { "type": "object", "required": [ diff --git a/swagger.yaml b/swagger.yaml index 85329da2b28..cd6010910bc 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -4144,6 +4144,7 @@ definitions: - 'EXTERNAL_PLATFORM' - 'OVN_NETWORK_TYPE' - 'SDN_NETWORK_TYPE' + - 'EXTERNAL_LOAD_BALANCER' architecture-support-level-id: type: string @@ -5450,7 +5451,8 @@ definitions: org_soft_timeouts_enabled: type: boolean description: Indication if organization soft timeouts is enabled for the cluster. - + load_balancer: + $ref: '#/definitions/load_balancer' last-installation-preparation: type: object @@ -7537,3 +7539,24 @@ definitions: type: string format: date-time description: Expiration time for the URL token. + + load_balancer: + type: object + x-go-custom-tag: gorm:"embedded;embeddedPrefix:load_balancer_" + properties: + type: + x-go-custom-tag: gorm:"not null;check:load_balancer_type in ('cluster-managed', 'user-managed');default:'cluster-managed'" + description: | + Indicates if the load balancer will be managed by the cluster or by the user. This is optional and The + default is `cluster-managed`. + + `cluster-managed` means that the cluster will start the components that assign the API and ingress VIPs to the + nodes of the cluster automatically. + + `user-managed` means that the user is responsible for configuring an external load balancer and assign the + API and ingress VIPs to it. Note that this configuration needs to be completed before starting the + installation of the cluster, as it is needed during the installation process. + type: string + enum: + - cluster-managed + - user-managed \ No newline at end of file diff --git a/vendor/github.com/openshift/assisted-service/models/cluster.go b/vendor/github.com/openshift/assisted-service/models/cluster.go index 2ef415d2019..d7522a47680 100644 --- a/vendor/github.com/openshift/assisted-service/models/cluster.go +++ b/vendor/github.com/openshift/assisted-service/models/cluster.go @@ -171,6 +171,9 @@ type Cluster struct { // last installation preparation LastInstallationPreparation LastInstallationPreparation `json:"last-installation-preparation,omitempty" gorm:"embedded;embeddedPrefix:last_installation_preparation_"` + // load balancer + LoadBalancer *LoadBalancer `json:"load_balancer,omitempty" gorm:"embedded;embeddedPrefix:load_balancer_"` + // The progress of log collection or empty if logs are not applicable LogsInfo LogsState `json:"logs_info,omitempty" gorm:"type:varchar(2048)"` @@ -372,6 +375,10 @@ func (m *Cluster) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateLoadBalancer(formats); err != nil { + res = append(res, err) + } + if err := m.validateLogsInfo(formats); err != nil { res = append(res, err) } @@ -948,6 +955,25 @@ func (m *Cluster) validateLastInstallationPreparation(formats strfmt.Registry) e return nil } +func (m *Cluster) validateLoadBalancer(formats strfmt.Registry) error { + if swag.IsZero(m.LoadBalancer) { // not required + return nil + } + + if m.LoadBalancer != nil { + if err := m.LoadBalancer.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("load_balancer") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("load_balancer") + } + return err + } + } + + return nil +} + func (m *Cluster) validateLogsInfo(formats strfmt.Registry) error { if swag.IsZero(m.LogsInfo) { // not required return nil @@ -1302,6 +1328,10 @@ func (m *Cluster) ContextValidate(ctx context.Context, formats strfmt.Registry) res = append(res, err) } + if err := m.contextValidateLoadBalancer(ctx, formats); err != nil { + res = append(res, err) + } + if err := m.contextValidateLogsInfo(ctx, formats); err != nil { res = append(res, err) } @@ -1494,6 +1524,22 @@ func (m *Cluster) contextValidateLastInstallationPreparation(ctx context.Context return nil } +func (m *Cluster) contextValidateLoadBalancer(ctx context.Context, formats strfmt.Registry) error { + + if m.LoadBalancer != nil { + if err := m.LoadBalancer.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("load_balancer") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("load_balancer") + } + return err + } + } + + return nil +} + func (m *Cluster) contextValidateLogsInfo(ctx context.Context, formats strfmt.Registry) error { if err := m.LogsInfo.ContextValidate(ctx, formats); err != nil { 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 c5909e0af54..85ac9e9aae6 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 @@ -104,6 +104,9 @@ const ( // FeatureSupportLevelIDSDNNETWORKTYPE captures enum value "SDN_NETWORK_TYPE" FeatureSupportLevelIDSDNNETWORKTYPE FeatureSupportLevelID = "SDN_NETWORK_TYPE" + + // FeatureSupportLevelIDEXTERNALLOADBALANCER captures enum value "EXTERNAL_LOAD_BALANCER" + FeatureSupportLevelIDEXTERNALLOADBALANCER FeatureSupportLevelID = "EXTERNAL_LOAD_BALANCER" ) // for schema @@ -111,7 +114,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","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"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["SNO","VIP_AUTO_ALLOC","CUSTOM_MANIFEST","SINGLE_NODE_EXPANSION","LVM","ODF","LSO","CNV","MCE","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","EXTERNAL_LOAD_BALANCER"]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/vendor/github.com/openshift/assisted-service/models/load_balancer.go b/vendor/github.com/openshift/assisted-service/models/load_balancer.go new file mode 100644 index 00000000000..8d4a4fe21da --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/models/load_balancer.go @@ -0,0 +1,114 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "encoding/json" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// LoadBalancer load balancer +// +// swagger:model load_balancer +type LoadBalancer struct { + + // Indicates if the load balancer will be managed by the cluster or by the user. This is optional and The + // default is `cluster-managed`. + // + // `cluster-managed` means that the cluster will start the components that assign the API and ingress VIPs to the + // nodes of the cluster automatically. + // + // `user-managed` means that the user is responsible for configuring an external load balancer and assign the + // API and ingress VIPs to it. Note that this configuration needs to be completed before starting the + // installation of the cluster, as it is needed during the installation process. + // + // Enum: [cluster-managed user-managed] + Type string `json:"type,omitempty" gorm:"not null;check:load_balancer_type in ('cluster-managed', 'user-managed');default:'cluster-managed'"` +} + +// Validate validates this load balancer +func (m *LoadBalancer) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateType(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +var loadBalancerTypeTypePropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["cluster-managed","user-managed"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + loadBalancerTypeTypePropEnum = append(loadBalancerTypeTypePropEnum, v) + } +} + +const ( + + // LoadBalancerTypeClusterManaged captures enum value "cluster-managed" + LoadBalancerTypeClusterManaged string = "cluster-managed" + + // LoadBalancerTypeUserManaged captures enum value "user-managed" + LoadBalancerTypeUserManaged string = "user-managed" +) + +// prop value enum +func (m *LoadBalancer) validateTypeEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, loadBalancerTypeTypePropEnum, true); err != nil { + return err + } + return nil +} + +func (m *LoadBalancer) validateType(formats strfmt.Registry) error { + if swag.IsZero(m.Type) { // not required + return nil + } + + // value enum + if err := m.validateTypeEnum("type", "body", m.Type); err != nil { + return err + } + + return nil +} + +// ContextValidate validates this load balancer based on context it is used +func (m *LoadBalancer) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *LoadBalancer) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *LoadBalancer) UnmarshalBinary(b []byte) error { + var res LoadBalancer + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +}