From 1ec428120751a3a7bfbe901eb62c5822231e7453 Mon Sep 17 00:00:00 2001 From: Dan Manor Date: Wed, 24 Jan 2024 15:19:10 +0200 Subject: [PATCH] MGMT-16262: Create a utility that fetches current OCP releases from OpenShift API and updates DB --- .../models/openshift_version.go | 7 +- .../assisted-service/models/release_image.go | 11 +- .../assisted-service/models/release_source.go | 136 ++ .../models/release_sources.go | 73 + .../models/upgrade_channel.go | 165 ++ .../models/openshift_version.go | 7 +- .../assisted-service/models/release_image.go | 11 +- .../assisted-service/models/release_source.go | 136 ++ .../models/release_sources.go | 73 + .../models/upgrade_channel.go | 165 ++ .../v2_list_release_sources_parameters.go | 128 ++ .../v2_list_release_sources_responses.go | 303 ++++ client/versions/versions_client.go | 28 + cmd/main.go | 57 +- deploy/assisted-service-configmap.yaml | 3 + deploy/podman/configmap.yml | 3 + internal/common/common_test.go | 74 + internal/common/db.go | 43 + internal/common/test_configuration.go | 6 +- internal/common/version.go | 22 + internal/releasesources/clients.go | 149 ++ internal/releasesources/clients_test.go | 957 +++++++++++ internal/releasesources/config.go | 10 + internal/releasesources/mock_clients.go | 87 + internal/releasesources/release_sources.go | 488 ++++++ .../releasesources/release_sources_test.go | 1399 +++++++++++++++++ internal/versions/api.go | 27 +- internal/versions/api_test.go | 132 +- models/openshift_version.go | 7 +- models/release_image.go | 11 +- models/release_source.go | 136 ++ models/release_sources.go | 73 + models/upgrade_channel.go | 165 ++ openshift/template.yaml | 14 + pkg/auth/auth_assisted_service_mock_test.go | 6 + restapi/configure_assisted_install.go | 8 + restapi/embedded_spec.go | 228 ++- restapi/operations/assisted_install_api.go | 12 + .../versions/v2_list_release_sources.go | 69 + .../v2_list_release_sources_parameters.go | 46 + .../v2_list_release_sources_responses.go | 197 +++ .../v2_list_release_sources_urlbuilder.go | 87 + swagger.yaml | 71 +- tools/deploy_assisted_installer_configmap.py | 6 + .../v2_list_release_sources_parameters.go | 128 ++ .../v2_list_release_sources_responses.go | 303 ++++ .../client/versions/versions_client.go | 28 + .../models/openshift_version.go | 7 +- .../assisted-service/models/release_image.go | 11 +- .../assisted-service/models/release_source.go | 136 ++ .../models/release_sources.go | 73 + .../models/upgrade_channel.go | 165 ++ 52 files changed, 6633 insertions(+), 54 deletions(-) create mode 100644 api/vendor/github.com/openshift/assisted-service/models/release_source.go create mode 100644 api/vendor/github.com/openshift/assisted-service/models/release_sources.go create mode 100644 api/vendor/github.com/openshift/assisted-service/models/upgrade_channel.go create mode 100644 client/vendor/github.com/openshift/assisted-service/models/release_source.go create mode 100644 client/vendor/github.com/openshift/assisted-service/models/release_sources.go create mode 100644 client/vendor/github.com/openshift/assisted-service/models/upgrade_channel.go create mode 100644 client/versions/v2_list_release_sources_parameters.go create mode 100644 client/versions/v2_list_release_sources_responses.go create mode 100644 internal/releasesources/clients.go create mode 100644 internal/releasesources/clients_test.go create mode 100644 internal/releasesources/config.go create mode 100644 internal/releasesources/mock_clients.go create mode 100644 internal/releasesources/release_sources.go create mode 100644 internal/releasesources/release_sources_test.go create mode 100644 models/release_source.go create mode 100644 models/release_sources.go create mode 100644 models/upgrade_channel.go create mode 100644 restapi/operations/versions/v2_list_release_sources.go create mode 100644 restapi/operations/versions/v2_list_release_sources_parameters.go create mode 100644 restapi/operations/versions/v2_list_release_sources_responses.go create mode 100644 restapi/operations/versions/v2_list_release_sources_urlbuilder.go create mode 100644 vendor/github.com/openshift/assisted-service/client/versions/v2_list_release_sources_parameters.go create mode 100644 vendor/github.com/openshift/assisted-service/client/versions/v2_list_release_sources_responses.go create mode 100644 vendor/github.com/openshift/assisted-service/models/release_source.go create mode 100644 vendor/github.com/openshift/assisted-service/models/release_sources.go create mode 100644 vendor/github.com/openshift/assisted-service/models/upgrade_channel.go diff --git a/api/vendor/github.com/openshift/assisted-service/models/openshift_version.go b/api/vendor/github.com/openshift/assisted-service/models/openshift_version.go index 2af83b7f0d4..34c46662d0a 100644 --- a/api/vendor/github.com/openshift/assisted-service/models/openshift_version.go +++ b/api/vendor/github.com/openshift/assisted-service/models/openshift_version.go @@ -33,7 +33,7 @@ type OpenshiftVersion struct { // Level of support of the version. // Required: true - // Enum: [beta production maintenance] + // Enum: [beta production maintenance end of life] SupportLevel *string `json:"support_level"` } @@ -81,7 +81,7 @@ var openshiftVersionTypeSupportLevelPropEnum []interface{} func init() { var res []string - if err := json.Unmarshal([]byte(`["beta","production","maintenance"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["beta","production","maintenance","end of life"]`), &res); err != nil { panic(err) } for _, v := range res { @@ -99,6 +99,9 @@ const ( // OpenshiftVersionSupportLevelMaintenance captures enum value "maintenance" OpenshiftVersionSupportLevelMaintenance string = "maintenance" + + // OpenshiftVersionSupportLevelEndOfLife captures enum value "end of life" + OpenshiftVersionSupportLevelEndOfLife string = "end of life" ) // prop value enum diff --git a/api/vendor/github.com/openshift/assisted-service/models/release_image.go b/api/vendor/github.com/openshift/assisted-service/models/release_image.go index c1f0861d9ef..e87ed885519 100644 --- a/api/vendor/github.com/openshift/assisted-service/models/release_image.go +++ b/api/vendor/github.com/openshift/assisted-service/models/release_image.go @@ -26,7 +26,7 @@ type ReleaseImage struct { CPUArchitecture *string `json:"cpu_architecture" gorm:"default:'x86_64'"` // List of CPU architectures provided by the image. - CPUArchitectures []string `json:"cpu_architectures"` + CPUArchitectures []string `json:"cpu_architectures" gorm:"type:text[]"` // Indication that the version is the recommended one. Default bool `json:"default,omitempty"` @@ -36,12 +36,12 @@ type ReleaseImage struct { OpenshiftVersion *string `json:"openshift_version"` // Level of support of the version. - // Enum: [beta production maintenance] + // Enum: [beta production maintenance end of life] SupportLevel string `json:"support_level,omitempty"` // The installation image of the OpenShift cluster. // Required: true - URL *string `json:"url"` + URL *string `json:"url" gorm:"primarykey"` // OCP version from the release metadata. // Required: true @@ -146,7 +146,7 @@ var releaseImageTypeSupportLevelPropEnum []interface{} func init() { var res []string - if err := json.Unmarshal([]byte(`["beta","production","maintenance"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["beta","production","maintenance","end of life"]`), &res); err != nil { panic(err) } for _, v := range res { @@ -164,6 +164,9 @@ const ( // ReleaseImageSupportLevelMaintenance captures enum value "maintenance" ReleaseImageSupportLevelMaintenance string = "maintenance" + + // ReleaseImageSupportLevelEndOfLife captures enum value "end of life" + ReleaseImageSupportLevelEndOfLife string = "end of life" ) // prop value enum diff --git a/api/vendor/github.com/openshift/assisted-service/models/release_source.go b/api/vendor/github.com/openshift/assisted-service/models/release_source.go new file mode 100644 index 00000000000..44dbf733bef --- /dev/null +++ b/api/vendor/github.com/openshift/assisted-service/models/release_source.go @@ -0,0 +1,136 @@ +// 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" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// ReleaseSource release source +// +// swagger:model release-source +type ReleaseSource struct { + + // Version of the OpenShift cluster. + // Required: true + OpenshiftVersion *string `json:"openshift_version"` + + // upgrade channels + // Required: true + UpgradeChannels []*UpgradeChannel `json:"upgrade_channels"` +} + +// Validate validates this release source +func (m *ReleaseSource) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateOpenshiftVersion(formats); err != nil { + res = append(res, err) + } + + if err := m.validateUpgradeChannels(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *ReleaseSource) validateOpenshiftVersion(formats strfmt.Registry) error { + + if err := validate.Required("openshift_version", "body", m.OpenshiftVersion); err != nil { + return err + } + + return nil +} + +func (m *ReleaseSource) validateUpgradeChannels(formats strfmt.Registry) error { + + if err := validate.Required("upgrade_channels", "body", m.UpgradeChannels); err != nil { + return err + } + + for i := 0; i < len(m.UpgradeChannels); i++ { + if swag.IsZero(m.UpgradeChannels[i]) { // not required + continue + } + + if m.UpgradeChannels[i] != nil { + if err := m.UpgradeChannels[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("upgrade_channels" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("upgrade_channels" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// ContextValidate validate this release source based on the context it is used +func (m *ReleaseSource) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateUpgradeChannels(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *ReleaseSource) contextValidateUpgradeChannels(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.UpgradeChannels); i++ { + + if m.UpgradeChannels[i] != nil { + if err := m.UpgradeChannels[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("upgrade_channels" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("upgrade_channels" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// MarshalBinary interface implementation +func (m *ReleaseSource) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *ReleaseSource) UnmarshalBinary(b []byte) error { + var res ReleaseSource + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/api/vendor/github.com/openshift/assisted-service/models/release_sources.go b/api/vendor/github.com/openshift/assisted-service/models/release_sources.go new file mode 100644 index 00000000000..c2e51cd789a --- /dev/null +++ b/api/vendor/github.com/openshift/assisted-service/models/release_sources.go @@ -0,0 +1,73 @@ +// 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" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// ReleaseSources release sources +// +// swagger:model release-sources +type ReleaseSources []*ReleaseSource + +// Validate validates this release sources +func (m ReleaseSources) Validate(formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + if swag.IsZero(m[i]) { // not required + continue + } + + if m[i] != nil { + if err := m[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// ContextValidate validate this release sources based on the context it is used +func (m ReleaseSources) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + + if m[i] != nil { + if err := m[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/api/vendor/github.com/openshift/assisted-service/models/upgrade_channel.go b/api/vendor/github.com/openshift/assisted-service/models/upgrade_channel.go new file mode 100644 index 00000000000..299d1d6f2c9 --- /dev/null +++ b/api/vendor/github.com/openshift/assisted-service/models/upgrade_channel.go @@ -0,0 +1,165 @@ +// 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" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// UpgradeChannel upgrade channel +// +// swagger:model upgrade-channel +type UpgradeChannel struct { + + // channels + // Required: true + Channels []string `json:"channels"` + + // The CPU architecture of the image. + // Required: true + // Enum: [x86_64 aarch64 arm64 ppc64le s390x multi] + CPUArchitecture *string `json:"cpu_architecture" gorm:"default:'x86_64'"` +} + +// Validate validates this upgrade channel +func (m *UpgradeChannel) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateChannels(formats); err != nil { + res = append(res, err) + } + + if err := m.validateCPUArchitecture(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +var upgradeChannelChannelsItemsEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["stable","candidate"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + upgradeChannelChannelsItemsEnum = append(upgradeChannelChannelsItemsEnum, v) + } +} + +func (m *UpgradeChannel) validateChannelsItemsEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, upgradeChannelChannelsItemsEnum, true); err != nil { + return err + } + return nil +} + +func (m *UpgradeChannel) validateChannels(formats strfmt.Registry) error { + + if err := validate.Required("channels", "body", m.Channels); err != nil { + return err + } + + for i := 0; i < len(m.Channels); i++ { + + // value enum + if err := m.validateChannelsItemsEnum("channels"+"."+strconv.Itoa(i), "body", m.Channels[i]); err != nil { + return err + } + + } + + return nil +} + +var upgradeChannelTypeCPUArchitecturePropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["x86_64","aarch64","arm64","ppc64le","s390x","multi"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + upgradeChannelTypeCPUArchitecturePropEnum = append(upgradeChannelTypeCPUArchitecturePropEnum, v) + } +} + +const ( + + // UpgradeChannelCPUArchitectureX8664 captures enum value "x86_64" + UpgradeChannelCPUArchitectureX8664 string = "x86_64" + + // UpgradeChannelCPUArchitectureAarch64 captures enum value "aarch64" + UpgradeChannelCPUArchitectureAarch64 string = "aarch64" + + // UpgradeChannelCPUArchitectureArm64 captures enum value "arm64" + UpgradeChannelCPUArchitectureArm64 string = "arm64" + + // UpgradeChannelCPUArchitecturePpc64le captures enum value "ppc64le" + UpgradeChannelCPUArchitecturePpc64le string = "ppc64le" + + // UpgradeChannelCPUArchitectureS390x captures enum value "s390x" + UpgradeChannelCPUArchitectureS390x string = "s390x" + + // UpgradeChannelCPUArchitectureMulti captures enum value "multi" + UpgradeChannelCPUArchitectureMulti string = "multi" +) + +// prop value enum +func (m *UpgradeChannel) validateCPUArchitectureEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, upgradeChannelTypeCPUArchitecturePropEnum, true); err != nil { + return err + } + return nil +} + +func (m *UpgradeChannel) validateCPUArchitecture(formats strfmt.Registry) error { + + if err := validate.Required("cpu_architecture", "body", m.CPUArchitecture); err != nil { + return err + } + + // value enum + if err := m.validateCPUArchitectureEnum("cpu_architecture", "body", *m.CPUArchitecture); err != nil { + return err + } + + return nil +} + +// ContextValidate validates this upgrade channel based on context it is used +func (m *UpgradeChannel) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *UpgradeChannel) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *UpgradeChannel) UnmarshalBinary(b []byte) error { + var res UpgradeChannel + 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/openshift_version.go b/client/vendor/github.com/openshift/assisted-service/models/openshift_version.go index 2af83b7f0d4..34c46662d0a 100644 --- a/client/vendor/github.com/openshift/assisted-service/models/openshift_version.go +++ b/client/vendor/github.com/openshift/assisted-service/models/openshift_version.go @@ -33,7 +33,7 @@ type OpenshiftVersion struct { // Level of support of the version. // Required: true - // Enum: [beta production maintenance] + // Enum: [beta production maintenance end of life] SupportLevel *string `json:"support_level"` } @@ -81,7 +81,7 @@ var openshiftVersionTypeSupportLevelPropEnum []interface{} func init() { var res []string - if err := json.Unmarshal([]byte(`["beta","production","maintenance"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["beta","production","maintenance","end of life"]`), &res); err != nil { panic(err) } for _, v := range res { @@ -99,6 +99,9 @@ const ( // OpenshiftVersionSupportLevelMaintenance captures enum value "maintenance" OpenshiftVersionSupportLevelMaintenance string = "maintenance" + + // OpenshiftVersionSupportLevelEndOfLife captures enum value "end of life" + OpenshiftVersionSupportLevelEndOfLife string = "end of life" ) // prop value enum diff --git a/client/vendor/github.com/openshift/assisted-service/models/release_image.go b/client/vendor/github.com/openshift/assisted-service/models/release_image.go index c1f0861d9ef..e87ed885519 100644 --- a/client/vendor/github.com/openshift/assisted-service/models/release_image.go +++ b/client/vendor/github.com/openshift/assisted-service/models/release_image.go @@ -26,7 +26,7 @@ type ReleaseImage struct { CPUArchitecture *string `json:"cpu_architecture" gorm:"default:'x86_64'"` // List of CPU architectures provided by the image. - CPUArchitectures []string `json:"cpu_architectures"` + CPUArchitectures []string `json:"cpu_architectures" gorm:"type:text[]"` // Indication that the version is the recommended one. Default bool `json:"default,omitempty"` @@ -36,12 +36,12 @@ type ReleaseImage struct { OpenshiftVersion *string `json:"openshift_version"` // Level of support of the version. - // Enum: [beta production maintenance] + // Enum: [beta production maintenance end of life] SupportLevel string `json:"support_level,omitempty"` // The installation image of the OpenShift cluster. // Required: true - URL *string `json:"url"` + URL *string `json:"url" gorm:"primarykey"` // OCP version from the release metadata. // Required: true @@ -146,7 +146,7 @@ var releaseImageTypeSupportLevelPropEnum []interface{} func init() { var res []string - if err := json.Unmarshal([]byte(`["beta","production","maintenance"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["beta","production","maintenance","end of life"]`), &res); err != nil { panic(err) } for _, v := range res { @@ -164,6 +164,9 @@ const ( // ReleaseImageSupportLevelMaintenance captures enum value "maintenance" ReleaseImageSupportLevelMaintenance string = "maintenance" + + // ReleaseImageSupportLevelEndOfLife captures enum value "end of life" + ReleaseImageSupportLevelEndOfLife string = "end of life" ) // prop value enum diff --git a/client/vendor/github.com/openshift/assisted-service/models/release_source.go b/client/vendor/github.com/openshift/assisted-service/models/release_source.go new file mode 100644 index 00000000000..44dbf733bef --- /dev/null +++ b/client/vendor/github.com/openshift/assisted-service/models/release_source.go @@ -0,0 +1,136 @@ +// 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" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// ReleaseSource release source +// +// swagger:model release-source +type ReleaseSource struct { + + // Version of the OpenShift cluster. + // Required: true + OpenshiftVersion *string `json:"openshift_version"` + + // upgrade channels + // Required: true + UpgradeChannels []*UpgradeChannel `json:"upgrade_channels"` +} + +// Validate validates this release source +func (m *ReleaseSource) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateOpenshiftVersion(formats); err != nil { + res = append(res, err) + } + + if err := m.validateUpgradeChannels(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *ReleaseSource) validateOpenshiftVersion(formats strfmt.Registry) error { + + if err := validate.Required("openshift_version", "body", m.OpenshiftVersion); err != nil { + return err + } + + return nil +} + +func (m *ReleaseSource) validateUpgradeChannels(formats strfmt.Registry) error { + + if err := validate.Required("upgrade_channels", "body", m.UpgradeChannels); err != nil { + return err + } + + for i := 0; i < len(m.UpgradeChannels); i++ { + if swag.IsZero(m.UpgradeChannels[i]) { // not required + continue + } + + if m.UpgradeChannels[i] != nil { + if err := m.UpgradeChannels[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("upgrade_channels" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("upgrade_channels" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// ContextValidate validate this release source based on the context it is used +func (m *ReleaseSource) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateUpgradeChannels(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *ReleaseSource) contextValidateUpgradeChannels(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.UpgradeChannels); i++ { + + if m.UpgradeChannels[i] != nil { + if err := m.UpgradeChannels[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("upgrade_channels" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("upgrade_channels" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// MarshalBinary interface implementation +func (m *ReleaseSource) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *ReleaseSource) UnmarshalBinary(b []byte) error { + var res ReleaseSource + 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/release_sources.go b/client/vendor/github.com/openshift/assisted-service/models/release_sources.go new file mode 100644 index 00000000000..c2e51cd789a --- /dev/null +++ b/client/vendor/github.com/openshift/assisted-service/models/release_sources.go @@ -0,0 +1,73 @@ +// 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" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// ReleaseSources release sources +// +// swagger:model release-sources +type ReleaseSources []*ReleaseSource + +// Validate validates this release sources +func (m ReleaseSources) Validate(formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + if swag.IsZero(m[i]) { // not required + continue + } + + if m[i] != nil { + if err := m[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// ContextValidate validate this release sources based on the context it is used +func (m ReleaseSources) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + + if m[i] != nil { + if err := m[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/client/vendor/github.com/openshift/assisted-service/models/upgrade_channel.go b/client/vendor/github.com/openshift/assisted-service/models/upgrade_channel.go new file mode 100644 index 00000000000..299d1d6f2c9 --- /dev/null +++ b/client/vendor/github.com/openshift/assisted-service/models/upgrade_channel.go @@ -0,0 +1,165 @@ +// 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" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// UpgradeChannel upgrade channel +// +// swagger:model upgrade-channel +type UpgradeChannel struct { + + // channels + // Required: true + Channels []string `json:"channels"` + + // The CPU architecture of the image. + // Required: true + // Enum: [x86_64 aarch64 arm64 ppc64le s390x multi] + CPUArchitecture *string `json:"cpu_architecture" gorm:"default:'x86_64'"` +} + +// Validate validates this upgrade channel +func (m *UpgradeChannel) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateChannels(formats); err != nil { + res = append(res, err) + } + + if err := m.validateCPUArchitecture(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +var upgradeChannelChannelsItemsEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["stable","candidate"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + upgradeChannelChannelsItemsEnum = append(upgradeChannelChannelsItemsEnum, v) + } +} + +func (m *UpgradeChannel) validateChannelsItemsEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, upgradeChannelChannelsItemsEnum, true); err != nil { + return err + } + return nil +} + +func (m *UpgradeChannel) validateChannels(formats strfmt.Registry) error { + + if err := validate.Required("channels", "body", m.Channels); err != nil { + return err + } + + for i := 0; i < len(m.Channels); i++ { + + // value enum + if err := m.validateChannelsItemsEnum("channels"+"."+strconv.Itoa(i), "body", m.Channels[i]); err != nil { + return err + } + + } + + return nil +} + +var upgradeChannelTypeCPUArchitecturePropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["x86_64","aarch64","arm64","ppc64le","s390x","multi"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + upgradeChannelTypeCPUArchitecturePropEnum = append(upgradeChannelTypeCPUArchitecturePropEnum, v) + } +} + +const ( + + // UpgradeChannelCPUArchitectureX8664 captures enum value "x86_64" + UpgradeChannelCPUArchitectureX8664 string = "x86_64" + + // UpgradeChannelCPUArchitectureAarch64 captures enum value "aarch64" + UpgradeChannelCPUArchitectureAarch64 string = "aarch64" + + // UpgradeChannelCPUArchitectureArm64 captures enum value "arm64" + UpgradeChannelCPUArchitectureArm64 string = "arm64" + + // UpgradeChannelCPUArchitecturePpc64le captures enum value "ppc64le" + UpgradeChannelCPUArchitecturePpc64le string = "ppc64le" + + // UpgradeChannelCPUArchitectureS390x captures enum value "s390x" + UpgradeChannelCPUArchitectureS390x string = "s390x" + + // UpgradeChannelCPUArchitectureMulti captures enum value "multi" + UpgradeChannelCPUArchitectureMulti string = "multi" +) + +// prop value enum +func (m *UpgradeChannel) validateCPUArchitectureEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, upgradeChannelTypeCPUArchitecturePropEnum, true); err != nil { + return err + } + return nil +} + +func (m *UpgradeChannel) validateCPUArchitecture(formats strfmt.Registry) error { + + if err := validate.Required("cpu_architecture", "body", m.CPUArchitecture); err != nil { + return err + } + + // value enum + if err := m.validateCPUArchitectureEnum("cpu_architecture", "body", *m.CPUArchitecture); err != nil { + return err + } + + return nil +} + +// ContextValidate validates this upgrade channel based on context it is used +func (m *UpgradeChannel) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *UpgradeChannel) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *UpgradeChannel) UnmarshalBinary(b []byte) error { + var res UpgradeChannel + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/client/versions/v2_list_release_sources_parameters.go b/client/versions/v2_list_release_sources_parameters.go new file mode 100644 index 00000000000..f80a78d7927 --- /dev/null +++ b/client/versions/v2_list_release_sources_parameters.go @@ -0,0 +1,128 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package versions + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewV2ListReleaseSourcesParams creates a new V2ListReleaseSourcesParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewV2ListReleaseSourcesParams() *V2ListReleaseSourcesParams { + return &V2ListReleaseSourcesParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewV2ListReleaseSourcesParamsWithTimeout creates a new V2ListReleaseSourcesParams object +// with the ability to set a timeout on a request. +func NewV2ListReleaseSourcesParamsWithTimeout(timeout time.Duration) *V2ListReleaseSourcesParams { + return &V2ListReleaseSourcesParams{ + timeout: timeout, + } +} + +// NewV2ListReleaseSourcesParamsWithContext creates a new V2ListReleaseSourcesParams object +// with the ability to set a context for a request. +func NewV2ListReleaseSourcesParamsWithContext(ctx context.Context) *V2ListReleaseSourcesParams { + return &V2ListReleaseSourcesParams{ + Context: ctx, + } +} + +// NewV2ListReleaseSourcesParamsWithHTTPClient creates a new V2ListReleaseSourcesParams object +// with the ability to set a custom HTTPClient for a request. +func NewV2ListReleaseSourcesParamsWithHTTPClient(client *http.Client) *V2ListReleaseSourcesParams { + return &V2ListReleaseSourcesParams{ + HTTPClient: client, + } +} + +/* +V2ListReleaseSourcesParams contains all the parameters to send to the API endpoint + + for the v2 list release sources operation. + + Typically these are written to a http.Request. +*/ +type V2ListReleaseSourcesParams struct { + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the v2 list release sources params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *V2ListReleaseSourcesParams) WithDefaults() *V2ListReleaseSourcesParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the v2 list release sources params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *V2ListReleaseSourcesParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the v2 list release sources params +func (o *V2ListReleaseSourcesParams) WithTimeout(timeout time.Duration) *V2ListReleaseSourcesParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the v2 list release sources params +func (o *V2ListReleaseSourcesParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the v2 list release sources params +func (o *V2ListReleaseSourcesParams) WithContext(ctx context.Context) *V2ListReleaseSourcesParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the v2 list release sources params +func (o *V2ListReleaseSourcesParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the v2 list release sources params +func (o *V2ListReleaseSourcesParams) WithHTTPClient(client *http.Client) *V2ListReleaseSourcesParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the v2 list release sources params +func (o *V2ListReleaseSourcesParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WriteToRequest writes these params to a swagger request +func (o *V2ListReleaseSourcesParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/client/versions/v2_list_release_sources_responses.go b/client/versions/v2_list_release_sources_responses.go new file mode 100644 index 00000000000..77819c20c59 --- /dev/null +++ b/client/versions/v2_list_release_sources_responses.go @@ -0,0 +1,303 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package versions + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + "github.com/openshift/assisted-service/models" +) + +// V2ListReleaseSourcesReader is a Reader for the V2ListReleaseSources structure. +type V2ListReleaseSourcesReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *V2ListReleaseSourcesReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewV2ListReleaseSourcesOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + case 400: + result := NewV2ListReleaseSourcesBadRequest() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 500: + result := NewV2ListReleaseSourcesInternalServerError() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 503: + result := NewV2ListReleaseSourcesServiceUnavailable() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + default: + return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + } +} + +// NewV2ListReleaseSourcesOK creates a V2ListReleaseSourcesOK with default headers values +func NewV2ListReleaseSourcesOK() *V2ListReleaseSourcesOK { + return &V2ListReleaseSourcesOK{} +} + +/* +V2ListReleaseSourcesOK describes a response with status code 200, with default header values. + +Success. +*/ +type V2ListReleaseSourcesOK struct { + Payload models.ReleaseSources +} + +// IsSuccess returns true when this v2 list release sources o k response has a 2xx status code +func (o *V2ListReleaseSourcesOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this v2 list release sources o k response has a 3xx status code +func (o *V2ListReleaseSourcesOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 list release sources o k response has a 4xx status code +func (o *V2ListReleaseSourcesOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this v2 list release sources o k response has a 5xx status code +func (o *V2ListReleaseSourcesOK) IsServerError() bool { + return false +} + +// IsCode returns true when this v2 list release sources o k response a status code equal to that given +func (o *V2ListReleaseSourcesOK) IsCode(code int) bool { + return code == 200 +} + +func (o *V2ListReleaseSourcesOK) Error() string { + return fmt.Sprintf("[GET /v2/release-sources][%d] v2ListReleaseSourcesOK %+v", 200, o.Payload) +} + +func (o *V2ListReleaseSourcesOK) String() string { + return fmt.Sprintf("[GET /v2/release-sources][%d] v2ListReleaseSourcesOK %+v", 200, o.Payload) +} + +func (o *V2ListReleaseSourcesOK) GetPayload() models.ReleaseSources { + return o.Payload +} + +func (o *V2ListReleaseSourcesOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewV2ListReleaseSourcesBadRequest creates a V2ListReleaseSourcesBadRequest with default headers values +func NewV2ListReleaseSourcesBadRequest() *V2ListReleaseSourcesBadRequest { + return &V2ListReleaseSourcesBadRequest{} +} + +/* +V2ListReleaseSourcesBadRequest describes a response with status code 400, with default header values. + +Bad Request +*/ +type V2ListReleaseSourcesBadRequest struct { + Payload *models.Error +} + +// IsSuccess returns true when this v2 list release sources bad request response has a 2xx status code +func (o *V2ListReleaseSourcesBadRequest) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this v2 list release sources bad request response has a 3xx status code +func (o *V2ListReleaseSourcesBadRequest) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 list release sources bad request response has a 4xx status code +func (o *V2ListReleaseSourcesBadRequest) IsClientError() bool { + return true +} + +// IsServerError returns true when this v2 list release sources bad request response has a 5xx status code +func (o *V2ListReleaseSourcesBadRequest) IsServerError() bool { + return false +} + +// IsCode returns true when this v2 list release sources bad request response a status code equal to that given +func (o *V2ListReleaseSourcesBadRequest) IsCode(code int) bool { + return code == 400 +} + +func (o *V2ListReleaseSourcesBadRequest) Error() string { + return fmt.Sprintf("[GET /v2/release-sources][%d] v2ListReleaseSourcesBadRequest %+v", 400, o.Payload) +} + +func (o *V2ListReleaseSourcesBadRequest) String() string { + return fmt.Sprintf("[GET /v2/release-sources][%d] v2ListReleaseSourcesBadRequest %+v", 400, o.Payload) +} + +func (o *V2ListReleaseSourcesBadRequest) GetPayload() *models.Error { + return o.Payload +} + +func (o *V2ListReleaseSourcesBadRequest) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewV2ListReleaseSourcesInternalServerError creates a V2ListReleaseSourcesInternalServerError with default headers values +func NewV2ListReleaseSourcesInternalServerError() *V2ListReleaseSourcesInternalServerError { + return &V2ListReleaseSourcesInternalServerError{} +} + +/* +V2ListReleaseSourcesInternalServerError describes a response with status code 500, with default header values. + +Error. +*/ +type V2ListReleaseSourcesInternalServerError struct { + Payload *models.Error +} + +// IsSuccess returns true when this v2 list release sources internal server error response has a 2xx status code +func (o *V2ListReleaseSourcesInternalServerError) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this v2 list release sources internal server error response has a 3xx status code +func (o *V2ListReleaseSourcesInternalServerError) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 list release sources internal server error response has a 4xx status code +func (o *V2ListReleaseSourcesInternalServerError) IsClientError() bool { + return false +} + +// IsServerError returns true when this v2 list release sources internal server error response has a 5xx status code +func (o *V2ListReleaseSourcesInternalServerError) IsServerError() bool { + return true +} + +// IsCode returns true when this v2 list release sources internal server error response a status code equal to that given +func (o *V2ListReleaseSourcesInternalServerError) IsCode(code int) bool { + return code == 500 +} + +func (o *V2ListReleaseSourcesInternalServerError) Error() string { + return fmt.Sprintf("[GET /v2/release-sources][%d] v2ListReleaseSourcesInternalServerError %+v", 500, o.Payload) +} + +func (o *V2ListReleaseSourcesInternalServerError) String() string { + return fmt.Sprintf("[GET /v2/release-sources][%d] v2ListReleaseSourcesInternalServerError %+v", 500, o.Payload) +} + +func (o *V2ListReleaseSourcesInternalServerError) GetPayload() *models.Error { + return o.Payload +} + +func (o *V2ListReleaseSourcesInternalServerError) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewV2ListReleaseSourcesServiceUnavailable creates a V2ListReleaseSourcesServiceUnavailable with default headers values +func NewV2ListReleaseSourcesServiceUnavailable() *V2ListReleaseSourcesServiceUnavailable { + return &V2ListReleaseSourcesServiceUnavailable{} +} + +/* +V2ListReleaseSourcesServiceUnavailable describes a response with status code 503, with default header values. + +Unavailable. +*/ +type V2ListReleaseSourcesServiceUnavailable struct { + Payload *models.Error +} + +// IsSuccess returns true when this v2 list release sources service unavailable response has a 2xx status code +func (o *V2ListReleaseSourcesServiceUnavailable) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this v2 list release sources service unavailable response has a 3xx status code +func (o *V2ListReleaseSourcesServiceUnavailable) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 list release sources service unavailable response has a 4xx status code +func (o *V2ListReleaseSourcesServiceUnavailable) IsClientError() bool { + return false +} + +// IsServerError returns true when this v2 list release sources service unavailable response has a 5xx status code +func (o *V2ListReleaseSourcesServiceUnavailable) IsServerError() bool { + return true +} + +// IsCode returns true when this v2 list release sources service unavailable response a status code equal to that given +func (o *V2ListReleaseSourcesServiceUnavailable) IsCode(code int) bool { + return code == 503 +} + +func (o *V2ListReleaseSourcesServiceUnavailable) Error() string { + return fmt.Sprintf("[GET /v2/release-sources][%d] v2ListReleaseSourcesServiceUnavailable %+v", 503, o.Payload) +} + +func (o *V2ListReleaseSourcesServiceUnavailable) String() string { + return fmt.Sprintf("[GET /v2/release-sources][%d] v2ListReleaseSourcesServiceUnavailable %+v", 503, o.Payload) +} + +func (o *V2ListReleaseSourcesServiceUnavailable) GetPayload() *models.Error { + return o.Payload +} + +func (o *V2ListReleaseSourcesServiceUnavailable) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/client/versions/versions_client.go b/client/versions/versions_client.go index 6365c153f5a..42947354c21 100644 --- a/client/versions/versions_client.go +++ b/client/versions/versions_client.go @@ -20,6 +20,9 @@ type API interface { /* V2ListComponentVersions List of component versions.*/ V2ListComponentVersions(ctx context.Context, params *V2ListComponentVersionsParams) (*V2ListComponentVersionsOK, error) + /* + V2ListReleaseSources Retrieves openshift release sources configuration.*/ + V2ListReleaseSources(ctx context.Context, params *V2ListReleaseSourcesParams) (*V2ListReleaseSourcesOK, error) /* V2ListSupportedOpenshiftVersions Retrieves the list of OpenShift supported versions.*/ V2ListSupportedOpenshiftVersions(ctx context.Context, params *V2ListSupportedOpenshiftVersionsParams) (*V2ListSupportedOpenshiftVersionsOK, error) @@ -68,6 +71,31 @@ func (a *Client) V2ListComponentVersions(ctx context.Context, params *V2ListComp } +/* +V2ListReleaseSources Retrieves openshift release sources configuration. +*/ +func (a *Client) V2ListReleaseSources(ctx context.Context, params *V2ListReleaseSourcesParams) (*V2ListReleaseSourcesOK, error) { + + result, err := a.transport.Submit(&runtime.ClientOperation{ + ID: "v2ListReleaseSources", + Method: "GET", + PathPattern: "/v2/release-sources", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http", "https"}, + Params: params, + Reader: &V2ListReleaseSourcesReader{formats: a.formats}, + AuthInfo: a.authInfo, + Context: ctx, + Client: params.HTTPClient, + }) + if err != nil { + return nil, err + } + return result.(*V2ListReleaseSourcesOK), nil + +} + /* V2ListSupportedOpenshiftVersions Retrieves the list of OpenShift supported versions. */ diff --git a/cmd/main.go b/cmd/main.go index f865679ea01..6cf8f5a94d6 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -47,6 +47,7 @@ import ( "github.com/openshift/assisted-service/internal/operators" "github.com/openshift/assisted-service/internal/operators/handler" "github.com/openshift/assisted-service/internal/provider/registry" + "github.com/openshift/assisted-service/internal/releasesources" "github.com/openshift/assisted-service/internal/spec" "github.com/openshift/assisted-service/internal/spoke_k8s_client" "github.com/openshift/assisted-service/internal/stream" @@ -116,6 +117,7 @@ var Options struct { InstructionConfig hostcommands.InstructionConfig OperatorsConfig operators.Options GCConfig garbagecollector.Config + ReleaseSourcesConfig releasesources.Config ClusterStateMonitorInterval time.Duration `envconfig:"CLUSTER_MONITOR_INTERVAL" default:"10s"` ClusterEventsUploaderInterval time.Duration `envconfig:"CLUSTER_EVENTS_UPLOADER_INTERVAL" default:"15m"` S3Config s3wrapper.Config @@ -265,6 +267,12 @@ func main() { "Failed to parse RELEASE_IMAGES json %s", Options.ReleaseImages) } + var releaseSourcesArray = models.ReleaseSources{} + if Options.ReleaseSourcesConfig.ReleaseSources != "" { + failOnError(json.Unmarshal([]byte(Options.ReleaseSourcesConfig.ReleaseSources), &releaseSourcesArray), + "Failed to parse RELEASE_SOURCES json %s", Options.ReleaseSourcesConfig.ReleaseSources) + } + log.Println(fmt.Sprintf("Started service with OS images %v, Release images %v", Options.OsImages, Options.ReleaseImages)) @@ -320,7 +328,7 @@ func main() { extracterHandler := oc.NewExtracter(&executer.CommonExecuter{}, oc.Config{MaxTries: oc.DefaultTries, RetryDelay: oc.DefaltRetryDelay}) - versionHandler, versionsAPIHandler, err := createVersionHandlers(log, ctrlMgr, releaseHandler, osImages, releaseImagesArray, authzHandler) + versionHandler, versionsAPIHandler, err := createVersionHandlers(log, ctrlMgr, releaseHandler, osImages, releaseImagesArray, authzHandler, releaseSourcesArray) failOnError(err, "failed to create Versions handlers") domainHandler := domains.NewHandler(Options.BMConfig.BaseDNSDomains) staticNetworkConfig := staticnetworkconfig.New(log.WithField("pkg", "static_network_config")) @@ -426,6 +434,9 @@ func main() { hostStateMonitor.Start() defer hostStateMonitor.Stop() + openshiftReleaseSyncer := runOpenshiftReleaseSyncerIfNeeded(releaseSourcesArray, releaseImagesArray, db, log) + defer stopOpenshiftReleaseSyncerIfNeeded(openshiftReleaseSyncer) + newUrl, err := s3wrapper.FixEndpointURL(Options.BMConfig.S3EndpointURL) failOnError(err, "failed to create valid bm config S3 endpoint URL from %s", Options.BMConfig.S3EndpointURL) Options.BMConfig.S3EndpointURL = newUrl @@ -838,7 +849,15 @@ func createControllerManager() (manager.Manager, error) { return nil, nil } -func createVersionHandlers(log logrus.FieldLogger, ctrlMgr manager.Manager, releaseHandler oc.Release, osImages versions.OSImages, releaseImagesArray models.ReleaseImages, authzHandler auth.Authorizer) (versions.Handler, restapi.VersionsAPI, error) { +func createVersionHandlers( + log logrus.FieldLogger, + ctrlMgr manager.Manager, + releaseHandler oc.Release, + osImages versions.OSImages, + releaseImagesArray models.ReleaseImages, + authzHandler auth.Authorizer, + releaseSources models.ReleaseSources, +) (versions.Handler, restapi.VersionsAPI, error) { var versionsClient client.Client if ctrlMgr != nil { versionsClient = ctrlMgr.GetClient() @@ -854,7 +873,15 @@ func createVersionHandlers(log logrus.FieldLogger, ctrlMgr manager.Manager, rele if err != nil { return nil, nil, err } - versionsAPIHandler := versions.NewAPIHandler(log, Options.Versions, authzHandler, versionsHandler, osImages) + + versionsAPIHandler := versions.NewAPIHandler(versions.NewAPIHandlerParams{ + Log: log, + Versions: Options.Versions, + AuthzHandler: authzHandler, + VersionsHandler: versionsHandler, + OSImages: osImages, + ReleaseSources: releaseSources, + }) return versionsHandler, versionsAPIHandler, nil } @@ -895,3 +922,27 @@ func startPPROF(log *logrus.Logger) { } } } + +func runOpenshiftReleaseSyncerIfNeeded( + releaseSources models.ReleaseSources, + releaseImages models.ReleaseImages, + db *gorm.DB, + log logrus.FieldLogger, +) *thread.Thread { + if !Options.EnableKubeAPI && Options.ReleaseSourcesConfig.ReleaseSources != "" { + releaseSourcesHandler := releasesources.NewReleaseSourcesHandler(releaseSources, releaseImages, log, db, Options.ReleaseSourcesConfig) + go releaseSourcesHandler.SyncReleaseImages() + openShiftReleaseSyncer := thread.New( + log.WithField("pkg", "opensift-release-syncer"), "Openshift Releases Syncer", Options.ReleaseSourcesConfig.OpenShiftReleaseSyncerInterval, releaseSourcesHandler.SyncReleaseImages) + openShiftReleaseSyncer.Start() + return openShiftReleaseSyncer + } + + return nil +} + +func stopOpenshiftReleaseSyncerIfNeeded(openShiftReleaseSyncer *thread.Thread) { + if openShiftReleaseSyncer != nil { + openShiftReleaseSyncer.Stop() + } +} diff --git a/deploy/assisted-service-configmap.yaml b/deploy/assisted-service-configmap.yaml index 59616e9d8b4..ec587216512 100644 --- a/deploy/assisted-service-configmap.yaml +++ b/deploy/assisted-service-configmap.yaml @@ -26,3 +26,6 @@ data: DISABLED_HOST_VALIDATIONS: REPLACE_DISABLED_HOST_VALIDATIONS DISABLED_STEPS: REPLACE_DISABLED_STEPS ISO_IMAGE_TYPE: REPLACE_ISO_IMAGE_TYPE + RELEASE_SOURCES: REPLACE_RELEASE_SOURCES + OPENSHIFT_RELEASE_SYNCER_INTERVAL: REPLACE_OPENSHIFT_RELEASE_SYNCER_INTERVAL + OPENSHIFT_SUPPORT_LEVEL_API_BASE_URL: REPLACE_OPENSHIFT_SUPPORT_LEVEL_API_BASE_URL diff --git a/deploy/podman/configmap.yml b/deploy/podman/configmap.yml index 0ff18b92588..7bbd2824232 100644 --- a/deploy/podman/configmap.yml +++ b/deploy/podman/configmap.yml @@ -31,3 +31,6 @@ data: SERVICE_BASE_URL: http://127.0.0.1:8090 STORAGE: filesystem ENABLE_UPGRADE_AGENT: "true" + RELEASE_SOURCES: '[{"openshift_version":"4.11","upgrade_channels":[{"cpu_architecture":"x86_64","channels":["stable"]},{"cpu_architecture":"arm64","channels":["stable"]},{"cpu_architecture":"ppc64le","channels":["stable"]},{"cpu_architecture":"s390x","channels":["stable"]},{"cpu_architecture":"multi","channels":["stable"]}]},{"openshift_version":"4.12","upgrade_channels":[{"cpu_architecture":"x86_64","channels":["stable"]},{"cpu_architecture":"arm64","channels":["stable"]},{"cpu_architecture":"ppc64le","channels":["stable"]},{"cpu_architecture":"s390x","channels":["stable"]},{"cpu_architecture":"multi","channels":["stable"]}]},{"openshift_version":"4.13","upgrade_channels":[{"cpu_architecture":"x86_64","channels":["stable"]},{"cpu_architecture":"arm64","channels":["stable"]},{"cpu_architecture":"ppc64le","channels":["stable"]},{"cpu_architecture":"s390x","channels":["stable"]},{"cpu_architecture":"multi","channels":["stable"]}]},{"openshift_version":"4.14","upgrade_channels":[{"cpu_architecture":"x86_64","channels":["stable","candidate"]},{"cpu_architecture":"arm64","channels":["stable","candidate"]},{"cpu_architecture":"ppc64le","channels":["stable","candidate"]},{"cpu_architecture":"s390x","channels":["stable","candidate"]},{"cpu_architecture":"multi","channels":["stable","candidate"]}]},{"openshift_version":"4.15","upgrade_channels":[{"cpu_architecture":"x86_64","channels":["stable","candidate"]},{"cpu_architecture":"arm64","channels":["stable","candidate"]},{"cpu_architecture":"ppc64le","channels":["stable","candidate"]},{"cpu_architecture":"s390x","channels":["stable","candidate"]},{"cpu_architecture":"multi","channels":["stable","candidate"]}]}]' + OPENSHIFT_RELEASE_SYNCER_INTERVAL: "30m" + OPENSHIFT_SUPPORT_LEVEL_API_BASE_URL: "https://access.redhat.com/product-life-cycles/api/v1/products" diff --git a/internal/common/common_test.go b/internal/common/common_test.go index 728bbb53dcc..0e9b3826935 100644 --- a/internal/common/common_test.go +++ b/internal/common/common_test.go @@ -320,6 +320,80 @@ var _ = Describe("Get Major.Minor version", func() { }) }) +var _ = Describe("Get Major version", func() { + It("GA release", func() { + majorVersion, err := GetMajorVersion("4.10.0") + Expect(err).ToNot(HaveOccurred()) + Expect(*majorVersion).To(Equal("4")) + }) + It("pre-release", func() { + majorVersion, err := GetMajorVersion("4.11.0-0.alpha") + Expect(err).ToNot(HaveOccurred()) + Expect(*majorVersion).To(Equal("4")) + }) + It("nightly release", func() { + majorVersion, err := GetMajorVersion("4.12.0-0.nightly-2022-01-23-013716") + Expect(err).ToNot(HaveOccurred()) + Expect(*majorVersion).To(Equal("4")) + }) + It("major minor release", func() { + majorVersion, err := GetMajorVersion("3.14") + Expect(err).ToNot(HaveOccurred()) + Expect(*majorVersion).To(Equal("3")) + }) + It("small version", func() { + majorVersion, err := GetMajorVersion("2") + Expect(err).ToNot(HaveOccurred()) + Expect(*majorVersion).To(Equal("2")) + }) + It("empty versions", func() { + _, err := GetMajorVersion("") + Expect(err).To(HaveOccurred()) + }) +}) + +var _ = Describe("Test isVersionPreRelease", func() { + It("With ec/rc/fc/alpha/nightly", func() { + isPreRelease, err := IsVersionPreRelease("4.14.0-ec.2") + Expect(err).ToNot(HaveOccurred()) + Expect(*isPreRelease).To(BeTrue()) + + isPreRelease, err = IsVersionPreRelease("4.14.0-fc.2") + Expect(err).ToNot(HaveOccurred()) + Expect(*isPreRelease).To(BeTrue()) + + isPreRelease, err = IsVersionPreRelease("4.14.0-rc.2") + Expect(err).ToNot(HaveOccurred()) + Expect(*isPreRelease).To(BeTrue()) + + isPreRelease, err = IsVersionPreRelease("4.14.0-alpha") + Expect(err).ToNot(HaveOccurred()) + Expect(*isPreRelease).To(BeTrue()) + + isPreRelease, err = IsVersionPreRelease("4.14.0-nightly") + Expect(err).ToNot(HaveOccurred()) + Expect(*isPreRelease).To(BeTrue()) + }) + + It("With versions that are not prerelease", func() { + isPreRelease, err := IsVersionPreRelease("4.13.17") + Expect(err).ToNot(HaveOccurred()) + Expect(*isPreRelease).To(BeFalse()) + + isPreRelease, err = IsVersionPreRelease("4.14.17") + Expect(err).ToNot(HaveOccurred()) + Expect(*isPreRelease).To(BeFalse()) + + isPreRelease, err = IsVersionPreRelease("4.12.17") + Expect(err).ToNot(HaveOccurred()) + Expect(*isPreRelease).To(BeFalse()) + + isPreRelease, err = IsVersionPreRelease("4.12.17-multi") + Expect(err).ToNot(HaveOccurred()) + Expect(*isPreRelease).To(BeFalse()) + }) +}) + var _ = Describe("Test AreMastersSchedulable", func() { Context("for every combination of schedulableMastersForcedTrue and schedulableMasters", func() { for _, test := range []struct { diff --git a/internal/common/db.go b/internal/common/db.go index a5ce1ceb35b..08495388db7 100644 --- a/internal/common/db.go +++ b/internal/common/db.go @@ -27,6 +27,8 @@ const ( NotificationTypeInfraEnv = "InfraEnv" ) +var SupportedMultiArchitectures []string = []string{X86CPUArchitecture, ARM64CPUArchitecture, S390xCPUArchitecture, PowerCPUArchitecture} + type Notifiable interface { GetClusterID() *strfmt.UUID GetInfraEnvID() *strfmt.UUID @@ -211,6 +213,14 @@ func (i *InfraEnv) Payload() any { return &i.InfraEnv } +type ReleaseImage struct { + CreatedAt time.Time + models.ReleaseImage + + // The release channel from which the release was fetched from + Channel *string +} + type EagerLoadingState bool const ( @@ -251,6 +261,7 @@ func AutoMigrate(db *gorm.DB) error { &Cluster{}, &Event{}, &InfraEnv{}, + &ReleaseImage{}, &models.ClusterNetwork{}, &models.ServiceNetwork{}, &models.MachineNetwork{}, @@ -259,6 +270,38 @@ func AutoMigrate(db *gorm.DB) error { ) } +func GetReleaseImagesFromDBWhere(db *gorm.DB, where ...interface{}) (models.ReleaseImages, error) { + releaseImages := models.ReleaseImages{} + dbReleaseImages := []*ReleaseImage{} + if err := db.Model(&ReleaseImage{}).Find(&dbReleaseImages, where...).Error; err != nil { + return nil, err + } + + for _, releaseImage := range dbReleaseImages { + cpuArchitectures := []string{*releaseImage.CPUArchitecture} + if *releaseImage.CPUArchitecture == MultiCPUArchitecture { + cpuArchitectures = SupportedMultiArchitectures + } + releaseImage.CPUArchitectures = cpuArchitectures + releaseImages = append(releaseImages, &releaseImage.ReleaseImage) + } + + return releaseImages, nil +} + +func GetReleaseImageFromDBWhere(db *gorm.DB, where ...interface{}) (*models.ReleaseImage, error) { + releaseImages, err := GetReleaseImagesFromDBWhere(db, where...) + if err != nil { + return nil, err + } + + if len(releaseImages) == 0 { + return nil, nil + } + + return releaseImages[0], nil +} + func LoadTableFromDB(db *gorm.DB, tableName string, conditions ...interface{}) *gorm.DB { return db.Preload(tableName, conditions...) } diff --git a/internal/common/test_configuration.go b/internal/common/test_configuration.go index 39e632ef4ac..3490a1e710a 100644 --- a/internal/common/test_configuration.go +++ b/internal/common/test_configuration.go @@ -53,7 +53,7 @@ const TestDiskPath = "/dev/test-disk" var ( OpenShiftVersion string = "4.6" ReleaseVersion = "4.6.0" - ReleaseImage = "quay.io/openshift-release-dev/ocp-release:4.6.16-x86_64" + ReleaseImageURL = "quay.io/openshift-release-dev/ocp-release:4.6.16-x86_64" RhcosImage = "rhcos_4.6.0" RhcosVersion = "version-46.123-0" SupportLevel = "beta" @@ -64,12 +64,12 @@ var ( var TestDefaultConfig = &TestConfiguration{ OpenShiftVersion: OpenShiftVersion, ReleaseVersion: ReleaseVersion, - ReleaseImageUrl: ReleaseImage, + ReleaseImageUrl: ReleaseImageURL, CPUArchitecture: CPUArchitecture, ReleaseImage: &models.ReleaseImage{ CPUArchitecture: &CPUArchitecture, OpenshiftVersion: &OpenShiftVersion, - URL: &ReleaseImage, + URL: &ReleaseImageURL, Version: &ReleaseVersion, CPUArchitectures: []string{CPUArchitecture}, }, diff --git a/internal/common/version.go b/internal/common/version.go index 948cbcb9686..35ad7f7550c 100644 --- a/internal/common/version.go +++ b/internal/common/version.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + "github.com/go-openapi/swag" "github.com/hashicorp/go-version" ) @@ -70,3 +71,24 @@ func GetMajorMinorVersion(version string) (*string, error) { versionStr := fmt.Sprintf("%s.%s", splittedVersion[0], splittedVersion[1]) return &versionStr, nil } + +func IsVersionPreRelease(str string) (*bool, error) { + str = strings.TrimSuffix(str, "-multi") + semVersion, err := version.NewVersion(str) + if err != nil { + return nil, err + } + + return swag.Bool(semVersion.Prerelease() != ""), nil +} + +func GetMajorVersion(version string) (*string, error) { + version = strings.Split(version, "-")[0] + majorVersion := strings.Split(version, ".")[0] + + if majorVersion == "" { + return nil, errors.New("invalid version") + } + + return &majorVersion, nil +} diff --git a/internal/releasesources/clients.go b/internal/releasesources/clients.go new file mode 100644 index 00000000000..dbdea420138 --- /dev/null +++ b/internal/releasesources/clients.go @@ -0,0 +1,149 @@ +package releasesources + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/openshift/assisted-service/models" + "github.com/pkg/errors" +) + +const releaseImagesResourcePath = "upgrades_info/v1/graph" + +// ocpVersionSupportLevels maps between major.minor OCP version to its corresponding suppurt level +type ocpVersionSupportLevels map[string]string + +type ocpMajorVersionSet map[string]bool + +// openShiftReleasesAPIClientInterface is an interface for a client, +// whose sole purpose is to fetch release images from the OCM upgrades info API, +// according to CPU architecture and release channel, e.g. amd64, stable-4.14 +// +//go:generate mockgen -source=clients.go -destination=mock_clients.go -package=releasesources +type openShiftReleasesAPIClientInterface interface { + getReleases(channel, openshiftVersion, cpuArchitecture string) (*releaseGraph, error) +} + +// openShiftSupportLevelAPIClientInterface is an interface for a client, +// whose sole purpose is to fetch support levels from Red Hat Customer Portal, +// according to a major OCP version, e.g. 4 +// +//go:generate mockgen -source=clients.go -destination=mock_clients.go -package=releasesources +type openShiftSupportLevelAPIClientInterface interface { + getSupportLevels(majorVersion string) (ocpVersionSupportLevels, error) +} + +type releaseGraph struct { + Nodes []node `json:"nodes"` +} + +type node struct { + Version string `json:"version"` +} + +type supportLevelGraph struct { + Data []data `json:"data"` +} + +type data struct { + Versions []version `json:"versions"` +} + +type version struct { + Name string `json:"name"` + Type string `json:"type"` +} + +const releasesAPIQueryTemplate string = "%s?channel=%s-%s&arch=%s" +const supportLevelAPIQueryTemplate string = "%s?name=Openshift+Container+Platform+%s" + +type openShiftReleasesAPIClient struct { + baseURL string +} + +type openShiftSupportLevelAPIClient struct { + baseURL string +} + +func createOCPReleaseAPIBaseEndpoint(apiEndpoint string) string { + return fmt.Sprintf("%s/%s", apiEndpoint, releaseImagesResourcePath) +} + +func (o openShiftReleasesAPIClient) getEndpoint(channel, openshiftVersion, cpuArchitecture string) string { + return fmt.Sprintf(releasesAPIQueryTemplate, o.baseURL, channel, openshiftVersion, cpuArchitecture) +} + +func (o openShiftReleasesAPIClient) parseResponse(response *http.Response) (*releaseGraph, error) { + var graph releaseGraph + err := json.NewDecoder(response.Body).Decode(&graph) + if err != nil { + return nil, err + } + + return &graph, nil +} + +func (o openShiftReleasesAPIClient) getReleases(channel, openshiftVersion, cpuArchitecture string) (*releaseGraph, error) { + endpoint := o.getEndpoint(channel, openshiftVersion, cpuArchitecture) + response, err := http.Get(endpoint) + if err != nil { + return nil, errors.Wrapf(err, "an error occurred while making http request to %s", endpoint) + } + + graph, err := o.parseResponse(response) + if err != nil { + return nil, errors.Errorf("an error occurred while decoding the response to a request made to %s: %s", endpoint, err) + } + + return graph, nil +} + +func (o openShiftSupportLevelAPIClient) getEndpoint(openshiftMajorVersion string) string { + return fmt.Sprintf(supportLevelAPIQueryTemplate, o.baseURL, openshiftMajorVersion) +} + +func (o openShiftSupportLevelAPIClient) parseResponse(response *http.Response) (*supportLevelGraph, error) { + var graph supportLevelGraph + err := json.NewDecoder(response.Body).Decode(&graph) + if err != nil { + return nil, err + } + + return &graph, nil +} + +func (o openShiftSupportLevelAPIClient) createSupportLevelsMap(supportLevelGraph *supportLevelGraph) ocpVersionSupportLevels { + mapAPISupportLevelToOurSupportLevel := map[string]string{ + "End of life": models.OpenshiftVersionSupportLevelEndOfLife, + "Maintenance Support": models.OpenshiftVersionSupportLevelMaintenance, + "Full Support": models.OpenshiftVersionSupportLevelProduction, + } + supportLevels := ocpVersionSupportLevels{} + + for _, Data := range supportLevelGraph.Data { + for _, version := range Data.Versions { + if convertion, ok := mapAPISupportLevelToOurSupportLevel[version.Type]; ok { + version.Type = convertion + } + supportLevels[version.Name] = version.Type + } + } + + return supportLevels +} + +func (o openShiftSupportLevelAPIClient) getSupportLevels(majorVersion string) (ocpVersionSupportLevels, error) { + endpoint := o.getEndpoint(majorVersion) + response, err := http.Get(endpoint) + if err != nil { + return nil, errors.Errorf("an error occurred while making http request to %s: %s", endpoint, err) + } + + supportLevelGraph, err := o.parseResponse(response) + if err != nil { + return nil, errors.Errorf("an error occurred while decoding the response to a request made to %s: %s", endpoint, err) + } + + return o.createSupportLevelsMap(supportLevelGraph), nil +} diff --git a/internal/releasesources/clients_test.go b/internal/releasesources/clients_test.go new file mode 100644 index 00000000000..2277ba87df7 --- /dev/null +++ b/internal/releasesources/clients_test.go @@ -0,0 +1,957 @@ +package releasesources + +import ( + "errors" + "fmt" + "net/http" + "net/http/httptest" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/openshift/assisted-service/internal/common" +) + +type RequestResponseParameters struct { + Version string + CPUArchitecture string + Channel string + Response string +} + +type QueryParameters struct { + Channel string + Arch string +} + +var supportLevelsResponseVersion4 = `{ + "data": [ + { + "uuid": "0964595a-151e-4240-8a62-31e6c3730226", + "name": "OpenShift Container Platform 4", + "former_names": [], + "show_last_minor_release": false, + "show_final_minor_release": false, + "is_layered_product": false, + "all_phases": [ + { + "name": "General availability", + "ptype": "normal", + "tooltip": null, + "display_name": "General availability", + "additional_text": null + }, + { + "name": "Full support", + "ptype": "normal", + "tooltip": null, + "display_name": "Full support ends", + "additional_text": null + }, + { + "name": "Maintenance support", + "ptype": "normal", + "tooltip": null, + "display_name": "Maintenance support ends", + "additional_text": null + }, + { + "name": "Extended update support", + "ptype": "normal", + "tooltip": null, + "display_name": "Extended update support ends", + "additional_text": null + }, + { + "name": "Extended life phase", + "ptype": "extended", + "tooltip": "The Extended Life Cycle Phase (ELP) is the post-retirement time period. We require that customers running Red Hat Enterprise Linux products beyond their retirement continue to have active subscriptions which ensures that they continue receiving access to all previously released content, documentation, and knowledge base articles as well as receive limited technical support. As there are no bug fixes, security fixes, hardware enablement, or root cause analysis available during the Extended Life Phase, customers may choose to purchase the Extended Life Cycle Support Add-On during the Extended Life Phase, which will provide them with critical impact security fixes and selected urgent priority bug fixes.", + "display_name": "Extended life phase ends", + "additional_text": null + } + ], + "versions": [ + { + "name": "4.14", + "type": "Full Support", + "last_minor_release": null, + "final_minor_release": null, + "extra_header_value": null, + "additional_text": "", + "phases": [ + { + "name": "General availability", + "date": "2023-10-31T00:00:00.000Z", + "date_format": "date", + "additional_text": "" + }, + { + "name": "Full support", + "date": "4.15 GA + 3 months", + "date_format": "string", + "additional_text": "" + }, + { + "name": "Maintenance support", + "date": "2025-05-01T00:00:00.000Z", + "date_format": "date", + "additional_text": "" + }, + { + "name": "Extended update support", + "date": "2025-10-31T00:00:00.000Z", + "date_format": "date", + "additional_text": "" + }, + { + "name": "Extended life phase", + "date": "", + "date_format": "string", + "additional_text": "" + } + ], + "extra_dependences": [] + }, + { + "name": "4.13", + "type": "Full Support", + "last_minor_release": null, + "final_minor_release": null, + "extra_header_value": null, + "additional_text": "", + "phases": [ + { + "name": "General availability", + "date": "2023-05-17T00:00:00.000Z", + "date_format": "date", + "additional_text": "" + }, + { + "name": "Full support", + "date": "2024-01-31T00:00:00.000Z", + "date_format": "date", + "additional_text": "" + }, + { + "name": "Maintenance support", + "date": "2024-11-17T00:00:00.000Z", + "date_format": "date", + "additional_text": "" + }, + { + "name": "Extended update support", + "date": "N/A", + "date_format": "string" + }, + { + "name": "Extended life phase", + "date": "N/A", + "date_format": "string" + } + ], + "extra_dependences": [] + }, + { + "name": "4.12", + "type": "Maintenance Support", + "last_minor_release": null, + "final_minor_release": null, + "extra_header_value": null, + "additional_text": "", + "phases": [ + { + "name": "General availability", + "date": "2023-01-17T00:00:00.000Z", + "date_format": "date", + "additional_text": "" + }, + { + "name": "Full support", + "date": "2023-08-17T00:00:00.000Z", + "date_format": "date", + "additional_text": "" + }, + { + "name": "Maintenance support", + "date": "2024-07-17T00:00:00.000Z", + "date_format": "date", + "additional_text": "" + }, + { + "name": "Extended update support", + "date": "2025-01-17T00:00:00.000Z", + "date_format": "date", + "additional_text": "" + }, + { + "name": "Extended life phase", + "date": "", + "date_format": "string", + "additional_text": "" + } + ], + "extra_dependences": [] + }, + { + "name": "4.11", + "type": "Maintenance Support", + "last_minor_release": null, + "final_minor_release": null, + "extra_header_value": null, + "additional_text": "", + "phases": [ + { + "name": "General availability", + "date": "2022-08-10T00:00:00.000Z", + "date_format": "date", + "additional_text": "" + }, + { + "name": "Full support", + "date": "2023-04-17T00:00:00.000Z", + "date_format": "date", + "additional_text": "" + }, + { + "name": "Maintenance support", + "date": "2024-02-10T00:00:00.000Z", + "date_format": "date", + "additional_text": "" + }, + { + "name": "Extended update support", + "date": "N/A", + "date_format": "string", + "additional_text": "" + }, + { + "name": "Extended life phase", + "date": "N/A", + "date_format": "string", + "additional_text": "" + } + ], + "extra_dependences": [] + }, + { + "name": "4.10", + "type": "End of life", + "last_minor_release": null, + "final_minor_release": null, + "extra_header_value": null, + "additional_text": "", + "phases": [ + { + "name": "General availability", + "date": "2022-03-10T00:00:00.000Z", + "date_format": "date", + "additional_text": "" + }, + { + "name": "Full support", + "date": "2022-11-10T00:00:00.000Z", + "date_format": "date", + "additional_text": "" + }, + { + "name": "Maintenance support", + "date": "2023-09-10T00:00:00.000Z", + "date_format": "date", + "additional_text": "" + }, + { + "name": "Extended update support", + "date": "N/A", + "date_format": "string", + "additional_text": "" + }, + { + "name": "Extended life phase", + "date": "N/A", + "date_format": "string", + "additional_text": "" + } + ], + "extra_dependences": [] + }, + { + "name": "4.9", + "type": "End of life", + "last_minor_release": null, + "final_minor_release": null, + "extra_header_value": null, + "additional_text": "", + "phases": [ + { + "name": "General availability", + "date": "2021-10-18T00:00:00.000Z", + "date_format": "date", + "additional_text": "" + }, + { + "name": "Full support", + "date": "2022-06-10T00:00:00.000Z", + "date_format": "date", + "additional_text": "" + }, + { + "name": "Maintenance support", + "date": "2023-04-18T00:00:00.000Z", + "date_format": "date", + "additional_text": "" + }, + { + "name": "Extended update support", + "date": "N/A", + "date_format": "string", + "additional_text": "" + }, + { + "name": "Extended life phase", + "date": "N/A", + "date_format": "string", + "additional_text": "" + } + ], + "extra_dependences": [] + }, + { + "name": "4.8", + "type": "End of life", + "last_minor_release": null, + "final_minor_release": null, + "extra_header_value": null, + "additional_text": "", + "phases": [ + { + "name": "General availability", + "date": "2021-07-27T00:00:00.000Z", + "date_format": "date", + "additional_text": "" + }, + { + "name": "Full support", + "date": "2022-01-27T00:00:00.000Z", + "date_format": "date", + "additional_text": "" + }, + { + "name": "Maintenance support", + "date": "2023-01-27T00:00:00.000Z", + "date_format": "date", + "additional_text": "" + }, + { + "name": "Extended update support", + "date": "N/A", + "date_format": "string", + "additional_text": "" + }, + { + "name": "Extended life phase", + "date": "2023-04-27T00:00:00.000Z", + "date_format": "date", + "additional_text": "" + } + ], + "extra_dependences": [] + } + ], + "footnote": "", + "link": "https://access.redhat.com/support/policy/updates/openshift/", + "policies": "https://access.redhat.com/site/support/policy/updates/openshift/policies/" + } + ] + }` + +func getExpectedSupportLevels(openshiftMajorVersion string) (ocpVersionSupportLevels, error) { + if openshiftMajorVersion == "4" { + return ocpVersionSupportLevels{ + "4.14": "production", + "4.13": "production", + "4.12": "maintenance", + "4.11": "maintenance", + "4.10": "end of life", + "4.9": "end of life", + "4.8": "end of life", + }, nil + } + + if openshiftMajorVersion == "5" { + return ocpVersionSupportLevels{ + "5.7": "production", + "5.6": "production", + "5.5": "maintenance", + "5.4": "maintenance", + "5.3": "end of life", + "5.2": "end of life", + "5.1": "end of life", + }, nil + } + + return nil, errors.New("") +} + +var requestResponseParams = []RequestResponseParameters{ + { + Version: "4.10", + CPUArchitecture: common.AMD64CPUArchitecture, + Channel: OpenshiftReleaseChannelStable, + Response: `{ + "nodes": [ + { + "version": "4.10.1", + "payload": "quay.io/openshift-release-dev/ocp-release@sha256:foo-bar", + "metadata": { + "io.openshift.upgrades.graph.previous.remove_regex": "foo-bar", + "io.openshift.upgrades.graph.release.channels": "foo-bar", + "io.openshift.upgrades.graph.release.manifestref": "sha256:foo-bar", + "url": "https://access.redhat.com/errata/foo-bar" + } + } + ] + }`, + }, + { + Version: "4.12", + CPUArchitecture: common.AMD64CPUArchitecture, + Channel: OpenshiftReleaseChannelStable, + Response: `{ + "nodes": [ + { + "version": "4.12.1", + "payload": "quay.io/openshift-release-dev/ocp-release@sha256:foo-bar", + "metadata": { + "io.openshift.upgrades.graph.previous.remove_regex": "foo-bar", + "io.openshift.upgrades.graph.release.channels": "foo-bar", + "io.openshift.upgrades.graph.release.manifestref": "sha256:foo-bar", + "url": "https://access.redhat.com/errata/foo-bar" + } + } + ] + }`, + }, + { + Version: "4.13", + CPUArchitecture: common.AMD64CPUArchitecture, + Channel: OpenshiftReleaseChannelStable, + Response: `{ + "nodes": [ + { + "version": "4.13.1", + "payload": "quay.io/openshift-release-dev/ocp-release@sha256:foo-bar", + "metadata": { + "io.openshift.upgrades.graph.previous.remove_regex": "foo-bar", + "io.openshift.upgrades.graph.release.channels": "foo-bar", + "io.openshift.upgrades.graph.release.manifestref": "sha256:foo-bar", + "url": "https://access.redhat.com/errata/foo-bar" + } + }, + { + "version": "4.13.17", + "payload": "quay.io/openshift-release-dev/ocp-release@sha256:foo-bar", + "metadata": { + "io.openshift.upgrades.graph.previous.remove_regex": "foo-bar", + "io.openshift.upgrades.graph.release.channels": "foo-bar", + "io.openshift.upgrades.graph.release.manifestref": "sha256:foo-bar", + "url": "foo-bar" + } + }, + { + "version": "4.12.15", + "payload": "quay.io/openshift-release-dev/ocp-release@sha256:foo-bar", + "metadata": { + "io.openshift.upgrades.graph.previous.remove_regex": "foo-bar", + "io.openshift.upgrades.graph.release.channels": "foo-bar", + "io.openshift.upgrades.graph.release.manifestref": "sha256:foo-bar", + "url": "https://access.redhat.com/errata/foo-bar" + } + } + ] + }`, + }, + { + Version: "4.13", + CPUArchitecture: common.S390xCPUArchitecture, + Channel: OpenshiftReleaseChannelStable, + Response: `{ + "nodes": [ + { + "version": "4.13.1", + "payload": "quay.io/openshift-release-dev/ocp-release@sha256:foo-bar", + "metadata": { + "io.openshift.upgrades.graph.previous.remove_regex": "foo-bar", + "io.openshift.upgrades.graph.release.channels": "foo-bar", + "io.openshift.upgrades.graph.release.manifestref": "sha256:foo-bar", + "url": "https://access.redhat.com/errata/foo-bar" + } + }, + { + "version": "4.13.19", + "payload": "quay.io/openshift-release-dev/ocp-release@sha256:foo-bar", + "metadata": { + "io.openshift.upgrades.graph.previous.remove_regex": "foo-bar", + "io.openshift.upgrades.graph.release.channels": "foo-bar", + "io.openshift.upgrades.graph.release.manifestref": "sha256:foo-bar", + "url": "foo-bar" + } + }, + { + "version": "4.12.40", + "payload": "quay.io/openshift-release-dev/ocp-release@sha256:foo-bar", + "metadata": { + "io.openshift.upgrades.graph.previous.remove_regex": "foo-bar", + "io.openshift.upgrades.graph.release.channels": "foo-bar", + "io.openshift.upgrades.graph.release.manifestref": "sha256:foo-bar", + "url": "https://access.redhat.com/errata/foo-bar" + } + } + ] + }`, + }, + { + Version: "4.14", + CPUArchitecture: common.AMD64CPUArchitecture, + Channel: OpenshiftReleaseChannelStable, + Response: `{ + "nodes": [ + { + "version": "4.14.0", + "payload": "quay.io/openshift-release-dev/ocp-release@sha256:foo-bar", + "metadata": { + "io.openshift.upgrades.graph.previous.remove_regex": "foo-bar", + "io.openshift.upgrades.graph.release.channels": "foo-bar", + "io.openshift.upgrades.graph.release.manifestref": "sha256:foo-bar", + "url": "https://access.redhat.com/errata/RHSA-foo-bar" + } + }, + { + "version": "4.14.1", + "payload": "quay.io/openshift-release-dev/ocp-release@sha256:foo-bar", + "metadata": { + "io.openshift.upgrades.graph.previous.remove_regex": "foo-bar", + "io.openshift.upgrades.graph.release.channels": "foo-bar", + "io.openshift.upgrades.graph.release.manifestref": "sha256:foo-bar", + "url": "https://access.redhat.com/errata/RHBA-foo-bar" + } + }, + { + "version": "4.13.40", + "payload": "quay.io/openshift-release-dev/ocp-release@sha256:foo-bar", + "metadata": { + "io.openshift.upgrades.graph.previous.remove_regex": "foo-bar", + "io.openshift.upgrades.graph.release.channels": "foo-bar", + "io.openshift.upgrades.graph.release.manifestref": "sha256:foo-bar", + "url": "https://access.redhat.com/errata/foo-bar" + } + } + ] + }`, + }, + { + Version: "4.14", + CPUArchitecture: common.AMD64CPUArchitecture, + Channel: OpenshiftReleaseChannelCandidate, + Response: `{ + "nodes": [ + { + "version": "4.14.0-rc.1", + "payload": "quay.io/openshift-release-dev/ocp-release@sha256:foo-bar", + "metadata": { + "io.openshift.upgrades.graph.previous.remove_regex": "foo-bar", + "io.openshift.upgrades.graph.release.channels": "foo-bar", + "io.openshift.upgrades.graph.release.manifestref": "sha256:foo-bar", + "url": "https://access.redhat.com/errata/RHSA-foo-bar" + } + }, + { + "version": "4.14.0-ec.2", + "payload": "quay.io/openshift-release-dev/ocp-release@sha256:foo-bar", + "metadata": { + "io.openshift.upgrades.graph.previous.remove_regex": "foo-bar", + "io.openshift.upgrades.graph.release.channels": "foo-bar", + "io.openshift.upgrades.graph.release.manifestref": "sha256:foo-bar", + "url": "https://access.redhat.com/errata/RHBA-foo-bar" + } + }, + { + "version": "4.13.10", + "payload": "quay.io/openshift-release-dev/ocp-release@sha256:foo-bar", + "metadata": { + "io.openshift.upgrades.graph.previous.remove_regex": "foo-bar", + "io.openshift.upgrades.graph.release.channels": "foo-bar", + "io.openshift.upgrades.graph.release.manifestref": "sha256:foo-bar", + "url": "https://access.redhat.com/errata/foo-bar" + } + } + ] + }`, + }, + { + Version: "4.14", + CPUArchitecture: common.PowerCPUArchitecture, + Channel: OpenshiftReleaseChannelStable, + Response: `{ + "nodes": [ + { + "version": "4.13.5", + "payload": "quay.io/openshift-release-dev/ocp-release@sha256:foo-bar", + "metadata": { + "io.openshift.upgrades.graph.previous.remove_regex": "foo-bar", + "io.openshift.upgrades.graph.release.channels": "foo-bar", + "io.openshift.upgrades.graph.release.manifestref": "sha256:foo-bar", + "url": "https://access.redhat.com/errata/RHBA-foo-bar" + } + }, + { + "version": "4.13.15", + "payload": "quay.io/openshift-release-dev/ocp-release@sha256:foo-bar", + "metadata": { + "io.openshift.upgrades.graph.previous.remove_regex": "foo-bar", + "io.openshift.upgrades.graph.release.channels": "foo-bar", + "io.openshift.upgrades.graph.release.manifestref": "sha256:foo-bar", + "url": "https://access.redhat.com/errata/foo-bar" + } + } + ] + }`, + }, + { + Version: "4.14", + CPUArchitecture: common.PowerCPUArchitecture, + Channel: OpenshiftReleaseChannelCandidate, + Response: `{ + "nodes": [ + { + "version": "4.14.0", + "payload": "quay.io/openshift-release-dev/ocp-release@sha256:foo-bar", + "metadata": { + "io.openshift.upgrades.graph.previous.remove_regex": "foo-bar", + "io.openshift.upgrades.graph.release.channels": "foo-bar", + "io.openshift.upgrades.graph.release.manifestref": "sha256:foo-bar", + "url": "https://access.redhat.com/errata/RHSA-foo-bar" + } + }, + { + "version": "4.14.1", + "payload": "quay.io/openshift-release-dev/ocp-release@sha256:foo-bar", + "metadata": { + "io.openshift.upgrades.graph.previous.remove_regex": "foo-bar", + "io.openshift.upgrades.graph.release.channels": "foo-bar", + "io.openshift.upgrades.graph.release.manifestref": "sha256:foo-bar", + "url": "https://access.redhat.com/errata/RHBA-foo-bar" + } + } + ] + }`, + }, + { + Version: "4.15", + CPUArchitecture: common.AMD64CPUArchitecture, + Channel: OpenshiftReleaseChannelCandidate, + Response: `{ + "nodes": [ + { + "version": "4.15.0-ec.2", + "payload": "quay.io/openshift-release-dev/ocp-release@sha256:foo-bar", + "metadata": { + "io.openshift.upgrades.graph.previous.remove_regex": "foo-bar", + "io.openshift.upgrades.graph.release.channels": "foo-bar", + "io.openshift.upgrades.graph.release.manifestref": "sha256:foo-bar", + "url": "https://access.redhat.com/errata/RHSA-foo-bar" + } + } + ] + }`, + }, + { + Version: "4.16", + CPUArchitecture: common.MultiCPUArchitecture, + Channel: OpenshiftReleaseChannelCandidate, + Response: `{ + "nodes": [ + { + "version": "4.16.0-ec.2", + "payload": "quay.io/openshift-release-dev/ocp-release@sha256:foo-bar", + "metadata": { + "io.openshift.upgrades.graph.previous.remove_regex": "foo-bar", + "io.openshift.upgrades.graph.release.channels": "foo-bar", + "io.openshift.upgrades.graph.release.manifestref": "sha256:foo-bar", + "url": "https://access.redhat.com/errata/RHSA-foo-bar" + } + } + ] + }`, + }, +} + +//gocyclo:ignore +func getExpectedReleasesGraphForValidParams(channel, openshiftVersion, cpuArchitecture string) (*releaseGraph, error) { + if channel == OpenshiftReleaseChannelStable && openshiftVersion == "4.10" && cpuArchitecture == common.AMD64CPUArchitecture { + return &releaseGraph{ + Nodes: []node{ + {Version: "4.10.1"}, + }, + }, nil + } + + if channel == OpenshiftReleaseChannelStable && openshiftVersion == "4.12" && cpuArchitecture == common.AMD64CPUArchitecture { + return &releaseGraph{ + Nodes: []node{ + {Version: "4.12.1"}, + }, + }, nil + } + + if channel == OpenshiftReleaseChannelStable && openshiftVersion == "4.13" && cpuArchitecture == common.AMD64CPUArchitecture { + return &releaseGraph{ + Nodes: []node{ + {Version: "4.13.1"}, + {Version: "4.13.17"}, + {Version: "4.12.15"}, + }, + }, nil + } + + if channel == OpenshiftReleaseChannelStable && openshiftVersion == "4.13" && cpuArchitecture == common.S390xCPUArchitecture { + return &releaseGraph{ + Nodes: []node{ + {Version: "4.13.1"}, + {Version: "4.13.19"}, + {Version: "4.12.40"}, + }, + }, nil + } + + if channel == OpenshiftReleaseChannelStable && openshiftVersion == "4.14" && cpuArchitecture == common.AMD64CPUArchitecture { + return &releaseGraph{ + Nodes: []node{ + {Version: "4.14.0"}, + {Version: "4.14.1"}, + {Version: "4.13.40"}, + }, + }, nil + } + + if channel == OpenshiftReleaseChannelCandidate && openshiftVersion == "4.14" && cpuArchitecture == common.AMD64CPUArchitecture { + return &releaseGraph{ + Nodes: []node{ + {Version: "4.14.0-rc.1"}, + {Version: "4.14.0-ec.2"}, + {Version: "4.13.10"}, + }, + }, nil + } + + if channel == OpenshiftReleaseChannelStable && openshiftVersion == "4.14" && cpuArchitecture == common.PowerCPUArchitecture { + return &releaseGraph{ + Nodes: []node{ + {Version: "4.13.5"}, + {Version: "4.13.15"}, + }, + }, nil + } + + if channel == OpenshiftReleaseChannelCandidate && openshiftVersion == "4.14" && cpuArchitecture == common.PowerCPUArchitecture { + return &releaseGraph{ + Nodes: []node{ + {Version: "4.14.0"}, + {Version: "4.14.1"}, + }, + }, nil + } + + if channel == OpenshiftReleaseChannelCandidate && openshiftVersion == "4.14" && cpuArchitecture == common.AMD64CPUArchitecture { + return &releaseGraph{ + Nodes: []node{ + {Version: "4.14.1"}, + }, + }, nil + } + + if channel == OpenshiftReleaseChannelCandidate && openshiftVersion == "4.15" && cpuArchitecture == common.AMD64CPUArchitecture { + return &releaseGraph{ + Nodes: []node{ + {Version: "4.15.0-ec.2"}, + }, + }, nil + } + + if channel == OpenshiftReleaseChannelCandidate && openshiftVersion == "4.16" && cpuArchitecture == common.MultiCPUArchitecture { + return &releaseGraph{ + Nodes: []node{ + {Version: "4.16.0-ec.2"}, + }, + }, nil + } + + return nil, errors.New("") +} + +var _ = Describe("Test clients", func() { + Describe("Test releases client", func() { + Context("Test getReleases", func() { + It("Should be successfull with valid request/response params", func() { + + var responseMatcher = map[QueryParameters]string{} + + for _, parameters := range requestResponseParams { + responseMatcher[QueryParameters{ + Channel: fmt.Sprintf("%s-%s", parameters.Channel, parameters.Version), + Arch: parameters.CPUArchitecture, + }] = parameters.Response + } + + releasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + queryParameters := r.URL.Query() + + if response, ok := responseMatcher[QueryParameters{ + Channel: queryParameters["channel"][0], + Arch: queryParameters["arch"][0], + }]; ok { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(response)) + return + } + w.WriteHeader(http.StatusNotFound) + })) + defer releasesServer.Close() + + client := openShiftReleasesAPIClient{baseURL: createOCPReleaseAPIBaseEndpoint(releasesServer.URL)} + + for _, params := range requestResponseParams { + expectedReleasesGraph, err := getExpectedReleasesGraphForValidParams(params.Channel, params.Version, params.CPUArchitecture) + Expect(err).ToNot(HaveOccurred()) + + releasesGraph, err := client.getReleases(params.Channel, params.Version, params.CPUArchitecture) + Expect(err).ToNot(HaveOccurred()) + Expect(releasesGraph).To(Equal(expectedReleasesGraph)) + } + }) + + It("Should cause an error with invalid response from server", func() { + var responseMatcher = map[QueryParameters]string{} + + for _, parameters := range requestResponseParams { + responseMatcher[QueryParameters{ + Channel: fmt.Sprintf("%s-%s", parameters.Channel, parameters.Version), + Arch: parameters.CPUArchitecture, + }] = parameters.Response + } + + releasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + queryParameters := r.URL.Query() + + if queryParameters["channel"][0] == "stable-4.14" && queryParameters["arch"][0] == common.PowerCPUArchitecture { + w.WriteHeader(http.StatusNotFound) + return + } + + if response, ok := responseMatcher[QueryParameters{ + Channel: queryParameters["channel"][0], + Arch: queryParameters["arch"][0], + }]; ok { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(response)) + return + } + w.WriteHeader(http.StatusNotFound) + })) + defer releasesServer.Close() + + client := openShiftReleasesAPIClient{baseURL: createOCPReleaseAPIBaseEndpoint(releasesServer.URL)} + releasesGraph, err := client.getReleases(OpenshiftReleaseChannelStable, "4.14", common.PowerCPUArchitecture) + Expect(err).To(HaveOccurred()) + Expect(releasesGraph).To(BeNil()) + }) + + It("Should cause an error with valid response but unparsable body from server", func() { + var responseMatcher = map[QueryParameters]string{} + + for _, parameters := range requestResponseParams { + responseMatcher[QueryParameters{ + Channel: fmt.Sprintf("%s-%s", parameters.Channel, parameters.Version), + Arch: parameters.CPUArchitecture, + }] = parameters.Response + } + + releasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + queryParameters := r.URL.Query() + + if queryParameters["channel"][0] == "stable-4.14" && queryParameters["arch"][0] == common.PowerCPUArchitecture { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("invalid-response")) + return + } + + if response, ok := responseMatcher[QueryParameters{ + Channel: queryParameters["channel"][0], + Arch: queryParameters["arch"][0], + }]; ok { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(response)) + return + } + w.WriteHeader(http.StatusNotFound) + })) + defer releasesServer.Close() + + client := openShiftReleasesAPIClient{baseURL: createOCPReleaseAPIBaseEndpoint(releasesServer.URL)} + releasesGraph, err := client.getReleases(OpenshiftReleaseChannelStable, "4.14", common.PowerCPUArchitecture) + Expect(err).To(HaveOccurred()) + Expect(releasesGraph).To(BeNil()) + }) + }) + }) + + var _ = Describe("Test support levels client", func() { + Context("Test GetSupportLevelg", func() { + It("Should be successfull with valid request/response params", func() { + supportLevelServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + queryParameters := r.URL.Query() + + if queryParameters["name"][0] == "Openshift Container Platform 4" { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(supportLevelsResponseVersion4)) + return + } + + w.WriteHeader(http.StatusNotFound) + })) + defer supportLevelServer.Close() + + client := openShiftSupportLevelAPIClient{baseURL: supportLevelServer.URL} + supportLevels, err := client.getSupportLevels("4") + Expect(err).ToNot(HaveOccurred()) + + expectedSupportLevels, err := getExpectedSupportLevels("4") + Expect(err).ToNot(HaveOccurred()) + Expect(supportLevels).To(Equal(expectedSupportLevels)) + }) + + It("Should cause an error with invalid response from server", func() { + supportLevelServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + })) + defer supportLevelServer.Close() + + client := openShiftSupportLevelAPIClient{baseURL: supportLevelServer.URL} + supportLevels, err := client.getSupportLevels("4") + Expect(err).To(HaveOccurred()) + Expect(supportLevels).To(BeNil()) + }) + + It("Should cause an error with valid response but unparsable body from server", func() { + supportLevelServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("invalid-response")) + })) + defer supportLevelServer.Close() + + client := openShiftSupportLevelAPIClient{baseURL: supportLevelServer.URL} + supportLevels, err := client.getSupportLevels("4") + Expect(err).To(HaveOccurred()) + Expect(supportLevels).To(BeNil()) + }) + }) + }) +}) diff --git a/internal/releasesources/config.go b/internal/releasesources/config.go new file mode 100644 index 00000000000..5fa3dadab0d --- /dev/null +++ b/internal/releasesources/config.go @@ -0,0 +1,10 @@ +package releasesources + +import "time" + +type Config struct { + OCMBaseURL string `envconfig:"OCM_BASE_URL" default:""` + ReleaseSources string `envconfig:"RELEASE_SOURCES" default:""` + OpenShiftReleaseSyncerInterval time.Duration `envconfig:"OPENSHIFT_RELEASE_SYNCER_INTERVAL" default:"30m"` + OpenshiftSupportLevelAPIBaseUrl string `envconfig:"OPENSHIFT_SUPPORT_LEVEL_API_BASE_URL" default:"https://access.redhat.com/product-life-cycles/api/v1/products"` +} diff --git a/internal/releasesources/mock_clients.go b/internal/releasesources/mock_clients.go new file mode 100644 index 00000000000..b0d6c9e1cfb --- /dev/null +++ b/internal/releasesources/mock_clients.go @@ -0,0 +1,87 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: clients.go + +// Package releasesources is a generated GoMock package. +package releasesources + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// MockopenShiftReleasesAPIClientInterface is a mock of openShiftReleasesAPIClientInterface interface. +type MockopenShiftReleasesAPIClientInterface struct { + ctrl *gomock.Controller + recorder *MockopenShiftReleasesAPIClientInterfaceMockRecorder +} + +// MockopenShiftReleasesAPIClientInterfaceMockRecorder is the mock recorder for MockopenShiftReleasesAPIClientInterface. +type MockopenShiftReleasesAPIClientInterfaceMockRecorder struct { + mock *MockopenShiftReleasesAPIClientInterface +} + +// NewMockopenShiftReleasesAPIClientInterface creates a new mock instance. +func NewMockopenShiftReleasesAPIClientInterface(ctrl *gomock.Controller) *MockopenShiftReleasesAPIClientInterface { + mock := &MockopenShiftReleasesAPIClientInterface{ctrl: ctrl} + mock.recorder = &MockopenShiftReleasesAPIClientInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockopenShiftReleasesAPIClientInterface) EXPECT() *MockopenShiftReleasesAPIClientInterfaceMockRecorder { + return m.recorder +} + +// getReleases mocks base method. +func (m *MockopenShiftReleasesAPIClientInterface) getReleases(channel, openshiftVersion, cpuArchitecture string) (*releaseGraph, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "getReleases", channel, openshiftVersion, cpuArchitecture) + ret0, _ := ret[0].(*releaseGraph) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// getReleases indicates an expected call of getReleases. +func (mr *MockopenShiftReleasesAPIClientInterfaceMockRecorder) getReleases(channel, openshiftVersion, cpuArchitecture interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getReleases", reflect.TypeOf((*MockopenShiftReleasesAPIClientInterface)(nil).getReleases), channel, openshiftVersion, cpuArchitecture) +} + +// MockopenShiftSupportLevelAPIClientInterface is a mock of openShiftSupportLevelAPIClientInterface interface. +type MockopenShiftSupportLevelAPIClientInterface struct { + ctrl *gomock.Controller + recorder *MockopenShiftSupportLevelAPIClientInterfaceMockRecorder +} + +// MockopenShiftSupportLevelAPIClientInterfaceMockRecorder is the mock recorder for MockopenShiftSupportLevelAPIClientInterface. +type MockopenShiftSupportLevelAPIClientInterfaceMockRecorder struct { + mock *MockopenShiftSupportLevelAPIClientInterface +} + +// NewMockopenShiftSupportLevelAPIClientInterface creates a new mock instance. +func NewMockopenShiftSupportLevelAPIClientInterface(ctrl *gomock.Controller) *MockopenShiftSupportLevelAPIClientInterface { + mock := &MockopenShiftSupportLevelAPIClientInterface{ctrl: ctrl} + mock.recorder = &MockopenShiftSupportLevelAPIClientInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockopenShiftSupportLevelAPIClientInterface) EXPECT() *MockopenShiftSupportLevelAPIClientInterfaceMockRecorder { + return m.recorder +} + +// getSupportLevels mocks base method. +func (m *MockopenShiftSupportLevelAPIClientInterface) getSupportLevels(majorVersion string) (ocpVersionSupportLevels, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "getSupportLevels", majorVersion) + ret0, _ := ret[0].(ocpVersionSupportLevels) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// getSupportLevels indicates an expected call of getSupportLevels. +func (mr *MockopenShiftSupportLevelAPIClientInterfaceMockRecorder) getSupportLevels(majorVersion interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getSupportLevels", reflect.TypeOf((*MockopenShiftSupportLevelAPIClientInterface)(nil).getSupportLevels), majorVersion) +} diff --git a/internal/releasesources/release_sources.go b/internal/releasesources/release_sources.go new file mode 100644 index 00000000000..03d534f7fe6 --- /dev/null +++ b/internal/releasesources/release_sources.go @@ -0,0 +1,488 @@ +package releasesources + +import ( + "fmt" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + goversion "github.com/hashicorp/go-version" + "github.com/openshift/assisted-service/internal/common" + "github.com/openshift/assisted-service/models" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "gorm.io/gorm" +) + +const ( + releaseImageReferenceTemplateSaaS string = "quay.io/openshift-release-dev/ocp-release:%s-%s" + OpenshiftReleaseChannelStable string = "stable" + OpenshiftReleaseChannelCandidate string = "candidate" +) + +type releaseSourcesHandler struct { + releaseSources models.ReleaseSources + staticReleaseImages models.ReleaseImages + log logrus.FieldLogger + db *gorm.DB + config Config + releasesClient openShiftReleasesAPIClientInterface + supportLevelClient openShiftSupportLevelAPIClientInterface +} + +func NewReleaseSourcesHandler( + releaseSources models.ReleaseSources, + releaseImages models.ReleaseImages, + logger logrus.FieldLogger, + db *gorm.DB, + config Config, +) releaseSourcesHandler { + return releaseSourcesHandler{ + releaseSources: releaseSources, + staticReleaseImages: releaseImages, + log: logger, + db: db, + config: config, + releasesClient: openShiftReleasesAPIClient{baseURL: createOCPReleaseAPIBaseEndpoint(config.OCMBaseURL)}, + supportLevelClient: openShiftSupportLevelAPIClient{baseURL: config.OpenshiftSupportLevelAPIBaseUrl}, + } +} + +func (h *releaseSourcesHandler) createReleaseImage(channel, version, cpuArchitecture string) (*common.ReleaseImage, error) { + majorMinorVersion, err := common.GetMajorMinorVersion(version) + if err != nil { + return nil, err + } + + newOpenshiftVersion := majorMinorVersion + newVersion := &version + if cpuArchitecture == common.MultiCPUArchitecture { + newOpenshiftVersion = swag.String(fmt.Sprintf("%s-%s", *newOpenshiftVersion, common.MultiCPUArchitecture)) + newVersion = swag.String(fmt.Sprintf("%s-%s", *newVersion, common.MultiCPUArchitecture)) + } + + return &common.ReleaseImage{ + ReleaseImage: models.ReleaseImage{ + OpenshiftVersion: newOpenshiftVersion, + CPUArchitecture: &cpuArchitecture, + Version: newVersion, + URL: swag.String(getReleaseImageReference(version, cpuArchitecture)), + Default: false, + }, + Channel: &channel, + }, nil +} + +func (h *releaseSourcesHandler) insertToDB(tx *gorm.DB, releases []*common.ReleaseImage) error { + h.log.Debugf("Inserting %d records to openshift_versions table", len(releases)) + + if len(releases) == 0 { + return nil + } + + return tx.Create(releases).Error +} + +// removeReleaseImageDuplicateRecords removes duplcates from release images slice +func (h *releaseSourcesHandler) removeReleaseImageDuplicateRecords(releases []*common.ReleaseImage) []*common.ReleaseImage { + resultReleases := []*common.ReleaseImage{} + releaseImageReferences := map[string]bool{} + + for _, releaseImage := range releases { + if releaseImageReferences[*releaseImage.URL] { + continue + } + + releaseImageReferences[*releaseImage.URL] = true + resultReleases = append(resultReleases, releaseImage) + } + + return resultReleases +} + +// removeReleaseImageDuplicates removes a release image if there is another release image +// with same version and CPU architecture which is stable and is already considered in the list. +func (h *releaseSourcesHandler) removeReleaseImageDuplicates(releases []*common.ReleaseImage) []*common.ReleaseImage { + releases = h.removeReleaseImageDuplicatesFromDifferentChannels(releases) + releases = h.removeReleaseImageDuplicateRecords(releases) + return releases +} + +// removeReleaseImageDuplicatesFromDifferentChannels removes duplicate release images from different channels +func (h *releaseSourcesHandler) removeReleaseImageDuplicatesFromDifferentChannels(releases []*common.ReleaseImage) []*common.ReleaseImage { + resultReleases := []*common.ReleaseImage{} + stableReleasesReferences := map[string]bool{} + + for _, release := range releases { + if *release.Channel == OpenshiftReleaseChannelStable { + stableReleasesReferences[*release.URL] = true + } + } + + // Add only releases which does not have a stable duplicate + for _, release := range releases { + if *release.Channel != OpenshiftReleaseChannelStable && stableReleasesReferences[*release.URL] { + continue + } + + resultReleases = append(resultReleases, release) + } + + return resultReleases +} + +func (h *releaseSourcesHandler) getDynamicReleaseImages() ([]*common.ReleaseImage, error) { + // OCP releases API needs amd64, arm64 instead of x86_64, aarch64 respectively + cpuArchMapToAPIArch := map[string]string{ + common.X86CPUArchitecture: common.AMD64CPUArchitecture, + common.AARCH64CPUArchitecture: common.ARM64CPUArchitecture, + } + + // switch back + cpuArchMapFromAPIArch := map[string]string{ + common.AMD64CPUArchitecture: common.X86CPUArchitecture, + common.ARM64CPUArchitecture: common.AARCH64CPUArchitecture, + } + + releases := []*common.ReleaseImage{} + // Aggregate slice of releases from releases API + for _, releaseSource := range h.releaseSources { + openshiftVersion := *releaseSource.OpenshiftVersion + for _, upgradeChannel := range releaseSource.UpgradeChannels { + cpuArchitecture := *upgradeChannel.CPUArchitecture + for _, channel := range upgradeChannel.Channels { + apiCpuArchitecture, shouldSwitch := cpuArchMapToAPIArch[cpuArchitecture] + if shouldSwitch { + cpuArchitecture = apiCpuArchitecture + } + + graph, err := h.releasesClient.getReleases(channel, openshiftVersion, cpuArchitecture) + if err != nil { + h.log.WithError(err).Warnf("failed to get releases from api with: %s-%s, %s", channel, openshiftVersion, cpuArchitecture) + continue + } + + if shouldSwitch { + if arch, ok := cpuArchMapFromAPIArch[cpuArchitecture]; ok { + cpuArchitecture = arch + } + } + + for _, node := range graph.Nodes { + majorMinorVersion, err := common.GetMajorMinorVersion(node.Version) + if err != nil { + h.log.WithError(err).Warnf("failed to get the major.minor version of: %s", node.Version) + continue + } + + if openshiftVersion == *majorMinorVersion { + // Got a match for OpenShift ReleaseImage + newReleaseImage, err := h.createReleaseImage(channel, node.Version, cpuArchitecture) + if err != nil { + return nil, err + } + releases = append(releases, newReleaseImage) + } + } + } + } + } + + h.log.Debugf("Got %d releases", len(releases)) + return releases, nil +} + +func (h *releaseSourcesHandler) getStaticReleaseImages() []*common.ReleaseImage { + dbReleaseImages := []*common.ReleaseImage{} + + for _, releaseImage := range h.staticReleaseImages { + dbReleaseImage := common.ReleaseImage{ReleaseImage: *releaseImage} + // CPUArchitectures can be driven from CPUArchitecture, so there is no need to + // maintain SQL array for CPUArchitectures. Therefore we need to set it to nil before inserting + // to the DB and populate it back when we fetch. + dbReleaseImage.CPUArchitectures = nil + dbReleaseImages = append(dbReleaseImages, &dbReleaseImage) + } + + return dbReleaseImages +} + +// mergeReleaseImages merges static and dynamic release images together such that +// static release images have precedence in case of conflicts e.g. which release is default +func (h *releaseSourcesHandler) mergeReleaseImages(staticReleaseImages, dynamicReleaseImages []*common.ReleaseImage) []*common.ReleaseImage { + mergedReleaseImages := []*common.ReleaseImage{} + staticReleaseImagesReferenceSet := map[string]bool{} + defaultStaticReleaseExists := false + + for _, staticRelease := range staticReleaseImages { + mergedReleaseImages = append(mergedReleaseImages, staticRelease) + staticReleaseImagesReferenceSet[*staticRelease.URL] = true + if staticRelease.Default { + defaultStaticReleaseExists = true + } + } + + for _, dynamicRelease := range dynamicReleaseImages { + if staticReleaseImagesReferenceSet[*dynamicRelease.URL] { + continue + } + if dynamicRelease.Default && defaultStaticReleaseExists { + dynamicRelease.Default = false + } + mergedReleaseImages = append(mergedReleaseImages, dynamicRelease) + } + + return mergedReleaseImages +} + +func (h *releaseSourcesHandler) syncReleaseImages() error { + dynamicReleaseImages, err := h.getDynamicReleaseImages() + if err != nil { + return err + } + err = h.setDefaultReleaseImage(dynamicReleaseImages) + if err != nil { + return err + } + supportLevels, err := h.getSupportLevels() + if err != nil { + return err + } + err = h.setSupportLevels(dynamicReleaseImages, supportLevels) + if err != nil { + return err + } + staticReleaseImages := h.getStaticReleaseImages() + err = h.setSupportLevels(staticReleaseImages, supportLevels) + if err != nil { + return err + } + dynamicReleaseImages = h.removeReleaseImageDuplicates(dynamicReleaseImages) + releaseImages := h.mergeReleaseImages(staticReleaseImages, dynamicReleaseImages) + + h.log.Debug("Starting SQL transaction") + tx := h.db.Begin() + // Deleting all releases before adding again in order to + // store only the releases according to RELEASE_SOURCES + err = h.deleteAllReleases(tx) + if err != nil { + tx.Rollback() + return err + } + + err = h.insertToDB(tx, releaseImages) + if err != nil { + tx.Rollback() + return err + } + + h.log.Debug("Commiting changes") + return tx.Commit().Error +} + +// getReleaseImagesMajorOCPVersions retrieves the major OCP versions for both static and dynamic release images discovered. +func (h *releaseSourcesHandler) getReleaseImagesMajorOCPVersions() (ocpMajorVersionSet, error) { + majorVersions := ocpMajorVersionSet{} + + // First from static release images + for _, releaseImage := range h.staticReleaseImages { + majorVersion, err := common.GetMajorVersion(*releaseImage.OpenshiftVersion) + if err != nil { + return nil, errors.Errorf("error occurred while trying to get the major version of %s", *releaseImage.OpenshiftVersion) + } + majorVersions[*majorVersion] = true + } + + // Then from dynamic release images + for _, releaseSource := range h.releaseSources { + majorVersion, err := common.GetMajorVersion(*releaseSource.OpenshiftVersion) + if err != nil { + return nil, errors.Errorf("error occurred while trying to get the major version of %s", *releaseSource.OpenshiftVersion) + } + majorVersions[*majorVersion] = true + } + + return majorVersions, nil +} + +// getSupportLevels retrieves a mapping from OCP major.minor versions +// to their corresponding support levels for both static and dynamic release images. +func (h *releaseSourcesHandler) getSupportLevels() (ocpVersionSupportLevels, error) { + ocpMajorVersionSet, err := h.getReleaseImagesMajorOCPVersions() + if err != nil { + return nil, err + } + + supportLevels := ocpVersionSupportLevels{} + + for majorVersion := range ocpMajorVersionSet { + supportLevelsForMajor, err := h.supportLevelClient.getSupportLevels(majorVersion) + if err != nil { + return nil, err + } + + for majorMinorVersion, supportLevel := range supportLevelsForMajor { + supportLevels[majorMinorVersion] = supportLevel + } + } + + return supportLevels, nil +} + +// setSupportLevels sets support level for given release images as follows: +// Candidate/pre-release releases are considered support level 'beta'. +// The rest are stable releases, and their support level is set according to the mapping +// retrieved from the API. +func (h *releaseSourcesHandler) setSupportLevels(releases []*common.ReleaseImage, supportLevels ocpVersionSupportLevels) error { + for _, release := range releases { + if release.SupportLevel != "" { + continue + } + + isPreRelease, err := common.IsVersionPreRelease(*release.Version) + if err != nil { + return err + } + + if release.Channel != nil && *release.Channel == OpenshiftReleaseChannelCandidate || *isPreRelease { + release.SupportLevel = models.OpenshiftVersionSupportLevelBeta + continue + } + + majorMinorVersion, err := common.GetMajorMinorVersion(*release.Version) + if err != nil { + return err + } + + if supportLevel, exists := supportLevels[*majorMinorVersion]; exists { + release.SupportLevel = supportLevel + continue + } + + return fmt.Errorf("release %s did not appear in the support level mapping and is not a prerelease or candidate", *release.Version) + } + + return nil +} + +// setDefaultReleaseImage labels the latest stable release of the default CPU architecture as default +func (h *releaseSourcesHandler) setDefaultReleaseImage(releases []*common.ReleaseImage) error { + var latestStableRelease *common.ReleaseImage + + for _, release := range releases { + if *release.CPUArchitecture != common.DefaultCPUArchitecture || *release.Channel != OpenshiftReleaseChannelStable { + continue + } + + if latestStableRelease == nil { + latestStableRelease = release + } + + lessThan, err := common.BaseVersionLessThan(*release.Version, *latestStableRelease.Version) + if err != nil { + return err + } + + if lessThan { + latestStableRelease = release + } + } + + if latestStableRelease == nil { + h.log.Debugf("No stable releases of CPU architecture %s found", common.DefaultCPUArchitecture) + return nil + } + + h.log.Debugf("Labeling release %s with CPU architecture %s as default", *latestStableRelease.Version, common.DefaultCPUArchitecture) + latestStableRelease.Default = true + + return nil +} + +func (h *releaseSourcesHandler) deleteAllReleases(tx *gorm.DB) error { + var count int64 + err := tx.Model(&common.ReleaseImage{}).Count(&count).Error + if err != nil { + return err + } + h.log.Debugf("Truncating all release_images table records. %d records", count) + return tx.Exec("TRUNCATE TABLE release_images").Error +} + +func (h *releaseSourcesHandler) syncReleaseImagesWithErr() error { + err := h.validateReleaseSources() + if err != nil { + return err + } + + err = h.validateStaticReleaseImages() + if err != nil { + return err + } + + err = h.syncReleaseImages() + if err != nil { + return err + } + + return nil + +} + +func (h *releaseSourcesHandler) SyncReleaseImages() { + err := h.syncReleaseImagesWithErr() + if err != nil { + h.log.WithError(err).Warn("Failed to sync OpenShift ReleaseImages") + } +} + +func (h *releaseSourcesHandler) validateReleaseSources() error { + if h.releaseSources == nil { + h.releaseSources = models.ReleaseSources{} + } + + err := h.releaseSources.Validate(strfmt.Default) + if err != nil { + return err + } + + for _, releaseSource := range h.releaseSources { + openshiftVersion := *releaseSource.OpenshiftVersion + _, err = goversion.NewVersion(openshiftVersion) + if err != nil { + h.log.Debugf("Failed to create a version struct from %s", openshiftVersion) + return err + } + } + + return nil +} + +func (h *releaseSourcesHandler) validateStaticReleaseImages() error { + if h.staticReleaseImages == nil { + h.staticReleaseImages = models.ReleaseImages{} + } + + err := h.staticReleaseImages.Validate(strfmt.Default) + if err != nil { + return err + } + + for _, releaseImage := range h.staticReleaseImages { + _, err = goversion.NewVersion(*releaseImage.OpenshiftVersion) + if err != nil { + h.log.Debugf("Failed to create a version struct from %s", *releaseImage.OpenshiftVersion) + return err + } + _, err = goversion.NewVersion(*releaseImage.Version) + if err != nil { + h.log.Debugf("Failed to create a version struct from %s", *releaseImage.Version) + return err + } + } + + return nil +} + +func getReleaseImageReference(version, cpuArchitecture string) string { + return fmt.Sprintf(releaseImageReferenceTemplateSaaS, version, cpuArchitecture) +} diff --git a/internal/releasesources/release_sources_test.go b/internal/releasesources/release_sources_test.go new file mode 100644 index 00000000000..f47e6387a06 --- /dev/null +++ b/internal/releasesources/release_sources_test.go @@ -0,0 +1,1399 @@ +package releasesources + +import ( + "testing" + + "github.com/go-openapi/swag" + "github.com/golang/mock/gomock" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/openshift/assisted-service/internal/common" + "github.com/openshift/assisted-service/models" + "gorm.io/gorm" +) + +type CoreOpenshiftVersion struct { + models.ReleaseImage + Channel string +} + +func TestHandler(t *testing.T) { + RegisterFailHandler(Fail) + common.InitializeDBTest() + defer common.TerminateDBTest() + RunSpecs(t, "releasesources") +} + +func setGetReleasesMock(releasesMock *MockopenShiftReleasesAPIClientInterface, testsParams []RequestResponseParameters) { + for _, testParams := range testsParams { + releasesGraph, err := getExpectedReleasesGraphForValidParams(testParams.Channel, testParams.Version, testParams.CPUArchitecture) + releasesMock.EXPECT(). + getReleases(testParams.Channel, testParams.Version, testParams.CPUArchitecture). + Return(releasesGraph, err). + AnyTimes() + } +} + +func setGetSupportLevelsMock(supportLevelMock *MockopenShiftSupportLevelAPIClientInterface, majorVersion string) { + supportLevels, err := getExpectedSupportLevels(majorVersion) + Expect(err).ToNot(HaveOccurred()) + + supportLevelMock.EXPECT(). + getSupportLevels(majorVersion). + Return(supportLevels, nil). + AnyTimes() +} + +var releaseSources = models.ReleaseSources{ + { + OpenshiftVersion: swag.String("4.10"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelStable}, + }, + }, + }, + { + OpenshiftVersion: swag.String("4.12"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelStable}, + }, + }, + }, + { + OpenshiftVersion: swag.String("4.13"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelStable}, + }, + { + CPUArchitecture: swag.String(common.S390xCPUArchitecture), + Channels: []string{OpenshiftReleaseChannelStable}, + }, + }, + }, + { + OpenshiftVersion: swag.String("4.14"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelStable, OpenshiftReleaseChannelCandidate}, + }, + { + CPUArchitecture: swag.String("ppc64le"), + Channels: []string{OpenshiftReleaseChannelStable, OpenshiftReleaseChannelCandidate}, + }, + }, + }, + { + OpenshiftVersion: swag.String("4.15"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelCandidate}, + }, + }, + }, + { + OpenshiftVersion: swag.String("4.16"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.MultiCPUArchitecture), + Channels: []string{OpenshiftReleaseChannelCandidate}, + }, + }, + }, +} + +var staticReleaseImages = models.ReleaseImages{ + { // Standard + OpenshiftVersion: swag.String("4.11"), + Version: swag.String("4.11.1"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + CPUArchitectures: []string{common.X86CPUArchitecture}, + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.11.1-x86_64"), + }, + { // Minimal + OpenshiftVersion: swag.String("4.13"), + Version: swag.String("4.13.2"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.13.2-x86_64"), + }, + { // With support level, major.minor version + OpenshiftVersion: swag.String("4.14"), + Version: swag.String("4.14.2"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + SupportLevel: models.OpenshiftVersionSupportLevelMaintenance, + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.14.2-x86_64"), + }, + { // Default, full version + OpenshiftVersion: swag.String("4.14.3"), + Version: swag.String("4.14.3"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Default: true, + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.14.3-x86_64"), + }, + { // pre-release + OpenshiftVersion: swag.String("4.16"), + Version: swag.String("4.16.0-ec.2"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + CPUArchitectures: []string{common.X86CPUArchitecture}, + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.16.0-ec.2-x86_64"), + }, + { // multiarch + OpenshiftVersion: swag.String("4.14.4-multi"), + Version: swag.String("4.14.4-multi"), + CPUArchitecture: swag.String(common.MultiCPUArchitecture), + CPUArchitectures: []string{common.X86CPUArchitecture, common.ARM64CPUArchitecture}, + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.14.4-multi"), + }, +} + +var _ = Describe("SyncReleaseImages", func() { + var ( + db *gorm.DB + dbName string + ctrl *gomock.Controller + handler releaseSourcesHandler + releasesClientMock *MockopenShiftReleasesAPIClientInterface + supportLevelsClientMock *MockopenShiftSupportLevelAPIClientInterface + ) + + BeforeEach(func() { + db, dbName = common.PrepareTestDB() + ctrl = gomock.NewController(GinkgoT()) + releasesClientMock = NewMockopenShiftReleasesAPIClientInterface(ctrl) + supportLevelsClientMock = NewMockopenShiftSupportLevelAPIClientInterface(ctrl) + handler = NewReleaseSourcesHandler( + models.ReleaseSources{}, + models.ReleaseImages{}, + common.GetTestLog(), + db, + Config{}, + ) + handler.releasesClient = releasesClientMock + handler.supportLevelClient = supportLevelsClientMock + }) + + AfterEach(func() { + ctrl.Finish() + common.DeleteTestDB(db, dbName) + }) + + Context("Configuration validations", func() { + BeforeEach(func() { + setGetReleasesMock(releasesClientMock, requestResponseParams) + setGetSupportLevelsMock(supportLevelsClientMock, "4") + }) + + Context("ReleaseSources", func() { + // Testing ReleaseSources with standard ReleaseImages + BeforeEach(func() { + handler.staticReleaseImages = staticReleaseImages + }) + + It("Should not cause an error with empty release sources", func() { + err := handler.syncReleaseImagesWithErr() + Expect(err).ToNot(HaveOccurred()) + }) + + It("Should cause an error with invalid release sources - invalid cpu architecture", func() { + handler.releaseSources = models.ReleaseSources{ + { + OpenshiftVersion: swag.String("4.12"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String("badCPUArchitecture"), + Channels: []string{common.X86CPUArchitecture}, + }, + }, + }, + } + + err := handler.syncReleaseImagesWithErr() + Expect(err).To(HaveOccurred()) + }) + + It("Should cause an error with invalid release sources - invalid openshift version", func() { + handler.releaseSources = models.ReleaseSources{ + { + OpenshiftVersion: swag.String("invalidVersion"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelStable}, + }, + }, + }, + } + + err := handler.syncReleaseImagesWithErr() + Expect(err).To(HaveOccurred()) + }) + + It("Should cause an error with invalid release sources - invalid channel", func() { + handler.releaseSources = models.ReleaseSources{ + { + OpenshiftVersion: swag.String("4.12"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Channels: []string{"invalid-channel"}, + }, + }, + }, + } + + err := handler.syncReleaseImagesWithErr() + Expect(err).To(HaveOccurred()) + }) + + It("Should not cause an error with invalid release sources - empty channels", func() { + handler.releaseSources = models.ReleaseSources{ + { + OpenshiftVersion: swag.String("4.12"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Channels: []string{}, + }, + }, + }, + } + + err := handler.syncReleaseImagesWithErr() + Expect(err).ToNot(HaveOccurred()) + }) + + It("Should not cause an error with invalid release sources - empty upgrade channels", func() { + handler.releaseSources = models.ReleaseSources{ + { + OpenshiftVersion: swag.String("4.12"), + UpgradeChannels: []*models.UpgradeChannel{}, + }, + } + + err := handler.syncReleaseImagesWithErr() + Expect(err).ToNot(HaveOccurred()) + }) + + It("Should cause an error with invalid release sources - nils", func() { + handler.releaseSources = models.ReleaseSources{ + { + OpenshiftVersion: nil, + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: nil, + Channels: nil, + }, + }, + }, + } + + err := handler.syncReleaseImagesWithErr() + Expect(err).To(HaveOccurred()) + }) + }) + + Context("ReleaseImages", func() { + // Testing ReleaseImages with standard ReleaseSources + BeforeEach(func() { + handler.releaseSources = releaseSources + }) + + It("Should not cause an error with empty ReleaseImages", func() { + err := handler.syncReleaseImagesWithErr() + Expect(err).ToNot(HaveOccurred()) + }) + + It("Should not cause an error with nil ReleaseImages", func() { + handler.staticReleaseImages = nil + + err := handler.syncReleaseImagesWithErr() + Expect(err).ToNot(HaveOccurred()) + }) + + It("Should not cause an error with required fields", func() { + handler.staticReleaseImages = models.ReleaseImages{ + { + OpenshiftVersion: swag.String("4.11"), + Version: swag.String("4.11.1"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.11.1-x86_64"), + }, + } + + err := handler.syncReleaseImagesWithErr() + Expect(err).ToNot(HaveOccurred()) + }) + + It("Should cause an error with missing required fields", func() { + handler.staticReleaseImages = models.ReleaseImages{ + { + Version: swag.String("4.11.1"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.11.1-x86_64"), + }, + } + + err := handler.syncReleaseImagesWithErr() + Expect(err).To(HaveOccurred()) + + handler.staticReleaseImages = models.ReleaseImages{ + { + OpenshiftVersion: swag.String("4.11"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.11.1-x86_64"), + }, + } + + err = handler.syncReleaseImagesWithErr() + Expect(err).To(HaveOccurred()) + + handler.staticReleaseImages = models.ReleaseImages{ + { + OpenshiftVersion: swag.String("4.11"), + Version: swag.String("4.11.1"), + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.11.1-x86_64"), + }, + } + + err = handler.syncReleaseImagesWithErr() + Expect(err).To(HaveOccurred()) + + handler.staticReleaseImages = models.ReleaseImages{ + { + OpenshiftVersion: swag.String("4.11"), + Version: swag.String("4.11.1"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + }, + } + + err = handler.syncReleaseImagesWithErr() + Expect(err).To(HaveOccurred()) + }) + + It("Should not cause an error with valid fields", func() { + handler.staticReleaseImages = models.ReleaseImages{ + { + OpenshiftVersion: swag.String("4.11"), + Version: swag.String("4.11.1"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.11.1-x86_64"), + }, + } + + err := handler.syncReleaseImagesWithErr() + Expect(err).ToNot(HaveOccurred()) + }) + + It("Should cause an error with valid fields", func() { + handler.staticReleaseImages = models.ReleaseImages{ + { + OpenshiftVersion: swag.String("4.11"), + Version: swag.String("4.11.1"), + CPUArchitecture: swag.String("invalidCPUArch"), + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.11.1-x86_64"), + }, + } + + err := handler.syncReleaseImagesWithErr() + Expect(err).To(HaveOccurred()) + }) + }) + }) + + Context("Functionality", func() { + It("Should not cause an error with valid release sources but no results", func() { + handler.staticReleaseImages = staticReleaseImages + handler.releaseSources = models.ReleaseSources{ + { + OpenshiftVersion: swag.String("4.16"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelStable}, + }, + }, + }, + } + + setGetSupportLevelsMock(supportLevelsClientMock, "4") + releasesClientMock.EXPECT(). + getReleases(OpenshiftReleaseChannelStable, "4.16", common.AMD64CPUArchitecture). + Return(&releaseGraph{}, nil). + Times(1) + + err := handler.syncReleaseImagesWithErr() + Expect(err).ToNot(HaveOccurred()) + }) + + // Latest x86_64 stable release + Context("Should set the default release correctly", func() { + It("With no default static release image", func() { + handler.staticReleaseImages = models.ReleaseImages{ + { + OpenshiftVersion: swag.String("4.11"), + Version: swag.String("4.11.1"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + CPUArchitectures: []string{common.X86CPUArchitecture}, + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.11.1-x86_64"), + Default: false, + }, + { + OpenshiftVersion: swag.String("4.11.2"), + Version: swag.String("4.11.2"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + CPUArchitectures: []string{common.X86CPUArchitecture}, + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.11.2-x86_64"), + Default: false, + }, + } + + handler.releaseSources = models.ReleaseSources{ + { + OpenshiftVersion: swag.String("4.14"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelStable}, + }, + { + CPUArchitecture: swag.String(common.ARM64CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelStable}, + }, + { + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelCandidate}, + }, + }, + }, + } + + setGetSupportLevelsMock(supportLevelsClientMock, "4") + releasesClientMock.EXPECT(). + getReleases(OpenshiftReleaseChannelStable, "4.14", common.AMD64CPUArchitecture). + Return( + &releaseGraph{ + Nodes: []node{ + {Version: "4.14.4"}, + {Version: "4.14.2"}, + }, + }, nil). + Times(1) + + releasesClientMock.EXPECT(). + getReleases(OpenshiftReleaseChannelStable, "4.14", common.ARM64CPUArchitecture). + Return( + &releaseGraph{ + Nodes: []node{ + {Version: "4.14.7"}, + {Version: "4.14.5"}, + }, + }, nil). + Times(1) + + releasesClientMock.EXPECT(). + getReleases(OpenshiftReleaseChannelCandidate, "4.14", common.AMD64CPUArchitecture). + Return( + &releaseGraph{ + Nodes: []node{ + {Version: "4.14.6"}, + {Version: "4.14.3"}, + }, + }, nil). + Times(1) + + err := handler.syncReleaseImagesWithErr() + Expect(err).ToNot(HaveOccurred()) + + dbReleases := []*common.ReleaseImage{} + err = db.Find(&dbReleases, `"default" = ?`, true).Error + Expect(err).ToNot(HaveOccurred()) + Expect(len(dbReleases)).To(Equal(1)) + Expect(*dbReleases[0].Version).To(Equal("4.14.4")) + }) + + It("With default configuration release image", func() { + handler.staticReleaseImages = models.ReleaseImages{ + { + OpenshiftVersion: swag.String("4.11"), + Version: swag.String("4.11.1"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + CPUArchitectures: []string{common.X86CPUArchitecture}, + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.11.1-x86_64"), + Default: false, + }, + { + OpenshiftVersion: swag.String("4.11.2"), + Version: swag.String("4.11.2"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + CPUArchitectures: []string{common.X86CPUArchitecture}, + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.11.2-x86_64"), + Default: true, + }, + } + + handler.releaseSources = models.ReleaseSources{ + { + OpenshiftVersion: swag.String("4.14"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelStable}, + }, + { + CPUArchitecture: swag.String(common.ARM64CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelStable}, + }, + { + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelCandidate}, + }, + }, + }, + } + + setGetSupportLevelsMock(supportLevelsClientMock, "4") + releasesClientMock.EXPECT(). + getReleases(OpenshiftReleaseChannelStable, "4.14", common.AMD64CPUArchitecture). + Return( + &releaseGraph{ + Nodes: []node{ + {Version: "4.14.4"}, + {Version: "4.14.2"}, + }, + }, nil). + Times(1) + + releasesClientMock.EXPECT(). + getReleases(OpenshiftReleaseChannelStable, "4.14", common.ARM64CPUArchitecture). + Return( + &releaseGraph{ + Nodes: []node{ + {Version: "4.14.7"}, + {Version: "4.14.5"}, + }, + }, nil). + Times(1) + + releasesClientMock.EXPECT(). + getReleases(OpenshiftReleaseChannelCandidate, "4.14", common.AMD64CPUArchitecture). + Return( + &releaseGraph{ + Nodes: []node{ + {Version: "4.14.6"}, + {Version: "4.14.3"}, + }, + }, nil). + Times(1) + + err := handler.syncReleaseImagesWithErr() + Expect(err).ToNot(HaveOccurred()) + + dbReleases := []*common.ReleaseImage{} + err = db.Find(&dbReleases, `"default" = ?`, true).Error + Expect(err).ToNot(HaveOccurred()) + Expect(len(dbReleases)).To(Equal(1)) + Expect(*dbReleases[0].Version).To(Equal("4.11.2")) + }) + + It("With no default release image at all", func() { + handler.staticReleaseImages = models.ReleaseImages{ + { + OpenshiftVersion: swag.String("4.11"), + Version: swag.String("4.11.1"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + CPUArchitectures: []string{common.X86CPUArchitecture}, + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.11.1-x86_64"), + Default: false, + }, + { + OpenshiftVersion: swag.String("4.11.2"), + Version: swag.String("4.11.2"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + CPUArchitectures: []string{common.X86CPUArchitecture}, + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.11.2-x86_64"), + Default: false, + }, + } + + handler.releaseSources = models.ReleaseSources{ + { + OpenshiftVersion: swag.String("4.14"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.S390xCPUArchitecture), + Channels: []string{OpenshiftReleaseChannelStable}, + }, + { + CPUArchitecture: swag.String(common.ARM64CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelStable}, + }, + { + CPUArchitecture: swag.String(common.PowerCPUArchitecture), + Channels: []string{OpenshiftReleaseChannelCandidate}, + }, + }, + }, + } + + setGetSupportLevelsMock(supportLevelsClientMock, "4") + releasesClientMock.EXPECT(). + getReleases(OpenshiftReleaseChannelStable, "4.14", common.S390xCPUArchitecture). + Return( + &releaseGraph{ + Nodes: []node{ + {Version: "4.14.4"}, + {Version: "4.14.2"}, + }, + }, nil). + Times(1) + + releasesClientMock.EXPECT(). + getReleases(OpenshiftReleaseChannelStable, "4.14", common.ARM64CPUArchitecture). + Return( + &releaseGraph{ + Nodes: []node{ + {Version: "4.14.7"}, + {Version: "4.14.5"}, + }, + }, nil). + Times(1) + + releasesClientMock.EXPECT(). + getReleases(OpenshiftReleaseChannelCandidate, "4.14", common.PowerCPUArchitecture). + Return( + &releaseGraph{ + Nodes: []node{ + {Version: "4.14.6"}, + {Version: "4.14.3"}, + }, + }, nil). + Times(1) + + err := handler.syncReleaseImagesWithErr() + Expect(err).ToNot(HaveOccurred()) + + var dbReleases []*common.ReleaseImage + err = handler.db.Find(&dbReleases, `"default" = ?`, true).Error + Expect(err).ToNot(HaveOccurred()) + Expect(dbReleases).To(BeEmpty()) + }) + }) + + Context("Should set releases support level correctly", func() { + It("Of static release images", func() { + handler.staticReleaseImages = models.ReleaseImages{ + { + OpenshiftVersion: swag.String("5.2"), + Version: swag.String("5.2.0"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + CPUArchitectures: []string{common.X86CPUArchitecture}, + URL: swag.String("quay.io/openshift-release-dev/ocp-release:5.2.0-x86_64"), + Default: false, + }, + { + OpenshiftVersion: swag.String("5.3"), + Version: swag.String("5.3.0-ec.2"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + CPUArchitectures: []string{common.X86CPUArchitecture}, + URL: swag.String("quay.io/openshift-release-dev/ocp-release:5.3.0-ec.2-x86_64"), + Default: false, + }, + { + OpenshiftVersion: swag.String("4.9"), + Version: swag.String("4.9.1"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + CPUArchitectures: []string{common.X86CPUArchitecture}, + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.9.1-x86_64"), + Default: false, + }, + { + OpenshiftVersion: swag.String("4.11"), + Version: swag.String("4.11.1"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + CPUArchitectures: []string{common.X86CPUArchitecture}, + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.11.1-x86_64"), + Default: false, + }, + { + OpenshiftVersion: swag.String("4.13"), + Version: swag.String("4.13.1"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + CPUArchitectures: []string{common.X86CPUArchitecture}, + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.13.1-x86_64"), + Default: false, + }, + { + OpenshiftVersion: swag.String("4.15"), + Version: swag.String("4.15.0-ec.2"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + CPUArchitectures: []string{common.X86CPUArchitecture}, + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.15.0-ec.2-x86_64"), + Default: false, + }, + } + + setGetSupportLevelsMock(supportLevelsClientMock, "4") + setGetSupportLevelsMock(supportLevelsClientMock, "5") + + err := handler.syncReleaseImagesWithErr() + Expect(err).ToNot(HaveOccurred()) + + dbReleases := []*common.ReleaseImage{} + + err = db.Find(&dbReleases, "version = ?", "5.3.0-ec.2").Error + Expect(err).ToNot(HaveOccurred()) + Expect(len(dbReleases)).To(Equal(1)) + Expect(dbReleases[0].SupportLevel).To(Equal(models.ReleaseImageSupportLevelBeta)) + + err = db.Find(&dbReleases, "version = ?", "5.2.0").Error + Expect(err).ToNot(HaveOccurred()) + Expect(len(dbReleases)).To(Equal(1)) + Expect(dbReleases[0].SupportLevel).To(Equal(models.ReleaseImageSupportLevelEndOfLife)) + + err = db.Find(&dbReleases, "version = ?", "4.15.0-ec.2").Error + Expect(err).ToNot(HaveOccurred()) + Expect(len(dbReleases)).To(Equal(1)) + Expect(dbReleases[0].SupportLevel).To(Equal(models.ReleaseImageSupportLevelBeta)) + + err = db.Find(&dbReleases, "version = ?", "4.13.1").Error + Expect(err).ToNot(HaveOccurred()) + Expect(len(dbReleases)).To(Equal(1)) + Expect(dbReleases[0].SupportLevel).To(Equal(models.ReleaseImageSupportLevelProduction)) + + err = db.Find(&dbReleases, "version = ?", "4.11.1").Error + Expect(err).ToNot(HaveOccurred()) + Expect(len(dbReleases)).To(Equal(1)) + Expect(dbReleases[0].SupportLevel).To(Equal(models.ReleaseImageSupportLevelMaintenance)) + + err = db.Find(&dbReleases, "version = ?", "4.9.1").Error + Expect(err).ToNot(HaveOccurred()) + Expect(len(dbReleases)).To(Equal(1)) + Expect(dbReleases[0].SupportLevel).To(Equal(models.ReleaseImageSupportLevelEndOfLife)) + + }) + + It("Of dynamic release images", func() { + handler.releaseSources = models.ReleaseSources{ + { + OpenshiftVersion: swag.String("5.3"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelStable}, + }, + }, + }, + { + OpenshiftVersion: swag.String("5.2"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelStable}, + }, + }, + }, + { + OpenshiftVersion: swag.String("4.15"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelStable}, + }, + }, + }, + { + OpenshiftVersion: swag.String("4.13"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelStable}, + }, + }, + }, + { + OpenshiftVersion: swag.String("4.11"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelStable}, + }, + }, + }, + { + OpenshiftVersion: swag.String("4.9"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelStable}, + }, + }, + }, + } + + setGetSupportLevelsMock(supportLevelsClientMock, "4") + setGetSupportLevelsMock(supportLevelsClientMock, "5") + releasesClientMock.EXPECT(). + getReleases(OpenshiftReleaseChannelStable, "5.3", common.AMD64CPUArchitecture). + Return( + &releaseGraph{ + Nodes: []node{ + {Version: "5.3.0-ec.2"}, + }, + }, nil). + Times(1) + releasesClientMock.EXPECT(). + getReleases(OpenshiftReleaseChannelStable, "5.2", common.AMD64CPUArchitecture). + Return( + &releaseGraph{ + Nodes: []node{ + {Version: "5.2.0"}, + }, + }, nil). + Times(1) + + releasesClientMock.EXPECT(). + getReleases(OpenshiftReleaseChannelStable, "4.15", common.AMD64CPUArchitecture). + Return( + &releaseGraph{ + Nodes: []node{ + {Version: "4.15.0-ec.3"}, + }, + }, nil). + Times(1) + + releasesClientMock.EXPECT(). + getReleases(OpenshiftReleaseChannelStable, "4.13", common.AMD64CPUArchitecture). + Return( + &releaseGraph{ + Nodes: []node{ + {Version: "4.13.7"}, + }, + }, nil). + Times(1) + + releasesClientMock.EXPECT(). + getReleases(OpenshiftReleaseChannelStable, "4.11", common.AMD64CPUArchitecture). + Return( + &releaseGraph{ + Nodes: []node{ + {Version: "4.11.6"}, + }, + }, nil). + Times(1) + + releasesClientMock.EXPECT(). + getReleases(OpenshiftReleaseChannelStable, "4.9", common.AMD64CPUArchitecture). + Return( + &releaseGraph{ + Nodes: []node{ + {Version: "4.9.3"}, + }, + }, nil). + Times(1) + + err := handler.syncReleaseImagesWithErr() + Expect(err).ToNot(HaveOccurred()) + + dbReleases := []*common.ReleaseImage{} + + err = db.Find(&dbReleases, "version = ?", "5.3.0-ec.2").Error + Expect(err).ToNot(HaveOccurred()) + Expect(len(dbReleases)).To(Equal(1)) + Expect(dbReleases[0].SupportLevel).To(Equal(models.ReleaseImageSupportLevelBeta)) + + err = db.Find(&dbReleases, "version = ?", "5.2.0").Error + Expect(err).ToNot(HaveOccurred()) + Expect(len(dbReleases)).To(Equal(1)) + Expect(dbReleases[0].SupportLevel).To(Equal(models.ReleaseImageSupportLevelEndOfLife)) + + err = db.Find(&dbReleases, "version = ?", "4.15.0-ec.3").Error + Expect(err).ToNot(HaveOccurred()) + Expect(len(dbReleases)).To(Equal(1)) + Expect(dbReleases[0].SupportLevel).To(Equal(models.ReleaseImageSupportLevelBeta)) + + err = db.Find(&dbReleases, "version = ?", "4.13.7").Error + Expect(err).ToNot(HaveOccurred()) + Expect(len(dbReleases)).To(Equal(1)) + Expect(dbReleases[0].SupportLevel).To(Equal(models.ReleaseImageSupportLevelProduction)) + + err = db.Find(&dbReleases, "version = ?", "4.11.6").Error + Expect(err).ToNot(HaveOccurred()) + Expect(len(dbReleases)).To(Equal(1)) + Expect(dbReleases[0].SupportLevel).To(Equal(models.ReleaseImageSupportLevelMaintenance)) + + err = db.Find(&dbReleases, "version = ?", "4.9.3").Error + Expect(err).ToNot(HaveOccurred()) + Expect(len(dbReleases)).To(Equal(1)) + Expect(dbReleases[0].SupportLevel).To(Equal(models.ReleaseImageSupportLevelEndOfLife)) + }) + }) + + It("Should not have the same release in different channels", func() { + handler.releaseSources = models.ReleaseSources{ + { + OpenshiftVersion: swag.String("4.14"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.ARM64CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelStable, OpenshiftReleaseChannelCandidate}, + }, + }, + }, + } + + setGetSupportLevelsMock(supportLevelsClientMock, "4") + releasesClientMock.EXPECT(). + getReleases(OpenshiftReleaseChannelStable, "4.14", common.ARM64CPUArchitecture). + Return( + &releaseGraph{ + Nodes: []node{ + {Version: "4.14.2"}, + }, + }, nil). + Times(1) + + releasesClientMock.EXPECT(). + getReleases(OpenshiftReleaseChannelCandidate, "4.14", common.ARM64CPUArchitecture). + Return( + &releaseGraph{ + Nodes: []node{ + {Version: "4.14.2"}, + {Version: "4.14.3"}, + }, + }, nil). + Times(1) + + err := handler.syncReleaseImagesWithErr() + Expect(err).ToNot(HaveOccurred()) + + var dbReleases []*common.ReleaseImage + err = handler.db.Find( + &dbReleases, + "version = ? AND channel = ? AND cpu_architecture = ?", + "4.14.2", + OpenshiftReleaseChannelCandidate, + common.ARM64CPUArchitecture, + ).Error + Expect(err).ToNot(HaveOccurred()) + Expect(dbReleases).To(BeEmpty()) + + err = handler.db.Find( + &dbReleases, + "version = ? AND channel = ? AND cpu_architecture = ?", + "4.14.2", + OpenshiftReleaseChannelStable, + common.ARM64CPUArchitecture, + ).Error + Expect(err).ToNot(HaveOccurred()) + Expect(len(dbReleases)).To(Equal(1)) + + err = handler.db.Find( + &dbReleases, + "version = ? AND channel = ? AND cpu_architecture = ?", + "4.14.3", + OpenshiftReleaseChannelCandidate, + common.ARM64CPUArchitecture, + ).Error + Expect(err).ToNot(HaveOccurred()) + Expect(len(dbReleases)).To(Equal(1)) + }) + + It("Should not have the same release twice", func() { + handler.releaseSources = models.ReleaseSources{ + { + OpenshiftVersion: swag.String("4.14"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.ARM64CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelStable}, + }, + }, + }, + } + + setGetSupportLevelsMock(supportLevelsClientMock, "4") + releasesClientMock.EXPECT(). + getReleases(OpenshiftReleaseChannelStable, "4.14", common.ARM64CPUArchitecture). + Return( + &releaseGraph{ + Nodes: []node{ + {Version: "4.14.2"}, + {Version: "4.14.2"}, + }, + }, nil). + Times(1) + + err := handler.syncReleaseImagesWithErr() + Expect(err).ToNot(HaveOccurred()) + + var dbReleases []*common.ReleaseImage + err = handler.db.Find( + &dbReleases, + "version = ? AND channel = ? AND cpu_architecture = ?", + "4.14.2", + OpenshiftReleaseChannelStable, + common.ARM64CPUArchitecture, + ).Error + Expect(err).ToNot(HaveOccurred()) + Expect(len(dbReleases)).To(Equal(1)) + }) + + Context("Should merge static and dynamic releases correctly", func() { + It("With conflicts", func() { + handler.staticReleaseImages = models.ReleaseImages{ + { + OpenshiftVersion: swag.String("4.14"), + Version: swag.String("4.14.1"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + CPUArchitectures: []string{common.X86CPUArchitecture}, + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.14.1-x86_64"), + Default: false, + SupportLevel: models.OpenshiftVersionSupportLevelMaintenance, + }, + { + OpenshiftVersion: swag.String("4.14"), + Version: swag.String("4.14.2"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + CPUArchitectures: []string{common.X86CPUArchitecture}, + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.14.2-x86_64"), + Default: false, + SupportLevel: models.OpenshiftVersionSupportLevelMaintenance, + }, + } + + handler.releaseSources = models.ReleaseSources{ + { + OpenshiftVersion: swag.String("4.14"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelStable}, + }, + }, + }, + } + + setGetSupportLevelsMock(supportLevelsClientMock, "4") + releasesClientMock.EXPECT(). + getReleases(OpenshiftReleaseChannelStable, "4.14", common.AMD64CPUArchitecture). + Return(&releaseGraph{ + Nodes: []node{ + {Version: "4.14.1"}, + {Version: "4.14.3"}, + }, + }, nil). + Times(1) + + err := handler.syncReleaseImagesWithErr() + Expect(err).ToNot(HaveOccurred()) + + var releaseImages []*common.ReleaseImage + err = handler.db.Find(&releaseImages, "version", "4.14.1").Error + Expect(err).ToNot(HaveOccurred()) + Expect(releaseImages).ToNot(BeEmpty()) + Expect(releaseImages[0].SupportLevel).To(Equal(models.OpenshiftVersionSupportLevelMaintenance)) + }) + + It("Without conflicts", func() { + handler.staticReleaseImages = models.ReleaseImages{ + { + OpenshiftVersion: swag.String("4.14"), + Version: swag.String("4.14.1"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + CPUArchitectures: []string{common.X86CPUArchitecture}, + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.14.1-x86_64"), + Default: false, + SupportLevel: models.OpenshiftVersionSupportLevelMaintenance, + }, + { + OpenshiftVersion: swag.String("4.14"), + Version: swag.String("4.14.2"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + CPUArchitectures: []string{common.X86CPUArchitecture}, + URL: swag.String("quay.io/openshift-release-dev/ocp-release:4.14.2-x86_64"), + Default: false, + SupportLevel: models.OpenshiftVersionSupportLevelMaintenance, + }, + } + + handler.releaseSources = models.ReleaseSources{ + { + OpenshiftVersion: swag.String("4.14"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Channels: []string{OpenshiftReleaseChannelStable}, + }, + }, + }, + } + + setGetSupportLevelsMock(supportLevelsClientMock, "4") + releasesClientMock.EXPECT(). + getReleases(OpenshiftReleaseChannelStable, "4.14", common.AMD64CPUArchitecture). + Return(&releaseGraph{ + Nodes: []node{ + {Version: "4.14.4"}, + {Version: "4.14.5"}, + }, + }, nil). + Times(1) + + err := handler.syncReleaseImagesWithErr() + Expect(err).ToNot(HaveOccurred()) + + var releaseImages []*common.ReleaseImage + err = handler.db.Find(&releaseImages).Error + Expect(err).ToNot(HaveOccurred()) + Expect(len(releaseImages)).To(Equal(4)) + }) + }) + + It("Should be successfull with valid static and dynamic release images extended scenario", func() { + handler.releaseSources = releaseSources + handler.staticReleaseImages = staticReleaseImages + + expectedResult := []CoreOpenshiftVersion{ + { + ReleaseImage: models.ReleaseImage{ + OpenshiftVersion: swag.String("4.10"), + Version: swag.String("4.10.1"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + SupportLevel: models.OpenshiftVersionSupportLevelEndOfLife, + URL: swag.String(getReleaseImageReference("4.10.1", common.X86CPUArchitecture)), + Default: false, + }, + Channel: OpenshiftReleaseChannelStable, + }, + { + ReleaseImage: models.ReleaseImage{ + OpenshiftVersion: swag.String("4.12"), + Version: swag.String("4.12.1"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + SupportLevel: models.OpenshiftVersionSupportLevelMaintenance, + URL: swag.String(getReleaseImageReference("4.12.1", common.X86CPUArchitecture)), + Default: false, + }, + Channel: OpenshiftReleaseChannelStable, + }, + { + ReleaseImage: models.ReleaseImage{ + OpenshiftVersion: swag.String("4.13"), + Version: swag.String("4.13.1"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + SupportLevel: models.ReleaseImageSupportLevelProduction, + URL: swag.String(getReleaseImageReference("4.13.1", common.X86CPUArchitecture)), + Default: false, + }, + Channel: OpenshiftReleaseChannelStable, + }, + { + ReleaseImage: models.ReleaseImage{ + OpenshiftVersion: swag.String("4.13"), + Version: swag.String("4.13.17"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + SupportLevel: models.ReleaseImageSupportLevelProduction, + URL: swag.String(getReleaseImageReference("4.13.17", common.X86CPUArchitecture)), + Default: false, + }, + Channel: OpenshiftReleaseChannelStable, + }, + { + ReleaseImage: models.ReleaseImage{ + OpenshiftVersion: swag.String("4.13"), + Version: swag.String("4.13.1"), + CPUArchitecture: swag.String(common.S390xCPUArchitecture), + SupportLevel: models.ReleaseImageSupportLevelProduction, + URL: swag.String(getReleaseImageReference("4.13.1", common.S390xCPUArchitecture)), + Default: false, + }, + Channel: OpenshiftReleaseChannelStable, + }, + { + ReleaseImage: models.ReleaseImage{ + OpenshiftVersion: swag.String("4.13"), + Version: swag.String("4.13.19"), + CPUArchitecture: swag.String(common.S390xCPUArchitecture), + SupportLevel: models.ReleaseImageSupportLevelProduction, + URL: swag.String(getReleaseImageReference("4.13.19", common.S390xCPUArchitecture)), + Default: false, + }, + Channel: OpenshiftReleaseChannelStable, + }, + { + ReleaseImage: models.ReleaseImage{ + OpenshiftVersion: swag.String("4.14"), + Version: swag.String("4.14.0"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + SupportLevel: models.ReleaseImageSupportLevelProduction, + URL: swag.String(getReleaseImageReference("4.14.0", common.X86CPUArchitecture)), + Default: false, + }, + Channel: OpenshiftReleaseChannelStable, + }, + { + ReleaseImage: models.ReleaseImage{ + OpenshiftVersion: swag.String("4.14"), + Version: swag.String("4.14.1"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + SupportLevel: models.ReleaseImageSupportLevelProduction, + URL: swag.String(getReleaseImageReference("4.14.1", common.X86CPUArchitecture)), + Default: false, + }, + Channel: OpenshiftReleaseChannelStable, + }, + { + ReleaseImage: models.ReleaseImage{ + OpenshiftVersion: swag.String("4.14"), + Version: swag.String("4.14.0-rc.1"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + SupportLevel: models.OpenshiftVersionSupportLevelBeta, + URL: swag.String(getReleaseImageReference("4.14.0-rc.1", common.X86CPUArchitecture)), + Default: false, + }, + Channel: OpenshiftReleaseChannelCandidate, + }, + { + ReleaseImage: models.ReleaseImage{ + OpenshiftVersion: swag.String("4.14"), + Version: swag.String("4.14.0-ec.2"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + SupportLevel: models.OpenshiftVersionSupportLevelBeta, + URL: swag.String(getReleaseImageReference("4.14.0-ec.2", common.X86CPUArchitecture)), + Default: false, + }, + Channel: OpenshiftReleaseChannelCandidate, + }, + { + ReleaseImage: models.ReleaseImage{ + OpenshiftVersion: swag.String("4.14"), + Version: swag.String("4.14.0"), + CPUArchitecture: swag.String(common.PowerCPUArchitecture), + SupportLevel: models.OpenshiftVersionSupportLevelBeta, + URL: swag.String(getReleaseImageReference("4.14.0", common.PowerCPUArchitecture)), + Default: false, + }, + Channel: OpenshiftReleaseChannelCandidate, + }, + { + ReleaseImage: models.ReleaseImage{ + OpenshiftVersion: swag.String("4.14"), + Version: swag.String("4.14.1"), + CPUArchitecture: swag.String(common.PowerCPUArchitecture), + SupportLevel: models.OpenshiftVersionSupportLevelBeta, + URL: swag.String(getReleaseImageReference("4.14.1", common.PowerCPUArchitecture)), + Default: false, + }, + Channel: OpenshiftReleaseChannelCandidate, + }, + { + ReleaseImage: models.ReleaseImage{ + OpenshiftVersion: swag.String("4.15"), + Version: swag.String("4.15.0-ec.2"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + SupportLevel: models.OpenshiftVersionSupportLevelBeta, + URL: swag.String(getReleaseImageReference("4.15.0-ec.2", common.X86CPUArchitecture)), + Default: false, + }, + Channel: OpenshiftReleaseChannelCandidate, + }, + { + ReleaseImage: models.ReleaseImage{ + OpenshiftVersion: swag.String("4.16-multi"), + Version: swag.String("4.16.0-ec.2-multi"), + CPUArchitecture: swag.String(common.MultiCPUArchitecture), + SupportLevel: models.OpenshiftVersionSupportLevelBeta, + URL: swag.String(getReleaseImageReference("4.16.0-ec.2", common.MultiCPUArchitecture)), + Default: false, + }, + Channel: OpenshiftReleaseChannelCandidate, + }, + { + ReleaseImage: models.ReleaseImage{ + OpenshiftVersion: swag.String("4.11"), + Version: swag.String("4.11.1"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + SupportLevel: models.OpenshiftVersionSupportLevelMaintenance, + URL: swag.String(getReleaseImageReference("4.11.1", common.X86CPUArchitecture)), + Default: false, + }, + }, + { + ReleaseImage: models.ReleaseImage{ + OpenshiftVersion: swag.String("4.13"), + Version: swag.String("4.13.2"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + SupportLevel: models.OpenshiftVersionSupportLevelProduction, + URL: swag.String(getReleaseImageReference("4.13.2", common.X86CPUArchitecture)), + Default: false, + }, + }, + { + ReleaseImage: models.ReleaseImage{ + OpenshiftVersion: swag.String("4.14"), + Version: swag.String("4.14.2"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + SupportLevel: models.OpenshiftVersionSupportLevelMaintenance, + URL: swag.String(getReleaseImageReference("4.14.2", common.X86CPUArchitecture)), + Default: false, + }, + }, + { + ReleaseImage: models.ReleaseImage{ + OpenshiftVersion: swag.String("4.14.3"), + Version: swag.String("4.14.3"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + SupportLevel: models.OpenshiftVersionSupportLevelProduction, + URL: swag.String(getReleaseImageReference("4.14.3", common.X86CPUArchitecture)), + Default: true, + }, + }, + { + ReleaseImage: models.ReleaseImage{ + OpenshiftVersion: swag.String("4.16"), + Version: swag.String("4.16.0-ec.2"), + CPUArchitecture: swag.String(common.X86CPUArchitecture), + SupportLevel: models.OpenshiftVersionSupportLevelBeta, + URL: swag.String(getReleaseImageReference("4.16.0-ec.2", common.X86CPUArchitecture)), + Default: false, + }, + }, + { + ReleaseImage: models.ReleaseImage{ + OpenshiftVersion: swag.String("4.14.4-multi"), + Version: swag.String("4.14.4-multi"), + CPUArchitecture: swag.String(common.MultiCPUArchitecture), + SupportLevel: models.OpenshiftVersionSupportLevelProduction, + URL: swag.String(getReleaseImageReference("4.14.4", common.MultiCPUArchitecture)), + Default: false, + }, + }, + } + + setGetReleasesMock(releasesClientMock, requestResponseParams) + setGetSupportLevelsMock(supportLevelsClientMock, "4") + + err := handler.syncReleaseImagesWithErr() + Expect(err).ToNot(HaveOccurred()) + + var results []CoreOpenshiftVersion + err = db.Model(&common.ReleaseImage{}). + Omit("id").Omit("created_at"). + Find(&results).Error + + Expect(err).ToNot(HaveOccurred()) + + for _, expectedResult := range expectedResult { + Expect(results).To(ContainElement(expectedResult)) + } + Expect(len(expectedResult)).To(Equal(len(results))) + }) + }) +}) diff --git a/internal/versions/api.go b/internal/versions/api.go index f008babc5ff..42beded67a9 100644 --- a/internal/versions/api.go +++ b/internal/versions/api.go @@ -29,17 +29,28 @@ type apiHandler struct { log logrus.FieldLogger versionsHandler *handler osImages OSImages + releaseSources models.ReleaseSources +} + +type NewAPIHandlerParams struct { + AuthzHandler auth.Authorizer + Versions Versions + Log logrus.FieldLogger + VersionsHandler *handler + OSImages OSImages + ReleaseSources models.ReleaseSources } var _ restapi.VersionsAPI = (*apiHandler)(nil) -func NewAPIHandler(log logrus.FieldLogger, versions Versions, authzHandler auth.Authorizer, versionsHandler *handler, osImages OSImages) restapi.VersionsAPI { +func NewAPIHandler(params NewAPIHandlerParams) restapi.VersionsAPI { return &apiHandler{ - authzHandler: authzHandler, - versions: versions, - log: log, - versionsHandler: versionsHandler, - osImages: osImages, + authzHandler: params.AuthzHandler, + versions: params.Versions, + log: params.Log, + versionsHandler: params.VersionsHandler, + osImages: params.OSImages, + releaseSources: params.ReleaseSources, } } @@ -153,6 +164,10 @@ func (h *apiHandler) V2ListSupportedOpenshiftVersions(ctx context.Context, param return operations.NewV2ListSupportedOpenshiftVersionsOK().WithPayload(openshiftVersions) } +func (r *apiHandler) V2ListReleaseSources(ctx context.Context, params operations.V2ListReleaseSourcesParams) middleware.Responder { + return operations.NewV2ListReleaseSourcesOK().WithPayload(r.releaseSources) +} + func getSupportLevel(releaseImage models.ReleaseImage) *string { if releaseImage.SupportLevel != "" { return &releaseImage.SupportLevel diff --git a/internal/versions/api_test.go b/internal/versions/api_test.go index 58abcc40764..cde0caeea4c 100644 --- a/internal/versions/api_test.go +++ b/internal/versions/api_test.go @@ -14,6 +14,7 @@ import ( . "github.com/onsi/gomega" "github.com/openshift/assisted-service/internal/common" "github.com/openshift/assisted-service/internal/oc" + "github.com/openshift/assisted-service/internal/releasesources" "github.com/openshift/assisted-service/models" auth "github.com/openshift/assisted-service/pkg/auth" "github.com/openshift/assisted-service/pkg/ocm" @@ -117,7 +118,13 @@ var _ = Describe("ListSupportedOpenshiftVersions", func() { osImages := readDefaultOsImages() versionsHandler := readDefaultReleaseImages(osImages) - h := NewAPIHandler(logger, versions, authzHandler, versionsHandler, osImages) + h := NewAPIHandler(NewAPIHandlerParams{ + Log: logger, + Versions: versions, + AuthzHandler: authzHandler, + VersionsHandler: versionsHandler, + OSImages: osImages, + }) reply := h.V2ListSupportedOpenshiftVersions(context.Background(), operations.V2ListSupportedOpenshiftVersionsParams{}) Expect(reply).Should(BeAssignableToTypeOf(operations.NewV2ListSupportedOpenshiftVersionsOK())) val, _ := reply.(*operations.V2ListSupportedOpenshiftVersionsOK) @@ -184,7 +191,13 @@ var _ = Describe("ListSupportedOpenshiftVersions", func() { osImages := readDefaultOsImages() versionsHandler, err := NewHandler(logger, mockRelease, models.ReleaseImages{}, nil, "", nil) Expect(err).ToNot(HaveOccurred()) - h := NewAPIHandler(logger, versions, authzHandler, versionsHandler, osImages) + h := NewAPIHandler(NewAPIHandlerParams{ + Log: logger, + Versions: versions, + AuthzHandler: authzHandler, + VersionsHandler: versionsHandler, + OSImages: osImages, + }) reply := h.V2ListSupportedOpenshiftVersions(context.Background(), operations.V2ListSupportedOpenshiftVersionsParams{}) Expect(reply).Should(BeAssignableToTypeOf(operations.NewV2ListSupportedOpenshiftVersionsOK())) val, _ := reply.(*operations.V2ListSupportedOpenshiftVersionsOK) @@ -208,7 +221,13 @@ var _ = Describe("ListSupportedOpenshiftVersions", func() { osImages := readDefaultOsImages() versionsHandler, err := NewHandler(logger, mockRelease, releaseImages, nil, "", nil) Expect(err).ToNot(HaveOccurred()) - h := NewAPIHandler(logger, versions, authzHandler, versionsHandler, osImages) + h := NewAPIHandler(NewAPIHandlerParams{ + Log: logger, + Versions: versions, + AuthzHandler: authzHandler, + VersionsHandler: versionsHandler, + OSImages: osImages, + }) reply := h.V2ListSupportedOpenshiftVersions(context.Background(), operations.V2ListSupportedOpenshiftVersionsParams{}) Expect(reply).Should(BeAssignableToTypeOf(operations.NewV2ListSupportedOpenshiftVersionsOK())) @@ -234,7 +253,13 @@ var _ = Describe("ListSupportedOpenshiftVersions", func() { osImages := readDefaultOsImages() versionsHandler, err := NewHandler(logger, mockRelease, releaseImages, nil, "", nil) Expect(err).ToNot(HaveOccurred()) - h := NewAPIHandler(logger, versions, authzHandler, versionsHandler, osImages) + h := NewAPIHandler(NewAPIHandlerParams{ + Log: logger, + Versions: versions, + AuthzHandler: authzHandler, + VersionsHandler: versionsHandler, + OSImages: osImages, + }) reply := h.V2ListSupportedOpenshiftVersions(context.Background(), operations.V2ListSupportedOpenshiftVersionsParams{}) Expect(reply).Should(BeAssignableToTypeOf(operations.NewV2ListSupportedOpenshiftVersionsOK())) @@ -278,7 +303,13 @@ var _ = Describe("ListSupportedOpenshiftVersions", func() { osImages := readDefaultOsImages() versionsHandler, err := NewHandler(logger, mockRelease, releaseImages, nil, "", nil) Expect(err).ToNot(HaveOccurred()) - h := NewAPIHandler(logger, versions, authzHandler, versionsHandler, osImages) + h := NewAPIHandler(NewAPIHandlerParams{ + Log: logger, + Versions: versions, + AuthzHandler: authzHandler, + VersionsHandler: versionsHandler, + OSImages: osImages, + }) reply := h.V2ListSupportedOpenshiftVersions(context.Background(), operations.V2ListSupportedOpenshiftVersionsParams{}) Expect(reply).Should(BeAssignableToTypeOf(operations.NewV2ListSupportedOpenshiftVersionsOK())) @@ -309,7 +340,13 @@ var _ = Describe("ListSupportedOpenshiftVersions", func() { osImages := readDefaultOsImages() versionsHandler, err := NewHandler(logger, mockRelease, releaseImages, nil, "", nil) Expect(err).ToNot(HaveOccurred()) - h := NewAPIHandler(logger, versions, authzHandler, versionsHandler, osImages) + h := NewAPIHandler(NewAPIHandlerParams{ + Log: logger, + Versions: versions, + AuthzHandler: authzHandler, + VersionsHandler: versionsHandler, + OSImages: osImages, + }) reply := h.V2ListSupportedOpenshiftVersions(context.Background(), operations.V2ListSupportedOpenshiftVersionsParams{}) Expect(reply).Should(BeAssignableToTypeOf(operations.NewV2ListSupportedOpenshiftVersionsOK())) @@ -365,7 +402,13 @@ var _ = Describe("Test list versions with capability restrictions", func() { versionsHandler, err := NewHandler(common.GetTestLog(), nil, defaultReleaseImages, nil, "", nil) Expect(err).ShouldNot(HaveOccurred()) - return NewAPIHandler(common.GetTestLog(), Versions{}, authzHandler, versionsHandler, osImages) + return NewAPIHandler(NewAPIHandlerParams{ + Log: common.GetTestLog(), + Versions: Versions{}, + AuthzHandler: authzHandler, + VersionsHandler: versionsHandler, + OSImages: osImages, + }) } hasMultiarch := func(versions models.OpenshiftVersions) bool { @@ -421,3 +464,78 @@ var _ = Describe("Test list versions with capability restrictions", func() { }) }) }) + +var _ = Describe("V2ListReleaseSources", func() { + + var ( + db *gorm.DB + dbName string + ) + + BeforeEach(func() { + db, dbName = common.PrepareTestDB() + }) + + AfterEach(func() { + common.DeleteTestDB(db, dbName) + }) + + It("Test success with non-empty release sources", func() { + releaseSources := models.ReleaseSources{ + { + OpenshiftVersion: swag.String("4.14"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Channels: []string{releasesources.OpenshiftReleaseChannelStable}, + }, + }, + }, + { + OpenshiftVersion: swag.String("4.15"), + UpgradeChannels: []*models.UpgradeChannel{ + { + CPUArchitecture: swag.String(common.X86CPUArchitecture), + Channels: []string{releasesources.OpenshiftReleaseChannelStable}, + }, + }, + }, + } + + apiHandler := NewAPIHandler(NewAPIHandlerParams{ + ReleaseSources: releaseSources, + }) + + middlewareResponder := apiHandler.V2ListReleaseSources( + context.Background(), + operations.V2ListReleaseSourcesParams{}, + ) + Expect(middlewareResponder).Should(BeAssignableToTypeOf(operations.NewV2ListReleaseSourcesOK())) + + reply, ok := middlewareResponder.(*operations.V2ListReleaseSourcesOK) + Expect(ok).To(BeTrue()) + + payload := reply.Payload + Expect(payload).To(Equal(releaseSources)) + }) + + It("Test success with empty release sources", func() { + releaseSources := models.ReleaseSources{} + + apiHandler := NewAPIHandler(NewAPIHandlerParams{ + ReleaseSources: releaseSources, + }) + + middlewareResponder := apiHandler.V2ListReleaseSources( + context.Background(), + operations.V2ListReleaseSourcesParams{}, + ) + Expect(middlewareResponder).Should(BeAssignableToTypeOf(operations.NewV2ListReleaseSourcesOK())) + + reply, ok := middlewareResponder.(*operations.V2ListReleaseSourcesOK) + Expect(ok).To(BeTrue()) + + payload := reply.Payload + Expect(payload).To(Equal(releaseSources)) + }) +}) diff --git a/models/openshift_version.go b/models/openshift_version.go index 2af83b7f0d4..34c46662d0a 100644 --- a/models/openshift_version.go +++ b/models/openshift_version.go @@ -33,7 +33,7 @@ type OpenshiftVersion struct { // Level of support of the version. // Required: true - // Enum: [beta production maintenance] + // Enum: [beta production maintenance end of life] SupportLevel *string `json:"support_level"` } @@ -81,7 +81,7 @@ var openshiftVersionTypeSupportLevelPropEnum []interface{} func init() { var res []string - if err := json.Unmarshal([]byte(`["beta","production","maintenance"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["beta","production","maintenance","end of life"]`), &res); err != nil { panic(err) } for _, v := range res { @@ -99,6 +99,9 @@ const ( // OpenshiftVersionSupportLevelMaintenance captures enum value "maintenance" OpenshiftVersionSupportLevelMaintenance string = "maintenance" + + // OpenshiftVersionSupportLevelEndOfLife captures enum value "end of life" + OpenshiftVersionSupportLevelEndOfLife string = "end of life" ) // prop value enum diff --git a/models/release_image.go b/models/release_image.go index c1f0861d9ef..e87ed885519 100644 --- a/models/release_image.go +++ b/models/release_image.go @@ -26,7 +26,7 @@ type ReleaseImage struct { CPUArchitecture *string `json:"cpu_architecture" gorm:"default:'x86_64'"` // List of CPU architectures provided by the image. - CPUArchitectures []string `json:"cpu_architectures"` + CPUArchitectures []string `json:"cpu_architectures" gorm:"type:text[]"` // Indication that the version is the recommended one. Default bool `json:"default,omitempty"` @@ -36,12 +36,12 @@ type ReleaseImage struct { OpenshiftVersion *string `json:"openshift_version"` // Level of support of the version. - // Enum: [beta production maintenance] + // Enum: [beta production maintenance end of life] SupportLevel string `json:"support_level,omitempty"` // The installation image of the OpenShift cluster. // Required: true - URL *string `json:"url"` + URL *string `json:"url" gorm:"primarykey"` // OCP version from the release metadata. // Required: true @@ -146,7 +146,7 @@ var releaseImageTypeSupportLevelPropEnum []interface{} func init() { var res []string - if err := json.Unmarshal([]byte(`["beta","production","maintenance"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["beta","production","maintenance","end of life"]`), &res); err != nil { panic(err) } for _, v := range res { @@ -164,6 +164,9 @@ const ( // ReleaseImageSupportLevelMaintenance captures enum value "maintenance" ReleaseImageSupportLevelMaintenance string = "maintenance" + + // ReleaseImageSupportLevelEndOfLife captures enum value "end of life" + ReleaseImageSupportLevelEndOfLife string = "end of life" ) // prop value enum diff --git a/models/release_source.go b/models/release_source.go new file mode 100644 index 00000000000..44dbf733bef --- /dev/null +++ b/models/release_source.go @@ -0,0 +1,136 @@ +// 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" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// ReleaseSource release source +// +// swagger:model release-source +type ReleaseSource struct { + + // Version of the OpenShift cluster. + // Required: true + OpenshiftVersion *string `json:"openshift_version"` + + // upgrade channels + // Required: true + UpgradeChannels []*UpgradeChannel `json:"upgrade_channels"` +} + +// Validate validates this release source +func (m *ReleaseSource) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateOpenshiftVersion(formats); err != nil { + res = append(res, err) + } + + if err := m.validateUpgradeChannels(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *ReleaseSource) validateOpenshiftVersion(formats strfmt.Registry) error { + + if err := validate.Required("openshift_version", "body", m.OpenshiftVersion); err != nil { + return err + } + + return nil +} + +func (m *ReleaseSource) validateUpgradeChannels(formats strfmt.Registry) error { + + if err := validate.Required("upgrade_channels", "body", m.UpgradeChannels); err != nil { + return err + } + + for i := 0; i < len(m.UpgradeChannels); i++ { + if swag.IsZero(m.UpgradeChannels[i]) { // not required + continue + } + + if m.UpgradeChannels[i] != nil { + if err := m.UpgradeChannels[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("upgrade_channels" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("upgrade_channels" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// ContextValidate validate this release source based on the context it is used +func (m *ReleaseSource) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateUpgradeChannels(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *ReleaseSource) contextValidateUpgradeChannels(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.UpgradeChannels); i++ { + + if m.UpgradeChannels[i] != nil { + if err := m.UpgradeChannels[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("upgrade_channels" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("upgrade_channels" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// MarshalBinary interface implementation +func (m *ReleaseSource) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *ReleaseSource) UnmarshalBinary(b []byte) error { + var res ReleaseSource + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/models/release_sources.go b/models/release_sources.go new file mode 100644 index 00000000000..c2e51cd789a --- /dev/null +++ b/models/release_sources.go @@ -0,0 +1,73 @@ +// 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" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// ReleaseSources release sources +// +// swagger:model release-sources +type ReleaseSources []*ReleaseSource + +// Validate validates this release sources +func (m ReleaseSources) Validate(formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + if swag.IsZero(m[i]) { // not required + continue + } + + if m[i] != nil { + if err := m[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// ContextValidate validate this release sources based on the context it is used +func (m ReleaseSources) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + + if m[i] != nil { + if err := m[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/models/upgrade_channel.go b/models/upgrade_channel.go new file mode 100644 index 00000000000..299d1d6f2c9 --- /dev/null +++ b/models/upgrade_channel.go @@ -0,0 +1,165 @@ +// 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" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// UpgradeChannel upgrade channel +// +// swagger:model upgrade-channel +type UpgradeChannel struct { + + // channels + // Required: true + Channels []string `json:"channels"` + + // The CPU architecture of the image. + // Required: true + // Enum: [x86_64 aarch64 arm64 ppc64le s390x multi] + CPUArchitecture *string `json:"cpu_architecture" gorm:"default:'x86_64'"` +} + +// Validate validates this upgrade channel +func (m *UpgradeChannel) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateChannels(formats); err != nil { + res = append(res, err) + } + + if err := m.validateCPUArchitecture(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +var upgradeChannelChannelsItemsEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["stable","candidate"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + upgradeChannelChannelsItemsEnum = append(upgradeChannelChannelsItemsEnum, v) + } +} + +func (m *UpgradeChannel) validateChannelsItemsEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, upgradeChannelChannelsItemsEnum, true); err != nil { + return err + } + return nil +} + +func (m *UpgradeChannel) validateChannels(formats strfmt.Registry) error { + + if err := validate.Required("channels", "body", m.Channels); err != nil { + return err + } + + for i := 0; i < len(m.Channels); i++ { + + // value enum + if err := m.validateChannelsItemsEnum("channels"+"."+strconv.Itoa(i), "body", m.Channels[i]); err != nil { + return err + } + + } + + return nil +} + +var upgradeChannelTypeCPUArchitecturePropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["x86_64","aarch64","arm64","ppc64le","s390x","multi"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + upgradeChannelTypeCPUArchitecturePropEnum = append(upgradeChannelTypeCPUArchitecturePropEnum, v) + } +} + +const ( + + // UpgradeChannelCPUArchitectureX8664 captures enum value "x86_64" + UpgradeChannelCPUArchitectureX8664 string = "x86_64" + + // UpgradeChannelCPUArchitectureAarch64 captures enum value "aarch64" + UpgradeChannelCPUArchitectureAarch64 string = "aarch64" + + // UpgradeChannelCPUArchitectureArm64 captures enum value "arm64" + UpgradeChannelCPUArchitectureArm64 string = "arm64" + + // UpgradeChannelCPUArchitecturePpc64le captures enum value "ppc64le" + UpgradeChannelCPUArchitecturePpc64le string = "ppc64le" + + // UpgradeChannelCPUArchitectureS390x captures enum value "s390x" + UpgradeChannelCPUArchitectureS390x string = "s390x" + + // UpgradeChannelCPUArchitectureMulti captures enum value "multi" + UpgradeChannelCPUArchitectureMulti string = "multi" +) + +// prop value enum +func (m *UpgradeChannel) validateCPUArchitectureEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, upgradeChannelTypeCPUArchitecturePropEnum, true); err != nil { + return err + } + return nil +} + +func (m *UpgradeChannel) validateCPUArchitecture(formats strfmt.Registry) error { + + if err := validate.Required("cpu_architecture", "body", m.CPUArchitecture); err != nil { + return err + } + + // value enum + if err := m.validateCPUArchitectureEnum("cpu_architecture", "body", *m.CPUArchitecture); err != nil { + return err + } + + return nil +} + +// ContextValidate validates this upgrade channel based on context it is used +func (m *UpgradeChannel) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *UpgradeChannel) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *UpgradeChannel) UnmarshalBinary(b []byte) error { + var res UpgradeChannel + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/openshift/template.yaml b/openshift/template.yaml index 288aef74872..9289e632fca 100644 --- a/openshift/template.yaml +++ b/openshift/template.yaml @@ -19,6 +19,14 @@ parameters: - name: RELEASE_IMAGES value: '[{"openshift_version":"4.9","cpu_architecture":"x86_64","cpu_architectures":["x86_64"],"url":"quay.io/openshift-release-dev/ocp-release:4.9.59-x86_64","version":"4.9.59"},{"openshift_version":"4.10","cpu_architecture":"x86_64","cpu_architectures":["x86_64"],"url":"quay.io/openshift-release-dev/ocp-release:4.10.67-x86_64","version":"4.10.67"},{"openshift_version":"4.10","cpu_architecture":"arm64","cpu_architectures":["arm64"],"url":"quay.io/openshift-release-dev/ocp-release:4.10.67-aarch64","version":"4.10.67"},{"openshift_version":"4.11","cpu_architecture":"x86_64","cpu_architectures":["x86_64"],"url":"quay.io/openshift-release-dev/ocp-release:4.11.58-x86_64","version":"4.11.58"},{"openshift_version":"4.11","cpu_architecture":"arm64","cpu_architectures":["arm64"],"url":"quay.io/openshift-release-dev/ocp-release:4.11.58-aarch64","version":"4.11.58"},{"openshift_version":"4.11.0-multi","cpu_architecture":"multi","cpu_architectures":["x86_64","arm64","ppc64le","s390x"],"url":"quay.io/openshift-release-dev/ocp-release:4.11.0-multi","version":"4.11.0-multi"},{"openshift_version":"4.12","cpu_architecture":"x86_64","cpu_architectures":["x86_64"],"url":"quay.io/openshift-release-dev/ocp-release:4.12.49-x86_64","version":"4.12.49"},{"openshift_version":"4.12","cpu_architecture":"arm64","cpu_architectures":["arm64"],"url":"quay.io/openshift-release-dev/ocp-release:4.12.49-aarch64","version":"4.12.49"},{"openshift_version":"4.12-multi","cpu_architecture":"multi","cpu_architectures":["x86_64","arm64","ppc64le","s390x"],"url":"quay.io/openshift-release-dev/ocp-release:4.12.49-multi","version":"4.12.49-multi"},{"openshift_version":"4.13","cpu_architecture":"x86_64","cpu_architectures":["x86_64"],"url":"quay.io/openshift-release-dev/ocp-release:4.13.32-x86_64","version":"4.13.32"},{"openshift_version":"4.13","cpu_architecture":"arm64","cpu_architectures":["arm64"],"url":"quay.io/openshift-release-dev/ocp-release:4.13.32-aarch64","version":"4.13.32"},{"openshift_version":"4.13-multi","cpu_architecture":"multi","cpu_architectures":["x86_64","arm64","ppc64le","s390x"],"url":"quay.io/openshift-release-dev/ocp-release:4.13.32-multi","version":"4.13.32-multi"},{"openshift_version":"4.14","cpu_architecture":"x86_64","cpu_architectures":["x86_64"],"url":"quay.io/openshift-release-dev/ocp-release:4.14.11-x86_64","version":"4.14.11","default":true},{"openshift_version":"4.14","cpu_architecture":"arm64","cpu_architectures":["arm64"],"url":"quay.io/openshift-release-dev/ocp-release:4.14.11-aarch64","version":"4.14.11"},{"openshift_version":"4.14-multi","cpu_architecture":"multi","cpu_architectures":["x86_64","arm64","ppc64le","s390x"],"url":"quay.io/openshift-release-dev/ocp-release:4.14.11-multi","version":"4.14.11-multi"},{"openshift_version":"4.15","cpu_architecture":"x86_64","cpu_architectures":["x86_64"],"url":"quay.io/openshift-release-dev/ocp-release:4.15.0-rc.5-x86_64","support_level":"beta","version":"4.15.0-rc.5"},{"openshift_version":"4.15","cpu_architecture":"arm64","cpu_architectures":["arm64"],"url":"quay.io/openshift-release-dev/ocp-release:4.15.0-rc.5-aarch64","support_level":"beta","version":"4.15.0-rc.5"},{"openshift_version":"4.15-multi","cpu_architecture":"multi","cpu_architectures":["x86_64","arm64","ppc64le","s390x"],"url":"quay.io/openshift-release-dev/ocp-release:4.15.0-rc.5-multi","support_level":"beta","version":"4.15.0-rc.5-multi"}]' # release images required: false +- name: RELEASE_SOURCES + value: '[{"openshift_version":"4.11","upgrade_channels":[{"cpu_architecture":"x86_64","channels":["stable"]},{"cpu_architecture":"arm64","channels":["stable"]},{"cpu_architecture":"ppc64le","channels":["stable"]},{"cpu_architecture":"s390x","channels":["stable"]},{"cpu_architecture":"multi","channels":["stable"]}]},{"openshift_version":"4.12","upgrade_channels":[{"cpu_architecture":"x86_64","channels":["stable"]},{"cpu_architecture":"arm64","channels":["stable"]},{"cpu_architecture":"ppc64le","channels":["stable"]},{"cpu_architecture":"s390x","channels":["stable"]},{"cpu_architecture":"multi","channels":["stable"]}]},{"openshift_version":"4.13","upgrade_channels":[{"cpu_architecture":"x86_64","channels":["stable"]},{"cpu_architecture":"arm64","channels":["stable"]},{"cpu_architecture":"ppc64le","channels":["stable"]},{"cpu_architecture":"s390x","channels":["stable"]},{"cpu_architecture":"multi","channels":["stable"]}]},{"openshift_version":"4.14","upgrade_channels":[{"cpu_architecture":"x86_64","channels":["stable","candidate"]},{"cpu_architecture":"arm64","channels":["stable","candidate"]},{"cpu_architecture":"ppc64le","channels":["stable","candidate"]},{"cpu_architecture":"s390x","channels":["stable","candidate"]},{"cpu_architecture":"multi","channels":["stable","candidate"]}]},{"openshift_version":"4.15","upgrade_channels":[{"cpu_architecture":"x86_64","channels":["stable","candidate"]},{"cpu_architecture":"arm64","channels":["stable","candidate"]},{"cpu_architecture":"ppc64le","channels":["stable","candidate"]},{"cpu_architecture":"s390x","channels":["stable","candidate"]},{"cpu_architecture":"multi","channels":["stable","candidate"]}]}]' + required: false +- name: OPENSHIFT_RELEASE_SYNCER_INTERVAL + value: "30m" + required: false +- name: OPENSHIFT_SUPPORT_LEVEL_API_BASE_URL + value: "https://access.redhat.com/product-life-cycles/api/v1/products" - name: MUST_GATHER_IMAGES value: '{"4.8-x86_64":{"cnv":"registry.redhat.io/container-native-virtualization/cnv-must-gather-rhel8:v2.6.5","ocs":"registry.redhat.io/ocs4/ocs-must-gather-rhel8:v4.8","lso":"registry.redhat.io/openshift4/ose-local-storage-mustgather-rhel8:v4.8"},"4.9-x86_64":{"cnv":"registry.redhat.io/container-native-virtualization/cnv-must-gather-rhel8:v4.9.3","ocs":"registry.redhat.io/odf4/ocs-must-gather-rhel8:v4.9","lso":"registry.redhat.io/openshift4/ose-local-storage-mustgather-rhel8:v4.9"},"4.10-x86_64":{"cnv":"registry.redhat.io/container-native-virtualization/cnv-must-gather-rhel8:v4.10.0","ocs":"registry.redhat.io/odf4/ocs-must-gather-rhel8:v4.10","lso":"registry.redhat.io/openshift4/ose-local-storage-mustgather-rhel8:v4.10"},"4.10-arm64":{"ocs":"registry.redhat.io/odf4/ocs-must-gather-rhel8:v4.10","lso":"registry.redhat.io/openshift4/ose-local-storage-mustgather-rhel8:v4.10"}}' # must-gather images required: false @@ -465,6 +473,12 @@ objects: value: ${INSTALLER_CACHE_CAPACITY} - name: ENABLE_OKD_SUPPORT value: ${ENABLE_OKD_SUPPORT} + - name: RELEASE_SOURCES + value: ${RELEASE_SOURCES} + - name: OPENSHIFT_RELEASE_SYNCER_INTERVAL + value: ${OPENSHIFT_RELEASE_SYNCER_INTERVAL} + - name: OPENSHIFT_SUPPORT_LEVEL_API_BASE_URL + value: ${OPENSHIFT_SUPPORT_LEVEL_API_BASE_URL} volumeMounts: - name: route53-creds mountPath: "/etc/.aws" diff --git a/pkg/auth/auth_assisted_service_mock_test.go b/pkg/auth/auth_assisted_service_mock_test.go index 0b601e6310f..02cea8227e7 100644 --- a/pkg/auth/auth_assisted_service_mock_test.go +++ b/pkg/auth/auth_assisted_service_mock_test.go @@ -304,6 +304,12 @@ func (f fakeVersionsAPI) V2ListSupportedOpenshiftVersions( return versionsapi.NewV2ListSupportedOpenshiftVersionsOK() } +func (f fakeVersionsAPI) V2ListReleaseSources( + _ context.Context, + _ versionsapi.V2ListReleaseSourcesParams) middleware.Responder { + return versionsapi.NewV2ListReleaseSourcesOK() +} + type fakeManagedDomainsAPI struct{} func (f fakeManagedDomainsAPI) V2ListManagedDomains( diff --git a/restapi/configure_assisted_install.go b/restapi/configure_assisted_install.go index 8243cc71d7b..5ecc07d70e4 100644 --- a/restapi/configure_assisted_install.go +++ b/restapi/configure_assisted_install.go @@ -282,6 +282,9 @@ type VersionsAPI interface { /* V2ListComponentVersions List of component versions. */ V2ListComponentVersions(ctx context.Context, params versions.V2ListComponentVersionsParams) middleware.Responder + /* V2ListReleaseSources Retrieves openshift release sources configuration. */ + V2ListReleaseSources(ctx context.Context, params versions.V2ListReleaseSourcesParams) middleware.Responder + /* V2ListSupportedOpenshiftVersions Retrieves the list of OpenShift supported versions. */ V2ListSupportedOpenshiftVersions(ctx context.Context, params versions.V2ListSupportedOpenshiftVersionsParams) middleware.Responder } @@ -693,6 +696,11 @@ func HandlerAPI(c Config) (http.Handler, *operations.AssistedInstallAPI, error) ctx = storeAuth(ctx, principal) return c.InstallerAPI.V2ListHosts(ctx, params) }) + api.VersionsV2ListReleaseSourcesHandler = versions.V2ListReleaseSourcesHandlerFunc(func(params versions.V2ListReleaseSourcesParams, principal interface{}) middleware.Responder { + ctx := params.HTTPRequest.Context() + ctx = storeAuth(ctx, principal) + return c.VersionsAPI.V2ListReleaseSources(ctx, params) + }) api.VersionsV2ListSupportedOpenshiftVersionsHandler = versions.V2ListSupportedOpenshiftVersionsHandlerFunc(func(params versions.V2ListSupportedOpenshiftVersionsParams, principal interface{}) middleware.Responder { ctx := params.HTTPRequest.Context() ctx = storeAuth(ctx, principal) diff --git a/restapi/embedded_spec.go b/restapi/embedded_spec.go index 5270542cf75..32cbb1a2777 100644 --- a/restapi/embedded_spec.go +++ b/restapi/embedded_spec.go @@ -5691,6 +5691,50 @@ func init() { } } }, + "/v2/release-sources": { + "get": { + "security": [ + { + "userAuth": [ + "admin", + "read-only-admin", + "user" + ] + } + ], + "description": "Retrieves openshift release sources configuration.", + "tags": [ + "versions" + ], + "operationId": "v2ListReleaseSources", + "responses": { + "200": { + "description": "Success.", + "schema": { + "$ref": "#/definitions/release-sources" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/error" + } + }, + "500": { + "description": "Error.", + "schema": { + "$ref": "#/definitions/error" + } + }, + "503": { + "description": "Unavailable.", + "schema": { + "$ref": "#/definitions/error" + } + } + } + } + }, "/v2/support-levels/architectures": { "get": { "security": [ @@ -9520,7 +9564,8 @@ func init() { "enum": [ "beta", "production", - "maintenance" + "maintenance", + "end of life" ] } } @@ -9846,7 +9891,8 @@ func init() { "type": "array", "items": { "type": "string" - } + }, + "x-go-custom-tag": "gorm:\"type:text[]\"" }, "default": { "description": "Indication that the version is the recommended one.", @@ -9862,12 +9908,14 @@ func init() { "enum": [ "beta", "production", - "maintenance" + "maintenance", + "end of life" ] }, "url": { "description": "The installation image of the OpenShift cluster.", - "type": "string" + "type": "string", + "x-go-custom-tag": "gorm:\"primarykey\"" }, "version": { "description": "OCP version from the release metadata.", @@ -9881,6 +9929,31 @@ func init() { "$ref": "#/definitions/release-image" } }, + "release-source": { + "type": "object", + "required": [ + "openshift_version", + "upgrade_channels" + ], + "properties": { + "openshift_version": { + "description": "Version of the OpenShift cluster.", + "type": "string" + }, + "upgrade_channels": { + "type": "array", + "items": { + "$ref": "#/definitions/upgrade-channel" + } + } + } + }, + "release-sources": { + "type": "array", + "items": { + "$ref": "#/definitions/release-source" + } + }, "route": { "type": "object", "properties": { @@ -10164,6 +10237,39 @@ func init() { } } }, + "upgrade-channel": { + "type": "object", + "required": [ + "cpu_architecture", + "channels" + ], + "properties": { + "channels": { + "type": "array", + "items": { + "description": "Release channel.", + "type": "string", + "enum": [ + "stable", + "candidate" + ] + } + }, + "cpu_architecture": { + "description": "The CPU architecture of the image.", + "type": "string", + "enum": [ + "x86_64", + "aarch64", + "arm64", + "ppc64le", + "s390x", + "multi" + ], + "x-go-custom-tag": "gorm:\"default:'x86_64'\"" + } + } + }, "upgrade_agent_request": { "type": "object", "properties": { @@ -16228,6 +16334,50 @@ func init() { } } }, + "/v2/release-sources": { + "get": { + "security": [ + { + "userAuth": [ + "admin", + "read-only-admin", + "user" + ] + } + ], + "description": "Retrieves openshift release sources configuration.", + "tags": [ + "versions" + ], + "operationId": "v2ListReleaseSources", + "responses": { + "200": { + "description": "Success.", + "schema": { + "$ref": "#/definitions/release-sources" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/error" + } + }, + "500": { + "description": "Error.", + "schema": { + "$ref": "#/definitions/error" + } + }, + "503": { + "description": "Unavailable.", + "schema": { + "$ref": "#/definitions/error" + } + } + } + } + }, "/v2/support-levels/architectures": { "get": { "security": [ @@ -20133,7 +20283,8 @@ func init() { "enum": [ "beta", "production", - "maintenance" + "maintenance", + "end of life" ] } } @@ -20459,7 +20610,8 @@ func init() { "type": "array", "items": { "type": "string" - } + }, + "x-go-custom-tag": "gorm:\"type:text[]\"" }, "default": { "description": "Indication that the version is the recommended one.", @@ -20475,12 +20627,14 @@ func init() { "enum": [ "beta", "production", - "maintenance" + "maintenance", + "end of life" ] }, "url": { "description": "The installation image of the OpenShift cluster.", - "type": "string" + "type": "string", + "x-go-custom-tag": "gorm:\"primarykey\"" }, "version": { "description": "OCP version from the release metadata.", @@ -20494,6 +20648,31 @@ func init() { "$ref": "#/definitions/release-image" } }, + "release-source": { + "type": "object", + "required": [ + "openshift_version", + "upgrade_channels" + ], + "properties": { + "openshift_version": { + "description": "Version of the OpenShift cluster.", + "type": "string" + }, + "upgrade_channels": { + "type": "array", + "items": { + "$ref": "#/definitions/upgrade-channel" + } + } + } + }, + "release-sources": { + "type": "array", + "items": { + "$ref": "#/definitions/release-source" + } + }, "route": { "type": "object", "properties": { @@ -20751,6 +20930,39 @@ func init() { } } }, + "upgrade-channel": { + "type": "object", + "required": [ + "cpu_architecture", + "channels" + ], + "properties": { + "channels": { + "type": "array", + "items": { + "description": "Release channel.", + "type": "string", + "enum": [ + "stable", + "candidate" + ] + } + }, + "cpu_architecture": { + "description": "The CPU architecture of the image.", + "type": "string", + "enum": [ + "x86_64", + "aarch64", + "arm64", + "ppc64le", + "s390x", + "multi" + ], + "x-go-custom-tag": "gorm:\"default:'x86_64'\"" + } + } + }, "upgrade_agent_request": { "type": "object", "properties": { diff --git a/restapi/operations/assisted_install_api.go b/restapi/operations/assisted_install_api.go index 1ce82410a37..20aaaf299ac 100644 --- a/restapi/operations/assisted_install_api.go +++ b/restapi/operations/assisted_install_api.go @@ -222,6 +222,9 @@ func NewAssistedInstallAPI(spec *loads.Document) *AssistedInstallAPI { InstallerV2ListHostsHandler: installer.V2ListHostsHandlerFunc(func(params installer.V2ListHostsParams, principal interface{}) middleware.Responder { return middleware.NotImplemented("operation installer.V2ListHosts has not yet been implemented") }), + VersionsV2ListReleaseSourcesHandler: versions.V2ListReleaseSourcesHandlerFunc(func(params versions.V2ListReleaseSourcesParams, principal interface{}) middleware.Responder { + return middleware.NotImplemented("operation versions.V2ListReleaseSources has not yet been implemented") + }), VersionsV2ListSupportedOpenshiftVersionsHandler: versions.V2ListSupportedOpenshiftVersionsHandlerFunc(func(params versions.V2ListSupportedOpenshiftVersionsParams, principal interface{}) middleware.Responder { return middleware.NotImplemented("operation versions.V2ListSupportedOpenshiftVersions has not yet been implemented") }), @@ -481,6 +484,8 @@ type AssistedInstallAPI struct { EventsV2ListEventsHandler events.V2ListEventsHandler // InstallerV2ListHostsHandler sets the operation handler for the v2 list hosts operation InstallerV2ListHostsHandler installer.V2ListHostsHandler + // VersionsV2ListReleaseSourcesHandler sets the operation handler for the v2 list release sources operation + VersionsV2ListReleaseSourcesHandler versions.V2ListReleaseSourcesHandler // VersionsV2ListSupportedOpenshiftVersionsHandler sets the operation handler for the v2 list supported openshift versions operation VersionsV2ListSupportedOpenshiftVersionsHandler versions.V2ListSupportedOpenshiftVersionsHandler // InstallerV2PostStepReplyHandler sets the operation handler for the v2 post step reply operation @@ -789,6 +794,9 @@ func (o *AssistedInstallAPI) Validate() error { if o.InstallerV2ListHostsHandler == nil { unregistered = append(unregistered, "installer.V2ListHostsHandler") } + if o.VersionsV2ListReleaseSourcesHandler == nil { + unregistered = append(unregistered, "versions.V2ListReleaseSourcesHandler") + } if o.VersionsV2ListSupportedOpenshiftVersionsHandler == nil { unregistered = append(unregistered, "versions.V2ListSupportedOpenshiftVersionsHandler") } @@ -1194,6 +1202,10 @@ func (o *AssistedInstallAPI) initHandlerCache() { if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } + o.handlers["GET"]["/v2/release-sources"] = versions.NewV2ListReleaseSources(o.context, o.VersionsV2ListReleaseSourcesHandler) + if o.handlers["GET"] == nil { + o.handlers["GET"] = make(map[string]http.Handler) + } o.handlers["GET"]["/v2/openshift-versions"] = versions.NewV2ListSupportedOpenshiftVersions(o.context, o.VersionsV2ListSupportedOpenshiftVersionsHandler) if o.handlers["POST"] == nil { o.handlers["POST"] = make(map[string]http.Handler) diff --git a/restapi/operations/versions/v2_list_release_sources.go b/restapi/operations/versions/v2_list_release_sources.go new file mode 100644 index 00000000000..a85f804a32d --- /dev/null +++ b/restapi/operations/versions/v2_list_release_sources.go @@ -0,0 +1,69 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package versions + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" +) + +// V2ListReleaseSourcesHandlerFunc turns a function with the right signature into a v2 list release sources handler +type V2ListReleaseSourcesHandlerFunc func(V2ListReleaseSourcesParams, interface{}) middleware.Responder + +// Handle executing the request and returning a response +func (fn V2ListReleaseSourcesHandlerFunc) Handle(params V2ListReleaseSourcesParams, principal interface{}) middleware.Responder { + return fn(params, principal) +} + +// V2ListReleaseSourcesHandler interface for that can handle valid v2 list release sources params +type V2ListReleaseSourcesHandler interface { + Handle(V2ListReleaseSourcesParams, interface{}) middleware.Responder +} + +// NewV2ListReleaseSources creates a new http.Handler for the v2 list release sources operation +func NewV2ListReleaseSources(ctx *middleware.Context, handler V2ListReleaseSourcesHandler) *V2ListReleaseSources { + return &V2ListReleaseSources{Context: ctx, Handler: handler} +} + +/* + V2ListReleaseSources swagger:route GET /v2/release-sources versions v2ListReleaseSources + +Retrieves openshift release sources configuration. +*/ +type V2ListReleaseSources struct { + Context *middleware.Context + Handler V2ListReleaseSourcesHandler +} + +func (o *V2ListReleaseSources) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewV2ListReleaseSourcesParams() + uprinc, aCtx, err := o.Context.Authorize(r, route) + if err != nil { + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + if aCtx != nil { + *r = *aCtx + } + var principal interface{} + if uprinc != nil { + principal = uprinc.(interface{}) // this is really a interface{}, I promise + } + + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params, principal) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/restapi/operations/versions/v2_list_release_sources_parameters.go b/restapi/operations/versions/v2_list_release_sources_parameters.go new file mode 100644 index 00000000000..ff24d3d2f2a --- /dev/null +++ b/restapi/operations/versions/v2_list_release_sources_parameters.go @@ -0,0 +1,46 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package versions + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime/middleware" +) + +// NewV2ListReleaseSourcesParams creates a new V2ListReleaseSourcesParams object +// +// There are no default values defined in the spec. +func NewV2ListReleaseSourcesParams() V2ListReleaseSourcesParams { + + return V2ListReleaseSourcesParams{} +} + +// V2ListReleaseSourcesParams contains all the bound params for the v2 list release sources operation +// typically these are obtained from a http.Request +// +// swagger:parameters v2ListReleaseSources +type V2ListReleaseSourcesParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewV2ListReleaseSourcesParams() beforehand. +func (o *V2ListReleaseSourcesParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/restapi/operations/versions/v2_list_release_sources_responses.go b/restapi/operations/versions/v2_list_release_sources_responses.go new file mode 100644 index 00000000000..11df8eeb58c --- /dev/null +++ b/restapi/operations/versions/v2_list_release_sources_responses.go @@ -0,0 +1,197 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package versions + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/openshift/assisted-service/models" +) + +// V2ListReleaseSourcesOKCode is the HTTP code returned for type V2ListReleaseSourcesOK +const V2ListReleaseSourcesOKCode int = 200 + +/* +V2ListReleaseSourcesOK Success. + +swagger:response v2ListReleaseSourcesOK +*/ +type V2ListReleaseSourcesOK struct { + + /* + In: Body + */ + Payload models.ReleaseSources `json:"body,omitempty"` +} + +// NewV2ListReleaseSourcesOK creates V2ListReleaseSourcesOK with default headers values +func NewV2ListReleaseSourcesOK() *V2ListReleaseSourcesOK { + + return &V2ListReleaseSourcesOK{} +} + +// WithPayload adds the payload to the v2 list release sources o k response +func (o *V2ListReleaseSourcesOK) WithPayload(payload models.ReleaseSources) *V2ListReleaseSourcesOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the v2 list release sources o k response +func (o *V2ListReleaseSourcesOK) SetPayload(payload models.ReleaseSources) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *V2ListReleaseSourcesOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + payload := o.Payload + if payload == nil { + // return empty array + payload = models.ReleaseSources{} + } + + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// V2ListReleaseSourcesBadRequestCode is the HTTP code returned for type V2ListReleaseSourcesBadRequest +const V2ListReleaseSourcesBadRequestCode int = 400 + +/* +V2ListReleaseSourcesBadRequest Bad Request + +swagger:response v2ListReleaseSourcesBadRequest +*/ +type V2ListReleaseSourcesBadRequest struct { + + /* + In: Body + */ + Payload *models.Error `json:"body,omitempty"` +} + +// NewV2ListReleaseSourcesBadRequest creates V2ListReleaseSourcesBadRequest with default headers values +func NewV2ListReleaseSourcesBadRequest() *V2ListReleaseSourcesBadRequest { + + return &V2ListReleaseSourcesBadRequest{} +} + +// WithPayload adds the payload to the v2 list release sources bad request response +func (o *V2ListReleaseSourcesBadRequest) WithPayload(payload *models.Error) *V2ListReleaseSourcesBadRequest { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the v2 list release sources bad request response +func (o *V2ListReleaseSourcesBadRequest) SetPayload(payload *models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *V2ListReleaseSourcesBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(400) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// V2ListReleaseSourcesInternalServerErrorCode is the HTTP code returned for type V2ListReleaseSourcesInternalServerError +const V2ListReleaseSourcesInternalServerErrorCode int = 500 + +/* +V2ListReleaseSourcesInternalServerError Error. + +swagger:response v2ListReleaseSourcesInternalServerError +*/ +type V2ListReleaseSourcesInternalServerError struct { + + /* + In: Body + */ + Payload *models.Error `json:"body,omitempty"` +} + +// NewV2ListReleaseSourcesInternalServerError creates V2ListReleaseSourcesInternalServerError with default headers values +func NewV2ListReleaseSourcesInternalServerError() *V2ListReleaseSourcesInternalServerError { + + return &V2ListReleaseSourcesInternalServerError{} +} + +// WithPayload adds the payload to the v2 list release sources internal server error response +func (o *V2ListReleaseSourcesInternalServerError) WithPayload(payload *models.Error) *V2ListReleaseSourcesInternalServerError { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the v2 list release sources internal server error response +func (o *V2ListReleaseSourcesInternalServerError) SetPayload(payload *models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *V2ListReleaseSourcesInternalServerError) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(500) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// V2ListReleaseSourcesServiceUnavailableCode is the HTTP code returned for type V2ListReleaseSourcesServiceUnavailable +const V2ListReleaseSourcesServiceUnavailableCode int = 503 + +/* +V2ListReleaseSourcesServiceUnavailable Unavailable. + +swagger:response v2ListReleaseSourcesServiceUnavailable +*/ +type V2ListReleaseSourcesServiceUnavailable struct { + + /* + In: Body + */ + Payload *models.Error `json:"body,omitempty"` +} + +// NewV2ListReleaseSourcesServiceUnavailable creates V2ListReleaseSourcesServiceUnavailable with default headers values +func NewV2ListReleaseSourcesServiceUnavailable() *V2ListReleaseSourcesServiceUnavailable { + + return &V2ListReleaseSourcesServiceUnavailable{} +} + +// WithPayload adds the payload to the v2 list release sources service unavailable response +func (o *V2ListReleaseSourcesServiceUnavailable) WithPayload(payload *models.Error) *V2ListReleaseSourcesServiceUnavailable { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the v2 list release sources service unavailable response +func (o *V2ListReleaseSourcesServiceUnavailable) SetPayload(payload *models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *V2ListReleaseSourcesServiceUnavailable) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(503) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} diff --git a/restapi/operations/versions/v2_list_release_sources_urlbuilder.go b/restapi/operations/versions/v2_list_release_sources_urlbuilder.go new file mode 100644 index 00000000000..a62348664ae --- /dev/null +++ b/restapi/operations/versions/v2_list_release_sources_urlbuilder.go @@ -0,0 +1,87 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package versions + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" +) + +// V2ListReleaseSourcesURL generates an URL for the v2 list release sources operation +type V2ListReleaseSourcesURL struct { + _basePath string +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *V2ListReleaseSourcesURL) WithBasePath(bp string) *V2ListReleaseSourcesURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *V2ListReleaseSourcesURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *V2ListReleaseSourcesURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/v2/release-sources" + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/api/assisted-install" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *V2ListReleaseSourcesURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *V2ListReleaseSourcesURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *V2ListReleaseSourcesURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on V2ListReleaseSourcesURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on V2ListReleaseSourcesURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *V2ListReleaseSourcesURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/swagger.yaml b/swagger.yaml index e5151f06b0b..1f2df5b04b2 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -3131,6 +3131,32 @@ paths: schema: $ref: '#/definitions/error' + /v2/release-sources: + get: + tags: + - versions + security: + - userAuth: [admin, read-only-admin, user] + operationId: v2ListReleaseSources + description: Retrieves openshift release sources configuration. + responses: + "200": + description: Success. + schema: + $ref: '#/definitions/release-sources' + "400": + description: Bad Request + schema: + $ref: '#/definitions/error' + "500": + description: Error. + schema: + $ref: '#/definitions/error' + "503": + description: Unavailable. + schema: + $ref: '#/definitions/error' + /v2/clusters/{cluster_id}/manifests/files: get: tags: @@ -4906,7 +4932,7 @@ definitions: type: array x-nullable: true description: JSON-formatted string of additional HTTP headers when fetching the ignition. - items: + items: $ref: '#/definitions/ignition-endpoint-http-headers-params' node_labels: type: array @@ -6937,7 +6963,7 @@ definitions: description: Name of the version to be presented to the user. support_level: type: string - enum: [beta, production, maintenance] + enum: [beta, production, maintenance, end of life] description: Level of support of the version. default: type: boolean @@ -7002,9 +7028,11 @@ definitions: items: type: string description: List of CPU architectures provided by the image. + x-go-custom-tag: gorm:"type:text[]" url: type: string description: The installation image of the OpenShift cluster. + x-go-custom-tag: gorm:"primarykey" version: type: string description: OCP version from the release metadata. @@ -7013,9 +7041,46 @@ definitions: description: Indication that the version is the recommended one. support_level: type: string - enum: [beta, production, maintenance] + enum: [beta, production, maintenance, end of life] description: Level of support of the version. + upgrade-channel: + type: object + required: + - cpu_architecture + - channels + properties: + cpu_architecture: + type: string + enum: ['x86_64', 'aarch64', 'arm64','ppc64le','s390x','multi'] + description: The CPU architecture of the image. + x-go-custom-tag: gorm:"default:'x86_64'" + channels: + type: array + items: + type: string + enum: [stable, candidate] + description: Release channel. + + release-source: + type: object + required: + - openshift_version + - upgrade_channels + properties: + openshift_version: + type: string + description: Version of the OpenShift cluster. + upgrade_channels: + type: array + items: + $ref: '#/definitions/upgrade-channel' + + release-sources: + type: array + items: + $ref: '#/definitions/release-source' + release-images: type: array items: diff --git a/tools/deploy_assisted_installer_configmap.py b/tools/deploy_assisted_installer_configmap.py index c0639a234f4..d8f96f801d3 100644 --- a/tools/deploy_assisted_installer_configmap.py +++ b/tools/deploy_assisted_installer_configmap.py @@ -48,6 +48,9 @@ def handle_arguments(): SERVICE = "assisted-service" IMAGE_SERVICE = "assisted-image-service" +RELEASE_SOURCES = os.environ.get("RELEASE_SOURCES", "") +OPENSHIFT_RELEASE_SYNCER_INTERVAL = os.environ.get("OPENSHIFT_RELEASE_SYNCER_INTERVAL", "30m") +OPENSHIFT_SUPPORT_LEVEL_API_BASE_URL = os.environ.get("OPENSHIFT_SUPPORT_LEVEL_API_BASE_URL", "https://access.redhat.com/product-life-cycles/api/v1/products") def get_deployment_tag(args): if args.deploy_manifest_tag: @@ -105,6 +108,9 @@ def main(): data = data.replace('REPLACE_HW_VALIDATOR_REQUIREMENTS', '"{}"'.format(deploy_options.hw_requirements)) data = data.replace('REPLACE_DISABLED_HOST_VALIDATIONS', '"{}"'.format(deploy_options.disabled_host_validations)) data = data.replace('REPLACE_DISABLED_STEPS', '"{}"'.format(deploy_options.disabled_steps)) + data = data.replace('REPLACE_RELEASE_SOURCES', '"{}"'.format(RELEASE_SOURCES)) + data = data.replace('REPLACE_OPENSHIFT_RELEASE_SYNCER_INTERVAL', '"{}"'.format(OPENSHIFT_RELEASE_SYNCER_INTERVAL)) + data = data.replace('REPLACE_OPENSHIFT_SUPPORT_LEVEL_API_BASE_URL', '"{}"'.format(OPENSHIFT_SUPPORT_LEVEL_API_BASE_URL)) versions = {"INSTALLER_IMAGE": "assisted-installer", "CONTROLLER_IMAGE": "assisted-installer-controller", diff --git a/vendor/github.com/openshift/assisted-service/client/versions/v2_list_release_sources_parameters.go b/vendor/github.com/openshift/assisted-service/client/versions/v2_list_release_sources_parameters.go new file mode 100644 index 00000000000..f80a78d7927 --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/client/versions/v2_list_release_sources_parameters.go @@ -0,0 +1,128 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package versions + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewV2ListReleaseSourcesParams creates a new V2ListReleaseSourcesParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewV2ListReleaseSourcesParams() *V2ListReleaseSourcesParams { + return &V2ListReleaseSourcesParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewV2ListReleaseSourcesParamsWithTimeout creates a new V2ListReleaseSourcesParams object +// with the ability to set a timeout on a request. +func NewV2ListReleaseSourcesParamsWithTimeout(timeout time.Duration) *V2ListReleaseSourcesParams { + return &V2ListReleaseSourcesParams{ + timeout: timeout, + } +} + +// NewV2ListReleaseSourcesParamsWithContext creates a new V2ListReleaseSourcesParams object +// with the ability to set a context for a request. +func NewV2ListReleaseSourcesParamsWithContext(ctx context.Context) *V2ListReleaseSourcesParams { + return &V2ListReleaseSourcesParams{ + Context: ctx, + } +} + +// NewV2ListReleaseSourcesParamsWithHTTPClient creates a new V2ListReleaseSourcesParams object +// with the ability to set a custom HTTPClient for a request. +func NewV2ListReleaseSourcesParamsWithHTTPClient(client *http.Client) *V2ListReleaseSourcesParams { + return &V2ListReleaseSourcesParams{ + HTTPClient: client, + } +} + +/* +V2ListReleaseSourcesParams contains all the parameters to send to the API endpoint + + for the v2 list release sources operation. + + Typically these are written to a http.Request. +*/ +type V2ListReleaseSourcesParams struct { + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the v2 list release sources params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *V2ListReleaseSourcesParams) WithDefaults() *V2ListReleaseSourcesParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the v2 list release sources params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *V2ListReleaseSourcesParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the v2 list release sources params +func (o *V2ListReleaseSourcesParams) WithTimeout(timeout time.Duration) *V2ListReleaseSourcesParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the v2 list release sources params +func (o *V2ListReleaseSourcesParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the v2 list release sources params +func (o *V2ListReleaseSourcesParams) WithContext(ctx context.Context) *V2ListReleaseSourcesParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the v2 list release sources params +func (o *V2ListReleaseSourcesParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the v2 list release sources params +func (o *V2ListReleaseSourcesParams) WithHTTPClient(client *http.Client) *V2ListReleaseSourcesParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the v2 list release sources params +func (o *V2ListReleaseSourcesParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WriteToRequest writes these params to a swagger request +func (o *V2ListReleaseSourcesParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/vendor/github.com/openshift/assisted-service/client/versions/v2_list_release_sources_responses.go b/vendor/github.com/openshift/assisted-service/client/versions/v2_list_release_sources_responses.go new file mode 100644 index 00000000000..77819c20c59 --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/client/versions/v2_list_release_sources_responses.go @@ -0,0 +1,303 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package versions + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + "github.com/openshift/assisted-service/models" +) + +// V2ListReleaseSourcesReader is a Reader for the V2ListReleaseSources structure. +type V2ListReleaseSourcesReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *V2ListReleaseSourcesReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewV2ListReleaseSourcesOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + case 400: + result := NewV2ListReleaseSourcesBadRequest() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 500: + result := NewV2ListReleaseSourcesInternalServerError() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 503: + result := NewV2ListReleaseSourcesServiceUnavailable() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + default: + return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + } +} + +// NewV2ListReleaseSourcesOK creates a V2ListReleaseSourcesOK with default headers values +func NewV2ListReleaseSourcesOK() *V2ListReleaseSourcesOK { + return &V2ListReleaseSourcesOK{} +} + +/* +V2ListReleaseSourcesOK describes a response with status code 200, with default header values. + +Success. +*/ +type V2ListReleaseSourcesOK struct { + Payload models.ReleaseSources +} + +// IsSuccess returns true when this v2 list release sources o k response has a 2xx status code +func (o *V2ListReleaseSourcesOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this v2 list release sources o k response has a 3xx status code +func (o *V2ListReleaseSourcesOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 list release sources o k response has a 4xx status code +func (o *V2ListReleaseSourcesOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this v2 list release sources o k response has a 5xx status code +func (o *V2ListReleaseSourcesOK) IsServerError() bool { + return false +} + +// IsCode returns true when this v2 list release sources o k response a status code equal to that given +func (o *V2ListReleaseSourcesOK) IsCode(code int) bool { + return code == 200 +} + +func (o *V2ListReleaseSourcesOK) Error() string { + return fmt.Sprintf("[GET /v2/release-sources][%d] v2ListReleaseSourcesOK %+v", 200, o.Payload) +} + +func (o *V2ListReleaseSourcesOK) String() string { + return fmt.Sprintf("[GET /v2/release-sources][%d] v2ListReleaseSourcesOK %+v", 200, o.Payload) +} + +func (o *V2ListReleaseSourcesOK) GetPayload() models.ReleaseSources { + return o.Payload +} + +func (o *V2ListReleaseSourcesOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewV2ListReleaseSourcesBadRequest creates a V2ListReleaseSourcesBadRequest with default headers values +func NewV2ListReleaseSourcesBadRequest() *V2ListReleaseSourcesBadRequest { + return &V2ListReleaseSourcesBadRequest{} +} + +/* +V2ListReleaseSourcesBadRequest describes a response with status code 400, with default header values. + +Bad Request +*/ +type V2ListReleaseSourcesBadRequest struct { + Payload *models.Error +} + +// IsSuccess returns true when this v2 list release sources bad request response has a 2xx status code +func (o *V2ListReleaseSourcesBadRequest) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this v2 list release sources bad request response has a 3xx status code +func (o *V2ListReleaseSourcesBadRequest) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 list release sources bad request response has a 4xx status code +func (o *V2ListReleaseSourcesBadRequest) IsClientError() bool { + return true +} + +// IsServerError returns true when this v2 list release sources bad request response has a 5xx status code +func (o *V2ListReleaseSourcesBadRequest) IsServerError() bool { + return false +} + +// IsCode returns true when this v2 list release sources bad request response a status code equal to that given +func (o *V2ListReleaseSourcesBadRequest) IsCode(code int) bool { + return code == 400 +} + +func (o *V2ListReleaseSourcesBadRequest) Error() string { + return fmt.Sprintf("[GET /v2/release-sources][%d] v2ListReleaseSourcesBadRequest %+v", 400, o.Payload) +} + +func (o *V2ListReleaseSourcesBadRequest) String() string { + return fmt.Sprintf("[GET /v2/release-sources][%d] v2ListReleaseSourcesBadRequest %+v", 400, o.Payload) +} + +func (o *V2ListReleaseSourcesBadRequest) GetPayload() *models.Error { + return o.Payload +} + +func (o *V2ListReleaseSourcesBadRequest) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewV2ListReleaseSourcesInternalServerError creates a V2ListReleaseSourcesInternalServerError with default headers values +func NewV2ListReleaseSourcesInternalServerError() *V2ListReleaseSourcesInternalServerError { + return &V2ListReleaseSourcesInternalServerError{} +} + +/* +V2ListReleaseSourcesInternalServerError describes a response with status code 500, with default header values. + +Error. +*/ +type V2ListReleaseSourcesInternalServerError struct { + Payload *models.Error +} + +// IsSuccess returns true when this v2 list release sources internal server error response has a 2xx status code +func (o *V2ListReleaseSourcesInternalServerError) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this v2 list release sources internal server error response has a 3xx status code +func (o *V2ListReleaseSourcesInternalServerError) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 list release sources internal server error response has a 4xx status code +func (o *V2ListReleaseSourcesInternalServerError) IsClientError() bool { + return false +} + +// IsServerError returns true when this v2 list release sources internal server error response has a 5xx status code +func (o *V2ListReleaseSourcesInternalServerError) IsServerError() bool { + return true +} + +// IsCode returns true when this v2 list release sources internal server error response a status code equal to that given +func (o *V2ListReleaseSourcesInternalServerError) IsCode(code int) bool { + return code == 500 +} + +func (o *V2ListReleaseSourcesInternalServerError) Error() string { + return fmt.Sprintf("[GET /v2/release-sources][%d] v2ListReleaseSourcesInternalServerError %+v", 500, o.Payload) +} + +func (o *V2ListReleaseSourcesInternalServerError) String() string { + return fmt.Sprintf("[GET /v2/release-sources][%d] v2ListReleaseSourcesInternalServerError %+v", 500, o.Payload) +} + +func (o *V2ListReleaseSourcesInternalServerError) GetPayload() *models.Error { + return o.Payload +} + +func (o *V2ListReleaseSourcesInternalServerError) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewV2ListReleaseSourcesServiceUnavailable creates a V2ListReleaseSourcesServiceUnavailable with default headers values +func NewV2ListReleaseSourcesServiceUnavailable() *V2ListReleaseSourcesServiceUnavailable { + return &V2ListReleaseSourcesServiceUnavailable{} +} + +/* +V2ListReleaseSourcesServiceUnavailable describes a response with status code 503, with default header values. + +Unavailable. +*/ +type V2ListReleaseSourcesServiceUnavailable struct { + Payload *models.Error +} + +// IsSuccess returns true when this v2 list release sources service unavailable response has a 2xx status code +func (o *V2ListReleaseSourcesServiceUnavailable) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this v2 list release sources service unavailable response has a 3xx status code +func (o *V2ListReleaseSourcesServiceUnavailable) IsRedirect() bool { + return false +} + +// IsClientError returns true when this v2 list release sources service unavailable response has a 4xx status code +func (o *V2ListReleaseSourcesServiceUnavailable) IsClientError() bool { + return false +} + +// IsServerError returns true when this v2 list release sources service unavailable response has a 5xx status code +func (o *V2ListReleaseSourcesServiceUnavailable) IsServerError() bool { + return true +} + +// IsCode returns true when this v2 list release sources service unavailable response a status code equal to that given +func (o *V2ListReleaseSourcesServiceUnavailable) IsCode(code int) bool { + return code == 503 +} + +func (o *V2ListReleaseSourcesServiceUnavailable) Error() string { + return fmt.Sprintf("[GET /v2/release-sources][%d] v2ListReleaseSourcesServiceUnavailable %+v", 503, o.Payload) +} + +func (o *V2ListReleaseSourcesServiceUnavailable) String() string { + return fmt.Sprintf("[GET /v2/release-sources][%d] v2ListReleaseSourcesServiceUnavailable %+v", 503, o.Payload) +} + +func (o *V2ListReleaseSourcesServiceUnavailable) GetPayload() *models.Error { + return o.Payload +} + +func (o *V2ListReleaseSourcesServiceUnavailable) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/vendor/github.com/openshift/assisted-service/client/versions/versions_client.go b/vendor/github.com/openshift/assisted-service/client/versions/versions_client.go index 6365c153f5a..42947354c21 100644 --- a/vendor/github.com/openshift/assisted-service/client/versions/versions_client.go +++ b/vendor/github.com/openshift/assisted-service/client/versions/versions_client.go @@ -20,6 +20,9 @@ type API interface { /* V2ListComponentVersions List of component versions.*/ V2ListComponentVersions(ctx context.Context, params *V2ListComponentVersionsParams) (*V2ListComponentVersionsOK, error) + /* + V2ListReleaseSources Retrieves openshift release sources configuration.*/ + V2ListReleaseSources(ctx context.Context, params *V2ListReleaseSourcesParams) (*V2ListReleaseSourcesOK, error) /* V2ListSupportedOpenshiftVersions Retrieves the list of OpenShift supported versions.*/ V2ListSupportedOpenshiftVersions(ctx context.Context, params *V2ListSupportedOpenshiftVersionsParams) (*V2ListSupportedOpenshiftVersionsOK, error) @@ -68,6 +71,31 @@ func (a *Client) V2ListComponentVersions(ctx context.Context, params *V2ListComp } +/* +V2ListReleaseSources Retrieves openshift release sources configuration. +*/ +func (a *Client) V2ListReleaseSources(ctx context.Context, params *V2ListReleaseSourcesParams) (*V2ListReleaseSourcesOK, error) { + + result, err := a.transport.Submit(&runtime.ClientOperation{ + ID: "v2ListReleaseSources", + Method: "GET", + PathPattern: "/v2/release-sources", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http", "https"}, + Params: params, + Reader: &V2ListReleaseSourcesReader{formats: a.formats}, + AuthInfo: a.authInfo, + Context: ctx, + Client: params.HTTPClient, + }) + if err != nil { + return nil, err + } + return result.(*V2ListReleaseSourcesOK), nil + +} + /* V2ListSupportedOpenshiftVersions Retrieves the list of OpenShift supported versions. */ diff --git a/vendor/github.com/openshift/assisted-service/models/openshift_version.go b/vendor/github.com/openshift/assisted-service/models/openshift_version.go index 2af83b7f0d4..34c46662d0a 100644 --- a/vendor/github.com/openshift/assisted-service/models/openshift_version.go +++ b/vendor/github.com/openshift/assisted-service/models/openshift_version.go @@ -33,7 +33,7 @@ type OpenshiftVersion struct { // Level of support of the version. // Required: true - // Enum: [beta production maintenance] + // Enum: [beta production maintenance end of life] SupportLevel *string `json:"support_level"` } @@ -81,7 +81,7 @@ var openshiftVersionTypeSupportLevelPropEnum []interface{} func init() { var res []string - if err := json.Unmarshal([]byte(`["beta","production","maintenance"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["beta","production","maintenance","end of life"]`), &res); err != nil { panic(err) } for _, v := range res { @@ -99,6 +99,9 @@ const ( // OpenshiftVersionSupportLevelMaintenance captures enum value "maintenance" OpenshiftVersionSupportLevelMaintenance string = "maintenance" + + // OpenshiftVersionSupportLevelEndOfLife captures enum value "end of life" + OpenshiftVersionSupportLevelEndOfLife string = "end of life" ) // prop value enum diff --git a/vendor/github.com/openshift/assisted-service/models/release_image.go b/vendor/github.com/openshift/assisted-service/models/release_image.go index c1f0861d9ef..e87ed885519 100644 --- a/vendor/github.com/openshift/assisted-service/models/release_image.go +++ b/vendor/github.com/openshift/assisted-service/models/release_image.go @@ -26,7 +26,7 @@ type ReleaseImage struct { CPUArchitecture *string `json:"cpu_architecture" gorm:"default:'x86_64'"` // List of CPU architectures provided by the image. - CPUArchitectures []string `json:"cpu_architectures"` + CPUArchitectures []string `json:"cpu_architectures" gorm:"type:text[]"` // Indication that the version is the recommended one. Default bool `json:"default,omitempty"` @@ -36,12 +36,12 @@ type ReleaseImage struct { OpenshiftVersion *string `json:"openshift_version"` // Level of support of the version. - // Enum: [beta production maintenance] + // Enum: [beta production maintenance end of life] SupportLevel string `json:"support_level,omitempty"` // The installation image of the OpenShift cluster. // Required: true - URL *string `json:"url"` + URL *string `json:"url" gorm:"primarykey"` // OCP version from the release metadata. // Required: true @@ -146,7 +146,7 @@ var releaseImageTypeSupportLevelPropEnum []interface{} func init() { var res []string - if err := json.Unmarshal([]byte(`["beta","production","maintenance"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["beta","production","maintenance","end of life"]`), &res); err != nil { panic(err) } for _, v := range res { @@ -164,6 +164,9 @@ const ( // ReleaseImageSupportLevelMaintenance captures enum value "maintenance" ReleaseImageSupportLevelMaintenance string = "maintenance" + + // ReleaseImageSupportLevelEndOfLife captures enum value "end of life" + ReleaseImageSupportLevelEndOfLife string = "end of life" ) // prop value enum diff --git a/vendor/github.com/openshift/assisted-service/models/release_source.go b/vendor/github.com/openshift/assisted-service/models/release_source.go new file mode 100644 index 00000000000..44dbf733bef --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/models/release_source.go @@ -0,0 +1,136 @@ +// 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" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// ReleaseSource release source +// +// swagger:model release-source +type ReleaseSource struct { + + // Version of the OpenShift cluster. + // Required: true + OpenshiftVersion *string `json:"openshift_version"` + + // upgrade channels + // Required: true + UpgradeChannels []*UpgradeChannel `json:"upgrade_channels"` +} + +// Validate validates this release source +func (m *ReleaseSource) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateOpenshiftVersion(formats); err != nil { + res = append(res, err) + } + + if err := m.validateUpgradeChannels(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *ReleaseSource) validateOpenshiftVersion(formats strfmt.Registry) error { + + if err := validate.Required("openshift_version", "body", m.OpenshiftVersion); err != nil { + return err + } + + return nil +} + +func (m *ReleaseSource) validateUpgradeChannels(formats strfmt.Registry) error { + + if err := validate.Required("upgrade_channels", "body", m.UpgradeChannels); err != nil { + return err + } + + for i := 0; i < len(m.UpgradeChannels); i++ { + if swag.IsZero(m.UpgradeChannels[i]) { // not required + continue + } + + if m.UpgradeChannels[i] != nil { + if err := m.UpgradeChannels[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("upgrade_channels" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("upgrade_channels" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// ContextValidate validate this release source based on the context it is used +func (m *ReleaseSource) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateUpgradeChannels(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *ReleaseSource) contextValidateUpgradeChannels(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.UpgradeChannels); i++ { + + if m.UpgradeChannels[i] != nil { + if err := m.UpgradeChannels[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("upgrade_channels" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("upgrade_channels" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// MarshalBinary interface implementation +func (m *ReleaseSource) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *ReleaseSource) UnmarshalBinary(b []byte) error { + var res ReleaseSource + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/vendor/github.com/openshift/assisted-service/models/release_sources.go b/vendor/github.com/openshift/assisted-service/models/release_sources.go new file mode 100644 index 00000000000..c2e51cd789a --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/models/release_sources.go @@ -0,0 +1,73 @@ +// 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" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// ReleaseSources release sources +// +// swagger:model release-sources +type ReleaseSources []*ReleaseSource + +// Validate validates this release sources +func (m ReleaseSources) Validate(formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + if swag.IsZero(m[i]) { // not required + continue + } + + if m[i] != nil { + if err := m[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// ContextValidate validate this release sources based on the context it is used +func (m ReleaseSources) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + + if m[i] != nil { + if err := m[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/vendor/github.com/openshift/assisted-service/models/upgrade_channel.go b/vendor/github.com/openshift/assisted-service/models/upgrade_channel.go new file mode 100644 index 00000000000..299d1d6f2c9 --- /dev/null +++ b/vendor/github.com/openshift/assisted-service/models/upgrade_channel.go @@ -0,0 +1,165 @@ +// 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" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// UpgradeChannel upgrade channel +// +// swagger:model upgrade-channel +type UpgradeChannel struct { + + // channels + // Required: true + Channels []string `json:"channels"` + + // The CPU architecture of the image. + // Required: true + // Enum: [x86_64 aarch64 arm64 ppc64le s390x multi] + CPUArchitecture *string `json:"cpu_architecture" gorm:"default:'x86_64'"` +} + +// Validate validates this upgrade channel +func (m *UpgradeChannel) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateChannels(formats); err != nil { + res = append(res, err) + } + + if err := m.validateCPUArchitecture(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +var upgradeChannelChannelsItemsEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["stable","candidate"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + upgradeChannelChannelsItemsEnum = append(upgradeChannelChannelsItemsEnum, v) + } +} + +func (m *UpgradeChannel) validateChannelsItemsEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, upgradeChannelChannelsItemsEnum, true); err != nil { + return err + } + return nil +} + +func (m *UpgradeChannel) validateChannels(formats strfmt.Registry) error { + + if err := validate.Required("channels", "body", m.Channels); err != nil { + return err + } + + for i := 0; i < len(m.Channels); i++ { + + // value enum + if err := m.validateChannelsItemsEnum("channels"+"."+strconv.Itoa(i), "body", m.Channels[i]); err != nil { + return err + } + + } + + return nil +} + +var upgradeChannelTypeCPUArchitecturePropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["x86_64","aarch64","arm64","ppc64le","s390x","multi"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + upgradeChannelTypeCPUArchitecturePropEnum = append(upgradeChannelTypeCPUArchitecturePropEnum, v) + } +} + +const ( + + // UpgradeChannelCPUArchitectureX8664 captures enum value "x86_64" + UpgradeChannelCPUArchitectureX8664 string = "x86_64" + + // UpgradeChannelCPUArchitectureAarch64 captures enum value "aarch64" + UpgradeChannelCPUArchitectureAarch64 string = "aarch64" + + // UpgradeChannelCPUArchitectureArm64 captures enum value "arm64" + UpgradeChannelCPUArchitectureArm64 string = "arm64" + + // UpgradeChannelCPUArchitecturePpc64le captures enum value "ppc64le" + UpgradeChannelCPUArchitecturePpc64le string = "ppc64le" + + // UpgradeChannelCPUArchitectureS390x captures enum value "s390x" + UpgradeChannelCPUArchitectureS390x string = "s390x" + + // UpgradeChannelCPUArchitectureMulti captures enum value "multi" + UpgradeChannelCPUArchitectureMulti string = "multi" +) + +// prop value enum +func (m *UpgradeChannel) validateCPUArchitectureEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, upgradeChannelTypeCPUArchitecturePropEnum, true); err != nil { + return err + } + return nil +} + +func (m *UpgradeChannel) validateCPUArchitecture(formats strfmt.Registry) error { + + if err := validate.Required("cpu_architecture", "body", m.CPUArchitecture); err != nil { + return err + } + + // value enum + if err := m.validateCPUArchitectureEnum("cpu_architecture", "body", *m.CPUArchitecture); err != nil { + return err + } + + return nil +} + +// ContextValidate validates this upgrade channel based on context it is used +func (m *UpgradeChannel) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *UpgradeChannel) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *UpgradeChannel) UnmarshalBinary(b []byte) error { + var res UpgradeChannel + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +}