From 4532de698535ebc946143524de7c1e01426a503c Mon Sep 17 00:00:00 2001 From: Andrea Decorte Date: Fri, 13 Oct 2023 11:22:39 +0200 Subject: [PATCH] OCM-3911 | feat: use HCP specific endpoints for versions and upgrades We need to handle HCP versions in the backend side and they may also depend on the customer. To obtain this, the current change: 1) introduces the use of an HCP specific version endpoint 2) switch all the available upgrades calls to what is available in the cluster or the node pool API response, as this is the source of truth. This will ensure that the list is the correct one and reduce the number of calls in the client needed to get the version properties. Functionally there should be no change. Improved also the testing to use always SDK objects instead of raw JSON which will help test clarity and mantainence. Related: OCM-3911 --- cmd/create/cluster/cmd.go | 2 +- cmd/create/machinepool/nodepool.go | 4 +- cmd/describe/machinepool/cmd_test.go | 15 +- cmd/describe/upgrade/cmd_test.go | 82 ++------ cmd/list/upgrade/cmd.go | 16 +- cmd/list/upgrade/cmd_test.go | 274 ++++----------------------- cmd/list/version/cmd.go | 6 +- cmd/upgrade/cluster/cmd.go | 12 +- cmd/upgrade/cluster/cmd_test.go | 73 +++---- cmd/upgrade/machinepool/cmd.go | 25 +-- cmd/upgrade/machinepool/cmd_test.go | 220 +++------------------ pkg/helper/versions/helpers.go | 27 ++- pkg/helper/versions/helpers_test.go | 28 +-- pkg/ocm/helpers.go | 2 + pkg/ocm/versions.go | 42 +++- pkg/test/helpers.go | 34 ++++ 16 files changed, 218 insertions(+), 644 deletions(-) diff --git a/cmd/create/cluster/cmd.go b/cmd/create/cluster/cmd.go index 1dd4e09cad..a5fe9e9083 100644 --- a/cmd/create/cluster/cmd.go +++ b/cmd/create/cluster/cmd.go @@ -1066,7 +1066,7 @@ func run(cmd *cobra.Command, _ []string) { // OpenShift version: version := args.version channelGroup := args.channelGroup - versionList, err := versions.GetVersionList(r, channelGroup, isSTS, isHostedCP, true) + versionList, err := versions.GetVersionList(r, channelGroup, isSTS, isHostedCP, true, true) if err != nil { r.Reporter.Errorf("%s", err) os.Exit(1) diff --git a/cmd/create/machinepool/nodepool.go b/cmd/create/machinepool/nodepool.go index cade049c85..447b1d2db2 100644 --- a/cmd/create/machinepool/nodepool.go +++ b/cmd/create/machinepool/nodepool.go @@ -71,7 +71,7 @@ func addNodePool(cmd *cobra.Command, clusterKey string, cluster *cmv1.Cluster, r clusterVersion := cluster.Version().RawID() // This is called in HyperShift, but we don't want to exclude version which are HCP disabled for node pools // so we pass the relative parameter as false - versionList, err := versions.GetVersionList(r, channelGroup, true, false, false) + versionList, err := versions.GetVersionList(r, channelGroup, true, true, false, false) if err != nil { r.Reporter.Errorf("%s", err) os.Exit(1) @@ -85,7 +85,7 @@ func addNodePool(cmd *cobra.Command, clusterKey string, cluster *cmv1.Cluster, r } // Filter the available list of versions for a hosted machine pool - filteredVersionList := versions.GetFilteredVersionListForCreation(versionList, minVersion, clusterVersion) + filteredVersionList := versions.GetFilteredVersionList(versionList, minVersion, clusterVersion) if err != nil { r.Reporter.Errorf("%s", err) os.Exit(1) diff --git a/cmd/describe/machinepool/cmd_test.go b/cmd/describe/machinepool/cmd_test.go index 4a7ca0d1ea..59d871fe1b 100644 --- a/cmd/describe/machinepool/cmd_test.go +++ b/cmd/describe/machinepool/cmd_test.go @@ -1,7 +1,6 @@ package machinepool import ( - "bytes" "fmt" "net/http" "time" @@ -260,12 +259,7 @@ func formatNodePool() string { np, err := cmv1.NewNodePool().ID(nodePoolName).Version(version). AWSNodePool(awsNodePool).AvailabilityZone("us-east-1a").Build() Expect(err).To(BeNil()) - var npJson bytes.Buffer - - err = cmv1.MarshalNodePool(np, &npJson) - Expect(err).To(BeNil()) - - return npJson.String() + return test.FormatResource(np) } // formatMachinePool simulates the output of APIs for a fake machine pool @@ -274,12 +268,7 @@ func formatMachinePool() string { mp, err := cmv1.NewMachinePool().ID(nodePoolName).AWS(awsMachinePoolPool).InstanceType("m5.xlarge"). AvailabilityZones("us-east-1a", "us-east-1b", "us-east-1c").Build() Expect(err).To(BeNil()) - var mpJson bytes.Buffer - - err = cmv1.MarshalMachinePool(mp, &mpJson) - Expect(err).To(BeNil()) - - return mpJson.String() + return test.FormatResource(mp) } func buildNodePoolUpgradePolicy() *cmv1.NodePoolUpgradePolicy { diff --git a/cmd/describe/upgrade/cmd_test.go b/cmd/describe/upgrade/cmd_test.go index 6163055a04..97720c3934 100644 --- a/cmd/describe/upgrade/cmd_test.go +++ b/cmd/describe/upgrade/cmd_test.go @@ -63,75 +63,23 @@ var _ = Describe("Describe upgrade", func() { var clusterID = "cluster1" var nodePoolID = "nodepool85" - const nodePoolResponse = `{ - "kind": "NodePool", - "href": "/api/clusters_mgmt/v1/clusters/243nmgjr5v2q9rn5sf3456euj2lcq5tn/node_pools/workers", - "id": "workers", - "replicas": 2, - "auto_repair": true, - "aws_node_pool": { - "instance_type": "m5.xlarge", - "instance_profile": "rosa-service-managed-integration-243nmgjr5v2q9rn5sf3456euj2lcq5tn-ad-int1-worker", - "tags": { - "api.openshift.com/environment": "integration", - "api.openshift.com/id": "243nmgjr5v2q9rn5sf3456euj2lcq5tn", - "api.openshift.com/legal-entity-id": "1jIHnIbrnLH9kQD57W0BuPm78f1", - "api.openshift.com/name": "ad-int1", - "api.openshift.com/nodepool-hypershift": "ad-int1-workers", - "api.openshift.com/nodepool-ocm": "workers", - "red-hat-clustertype": "rosa", - "red-hat-managed": "true" - } - }, - "availability_zone": "us-west-2a", - "subnet": "subnet-0e3a4046c1c2f1078", - "status": { - "current_replicas": 0, - "message": "WaitingForAvailableMachines: NodeProvisioning" - }, - "version": { - "kind": "VersionLink", - "id": "openshift-v4.12.%s", - "href": "/api/clusters_mgmt/v1/versions/openshift-v4.12.%s" - }, - "tuning_configs": [] - }` + version41224 := cmv1.NewVersion().ID("openshift-v4.12.24").RawID("4.12.24").ReleaseImage("1"). + HREF("/api/clusters_mgmt/v1/versions/openshift-v4.12.24").Enabled(true).ChannelGroup("stable"). + ROSAEnabled(true).HostedControlPlaneEnabled(true).AvailableUpgrades("4.12.25", "4.12.26") + nodePool, err := cmv1.NewNodePool().ID("workers").Replicas(2).AutoRepair(true).Version(version41224).Build() + Expect(err).To(BeNil()) - // nolint:lll - const nodePoolUpgradePolicy = `{ - "kind": "NodePoolUpgradePolicyList", - "page": 1, - "size": 1, - "total": 1, - "items": [ - { - "kind": "NodePoolUpgradePolicy", - "id": "e2800d05-3534-11ee-b9bc-0a580a811709", - "href": "/api/clusters_mgmt/v1/clusters/25f96obptkqc5mh9vdc779jiqb3sihnn/node_pools/workers/upgrade_policies/e2800d05-3534-11ee-b9bc-0a580a811709", - "schedule_type": "manual", - "upgrade_type": "NodePool", - "version": "4.12.25", - "next_run": "2023-08-07T15:22:00Z", - "cluster_id": "25f96obptkqc5mh9vdc779jiqb3sihnn", - "node_pool_id": "workers", - "enable_minor_version_upgrades": true, - "creation_timestamp": "2023-08-07T15:12:54.967835Z", - "last_update_timestamp": "2023-08-07T15:12:54.967835Z", - "state": { - "value": "scheduled", - "description": "Upgrade scheduled." - } - } - ] -}` + upgradePolicies := make([]*cmv1.NodePoolUpgradePolicy, 0) + upgradePolicies = append(upgradePolicies, buildNodePoolUpgradePolicy()) BeforeEach(func() { testRuntime.InitRuntime() }) It("Upgrade policy found, no error", func() { args.nodePool = nodePoolID - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, nodePoolResponse)) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, nodePoolUpgradePolicy)) + testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, test.FormatResource(nodePool))) + testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, + test.FormatNodePoolUpgradePolicyList(upgradePolicies))) err := describeHypershiftUpgrades(testRuntime.RosaRuntime, clusterID, nodePoolID) Expect(err).To(BeNil()) }) @@ -143,3 +91,13 @@ var _ = Describe("Describe upgrade", func() { }) }) }) + +func buildNodePoolUpgradePolicy() *cmv1.NodePoolUpgradePolicy { + t, err := time.Parse(time.RFC3339, "2023-06-02T12:30:00Z") + Expect(err).To(BeNil()) + state := cmv1.NewUpgradePolicyState().Value(cmv1.UpgradePolicyStateValuePending) + policy, err := cmv1.NewNodePoolUpgradePolicy().ScheduleType(cmv1.ScheduleTypeManual). + UpgradeType(cmv1.UpgradeTypeNodePool).Version("4.12.25").State(state).NextRun(t).Build() + Expect(err).To(BeNil()) + return policy +} diff --git a/cmd/list/upgrade/cmd.go b/cmd/list/upgrade/cmd.go index dcd7124c9e..005bfda1e5 100644 --- a/cmd/list/upgrade/cmd.go +++ b/cmd/list/upgrade/cmd.go @@ -24,7 +24,6 @@ import ( "text/tabwriter" cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" - "github.com/openshift/rosa/cmd/upgrade/machinepool" "github.com/openshift/rosa/pkg/interactive/confirm" "github.com/spf13/cobra" @@ -104,16 +103,17 @@ func runWithRuntime(r *rosa.Runtime, _ *cobra.Command) error { } // Get available node pool upgrades - availableUpgrades, err = machinepool.GetAvailableVersion(r, cluster, nodePool) - if err != nil { - return err - } + availableUpgrades = ocm.GetNodePoolAvailableUpgrades(nodePool) } else { // Control plane or cluster updates r.Reporter.Debugf("Loading available upgrades for cluster '%s'", clusterKey) - availableUpgrades, err = r.OCMClient.GetAvailableUpgrades(ocm.GetVersionID(cluster)) - if err != nil { - return fmt.Errorf("Failed to get available upgrades for cluster '%s': %v", clusterKey, err) + if isHypershift { + availableUpgrades = ocm.GetAvailableUpgradesByCluster(cluster) + } else { + availableUpgrades, err = r.OCMClient.GetAvailableUpgrades(ocm.GetVersionID(cluster)) + if err != nil { + return fmt.Errorf("Failed to get available upgrades for cluster '%s': %v", clusterKey, err) + } } if len(availableUpgrades) == 0 { diff --git a/cmd/list/upgrade/cmd_test.go b/cmd/list/upgrade/cmd_test.go index c4e409f742..ed0158f27b 100644 --- a/cmd/list/upgrade/cmd_test.go +++ b/cmd/list/upgrade/cmd_test.go @@ -91,180 +91,22 @@ var _ = Describe("List upgrade", func() { Expect(err).To(BeNil()) var classicCluster = test.FormatClusterList([]*cmv1.Cluster{mockClassicCluster}) - // nolint:lll - const versionListResponse = `{ - "kind": "VersionList", - "page": 1, - "size": 3, - "total": 3, - "items": [ - { - "kind": "Version", - "id": "openshift-v4.12.26", - "href": "/api/clusters_mgmt/v1/versions/openshift-v4.12.26", - "raw_id": "4.12.26", - "enabled": true, - "default": true, - "channel_group": "stable", - "rosa_enabled": true, - "hosted_control_plane_enabled": true, - "end_of_life_timestamp": "2024-05-17T00:00:00Z", - "ami_overrides": [ - { - "product": { - "kind": "ProductLink", - "id": "rosa", - "href": "/api/clusters_mgmt/v1/products/rosa" - }, - "region": { - "kind": "CloudRegionLink", - "id": "us-east-2", - "href": "/api/clusters_mgmt/v1/cloud_providers/aws/regions/us-east-2" - }, - "ami": "ami-0e677f92eb4180cc0" - }, - { - "product": { - "kind": "ProductLink", - "id": "rosa", - "href": "/api/clusters_mgmt/v1/products/rosa" - }, - "region": { - "kind": "CloudRegionLink", - "id": "us-east-1", - "href": "/api/clusters_mgmt/v1/cloud_providers/aws/regions/us-east-1" - }, - "ami": "ami-00354720d36d019f9" - } - ], - "release_image": "quay.io/openshift-release-dev/ocp-release@sha256:8d72f29227418d2ae12ee52e25cce9edef7cd645bdaea02410a89fe8a0ec6a47" - }, - { - "kind": "Version", - "id": "openshift-v4.12.25", - "href": "/api/clusters_mgmt/v1/versions/openshift-v4.12.25", - "raw_id": "4.12.25", - "enabled": true, - "default": false, - "channel_group": "stable", - "available_upgrades": [ - "4.12.26" - ], - "rosa_enabled": true, - "hosted_control_plane_enabled": true, - "end_of_life_timestamp": "2024-05-17T00:00:00Z", - "ami_overrides": [ - { - "product": { - "kind": "ProductLink", - "id": "rosa", - "href": "/api/clusters_mgmt/v1/products/rosa" - }, - "region": { - "kind": "CloudRegionLink", - "id": "us-east-1", - "href": "/api/clusters_mgmt/v1/cloud_providers/aws/regions/us-east-1" - }, - "ami": "ami-00354720d36d019f9" - }, - { - "product": { - "kind": "ProductLink", - "id": "rosa", - "href": "/api/clusters_mgmt/v1/products/rosa" - }, - "region": { - "kind": "CloudRegionLink", - "id": "us-east-2", - "href": "/api/clusters_mgmt/v1/cloud_providers/aws/regions/us-east-2" - }, - "ami": "ami-0e677f92eb4180cc0" - } - ], - "release_image": "quay.io/openshift-release-dev/ocp-release@sha256:5a4fb052cda1d14d1e306ce87e6b0ded84edddaa76f1cf401bcded99cef2ad84" - }, - { - "kind": "Version", - "id": "openshift-v4.12.24", - "href": "/api/clusters_mgmt/v1/versions/openshift-v4.12.24", - "raw_id": "4.12.24", - "enabled": true, - "default": false, - "channel_group": "stable", - "available_upgrades": [ - "4.12.25", - "4.12.26" - ], - "rosa_enabled": true, - "hosted_control_plane_enabled": true, - "end_of_life_timestamp": "2024-05-17T00:00:00Z", - "ami_overrides": [ - { - "product": { - "kind": "ProductLink", - "id": "rosa", - "href": "/api/clusters_mgmt/v1/products/rosa" - }, - "region": { - "kind": "CloudRegionLink", - "id": "us-east-2", - "href": "/api/clusters_mgmt/v1/cloud_providers/aws/regions/us-east-2" - }, - "ami": "ami-0e677f92eb4180cc0" - }, - { - "product": { - "kind": "ProductLink", - "id": "rosa", - "href": "/api/clusters_mgmt/v1/products/rosa" - }, - "region": { - "kind": "CloudRegionLink", - "id": "us-east-1", - "href": "/api/clusters_mgmt/v1/cloud_providers/aws/regions/us-east-1" - }, - "ami": "ami-00354720d36d019f9" - } - ], - "release_image": "quay.io/openshift-release-dev/ocp-release@sha256:b0b11eedf91175459b5d7aefcf3936d0cabf00f01ced756677483f5f26227328" - } - ] - }` + versionNoUpgrades := cmv1.NewVersion().ID("openshift-v4.12.24").RawID("4.12.24").ReleaseImage("1"). + HREF("/api/clusters_mgmt/v1/versions/openshift-v4.12.24").Enabled(true).ChannelGroup("stable"). + ROSAEnabled(true).HostedControlPlaneEnabled(true) + version41224 := cmv1.NewVersion().ID("openshift-v4.12.24").RawID("4.12.24").ReleaseImage("1"). + HREF("/api/clusters_mgmt/v1/versions/openshift-v4.12.24").Enabled(true).ChannelGroup("stable"). + ROSAEnabled(true).HostedControlPlaneEnabled(true).AvailableUpgrades("4.12.25", "4.12.26") + nodePool, err := cmv1.NewNodePool().ID("workers").Replicas(2).AutoRepair(true).Version(version41224).Build() + Expect(err).To(BeNil()) + nodePoolNoUpgrades, err := cmv1.NewNodePool().ID("workers").Replicas(2).AutoRepair(true). + Version(versionNoUpgrades).Build() + Expect(err).To(BeNil()) + emptyUpgradePolicies := make([]*cmv1.NodePoolUpgradePolicy, 0) + + upgradePolicies := make([]*cmv1.NodePoolUpgradePolicy, 0) + upgradePolicies = append(upgradePolicies, buildNodePoolUpgradePolicy()) - // nolint:lll - const nodePoolResponse = `{ - "kind": "NodePool", - "href": "/api/clusters_mgmt/v1/clusters/243nmgjr5v2q9rn5sf3456euj2lcq5tn/node_pools/workers", - "id": "workers", - "replicas": 2, - "auto_repair": true, - "aws_node_pool": { - "instance_type": "m5.xlarge", - "instance_profile": "rosa-service-managed-integration-243nmgjr5v2q9rn5sf3456euj2lcq5tn-ad-int1-worker", - "tags": { - "api.openshift.com/environment": "integration", - "api.openshift.com/id": "243nmgjr5v2q9rn5sf3456euj2lcq5tn", - "api.openshift.com/legal-entity-id": "1jIHnIbrnLH9kQD57W0BuPm78f1", - "api.openshift.com/name": "ad-int1", - "api.openshift.com/nodepool-hypershift": "ad-int1-workers", - "api.openshift.com/nodepool-ocm": "workers", - "red-hat-clustertype": "rosa", - "red-hat-managed": "true" - } - }, - "availability_zone": "us-west-2a", - "subnet": "subnet-0e3a4046c1c2f1078", - "status": { - "current_replicas": 0, - "message": "WaitingForAvailableMachines: NodeProvisioning" - }, - "version": { - "kind": "VersionLink", - "id": "openshift-v4.12.%s", - "href": "/api/clusters_mgmt/v1/versions/openshift-v4.12.%s" - }, - "tuning_configs": [] - }` BeforeEach(func() { testRuntime.InitRuntime() }) @@ -288,23 +130,10 @@ var _ = Describe("List upgrade", func() { args.nodePool = nodePoolName testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, hypershiftClusterReady)) // A node pool - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, - fmt.Sprintf(nodePoolResponse, "26", "26"))) + testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, test.FormatResource(nodePoolNoUpgrades))) // No existing policy upgrade - testRuntime.ApiServer.AppendHandlers( - RespondWithJSON( - http.StatusOK, - `{ - "kind": "NodePoolUpgradePolicyList", - "page": 1, - "size": 0, - "total": 0, - "items": [] - }`, - ), - ) - // available versions - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, versionListResponse)) + testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, + test.FormatNodePoolUpgradePolicyList(emptyUpgradePolicies))) stdout, _, err := test.RunWithOutputCapture(runWithRuntime, testRuntime.RosaRuntime, Cmd) Expect(stdout).To(Equal(noUpgradeOutput)) Expect(err).To(BeNil()) @@ -315,23 +144,10 @@ var _ = Describe("List upgrade", func() { args.nodePool = nodePoolName testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, hypershiftClusterReady)) // A node pool - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, - fmt.Sprintf(nodePoolResponse, "24", "24"))) + testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, test.FormatResource(nodePool))) // No existing policy upgrade - testRuntime.ApiServer.AppendHandlers( - RespondWithJSON( - http.StatusOK, - `{ - "kind": "NodePoolUpgradePolicyList", - "page": 1, - "size": 0, - "total": 0, - "items": [] - }`, - ), - ) - // available versions - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, versionListResponse)) + testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, + test.FormatNodePoolUpgradePolicyList(emptyUpgradePolicies))) stdout, _, err := test.RunWithOutputCapture(runWithRuntime, testRuntime.RosaRuntime, Cmd) Expect(stdout).To(Equal(upgradeAvailableOutput)) Expect(err).To(BeNil()) @@ -342,45 +158,23 @@ var _ = Describe("List upgrade", func() { args.nodePool = nodePoolName testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, hypershiftClusterReady)) // A node pool - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, - fmt.Sprintf(nodePoolResponse, "24", "24"))) + testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, test.FormatResource(nodePool))) // An existing policy upgrade - // nolint:lll - testRuntime.ApiServer.AppendHandlers( - RespondWithJSON( - http.StatusOK, - `{ - "kind": "NodePoolUpgradePolicyList", - "page": 1, - "size": 1, - "total": 1, - "items": [ - { - "kind": "NodePoolUpgradePolicy", - "id": "a33c8cae-013f-11ee-a3b2-acde48001122", - "href": "/api/clusters_mgmt/v1/clusters/243nmgjr5v2q9rn5sf3456euj2lcq5tn/node_pools/upgrade_policies/a33c8cae-013f-11ee-a3b2-acde48001122", - "schedule_type": "manual", - "upgrade_type": "NodePool", - "version": "4.12.25", - "next_run": "2023-06-02T12:30:00Z", - "cluster_id": "243nmgjr5v2q9rn5sf3456euj2lcq5tn", - "enable_minor_version_upgrades": false, - "creation_timestamp": "2023-06-02T14:18:52.828589+02:00", - "last_update_timestamp": "2023-06-02T14:18:52.828589+02:00", - "state": { - "value": "pending", - "description": "Upgrade policy defined, pending scheduling." - } - } - ] - }`, - ), - ) - // available versions - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, versionListResponse)) + testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, + test.FormatNodePoolUpgradePolicyList(upgradePolicies))) stdout, _, err := test.RunWithOutputCapture(runWithRuntime, testRuntime.RosaRuntime, Cmd) Expect(stdout).To(Equal(ongoingUpgradeOutput)) Expect(err).To(BeNil()) }) }) }) + +func buildNodePoolUpgradePolicy() *cmv1.NodePoolUpgradePolicy { + t, err := time.Parse(time.RFC3339, "2023-06-02T12:30:00Z") + Expect(err).To(BeNil()) + state := cmv1.NewUpgradePolicyState().Value(cmv1.UpgradePolicyStateValuePending) + policy, err := cmv1.NewNodePoolUpgradePolicy().ScheduleType(cmv1.ScheduleTypeManual). + UpgradeType(cmv1.UpgradeTypeNodePool).Version("4.12.25").State(state).NextRun(t).Build() + Expect(err).To(BeNil()) + return policy +} diff --git a/cmd/list/version/cmd.go b/cmd/list/version/cmd.go index 6e07169a6c..f808747b38 100644 --- a/cmd/list/version/cmd.go +++ b/cmd/list/version/cmd.go @@ -66,10 +66,14 @@ func run(cmd *cobra.Command, _ []string) { r := rosa.NewRuntime().WithOCM() defer r.Cleanup() isHostedCp := args.hostedCp + var product string + if isHostedCp { + product = ocm.HcpProduct + } // Try to find the cluster: r.Reporter.Debugf("Fetching versions") - versions, err := r.OCMClient.GetVersions(args.channelGroup, false) + versions, err := r.OCMClient.GetVersionsWithProduct(product, args.channelGroup, false) if err != nil { r.Reporter.Errorf("Failed to fetch versions: %v", err) os.Exit(1) diff --git a/cmd/upgrade/cluster/cmd.go b/cmd/upgrade/cluster/cmd.go index 6d54fea806..d5b72983f2 100644 --- a/cmd/upgrade/cluster/cmd.go +++ b/cmd/upgrade/cluster/cmd.go @@ -411,9 +411,15 @@ func createUpgradePolicyClassic(r *rosa.Runtime, cmd *cobra.Command, clusterKey func buildVersion(r *rosa.Runtime, cmd *cobra.Command, cluster *cmv1.Cluster, version string, isAutomaticUpgrade bool) ([]string, string, error) { - availableUpgrades, err := r.OCMClient.GetAvailableUpgrades(ocm.GetVersionID(cluster)) - if err != nil { - return availableUpgrades, version, fmt.Errorf("Failed to find available upgrades: %v", err) + var availableUpgrades []string + var err error + if ocm.IsHyperShiftCluster(cluster) { + availableUpgrades = ocm.GetAvailableUpgradesByCluster(cluster) + } else { + availableUpgrades, err = r.OCMClient.GetAvailableUpgrades(ocm.GetVersionID(cluster)) + if err != nil { + return availableUpgrades, version, fmt.Errorf("Failed to find available upgrades: %v", err) + } } if len(availableUpgrades) == 0 { return availableUpgrades, version, nil diff --git a/cmd/upgrade/cluster/cmd_test.go b/cmd/upgrade/cluster/cmd_test.go index ef7f9c638c..d9077f7cae 100644 --- a/cmd/upgrade/cluster/cmd_test.go +++ b/cmd/upgrade/cluster/cmd_test.go @@ -33,14 +33,6 @@ var _ = Describe("Upgrade", Ordered, func() { version4130 := cmv1.NewVersion().ID("openshift-v4.13.0").RawID("4.13.0").ReleaseImage("1"). HREF("/api/clusters_mgmt/v1/versions/openshift-v4.13.0").Enabled(true).ChannelGroup("stable"). ROSAEnabled(true).HostedControlPlaneEnabled(true) - version4130WithUpdate, err := version4130.AvailableUpgrades("4.13.1").Build() - Expect(err).To(BeNil()) - version4130WithoutUpdate, err := version4130.AvailableUpgrades().Build() - Expect(err).To(BeNil()) - version4131, err := cmv1.NewVersion().ID("openshift-v4.13.1").RawID("4.13.1").ReleaseImage("1"). - HREF("/api/clusters_mgmt/v1/versions/openshift-v4.13.1").Enabled(true).ChannelGroup("stable"). - ROSAEnabled(true).HostedControlPlaneEnabled(true).AvailableUpgrades("4.13.2").Build() - Expect(err).To(BeNil()) mockClusterError, err := test.MockOCMCluster(func(c *cmv1.ClusterBuilder) { c.AWS(cmv1.NewAWS().SubnetIDs("subnet-0b761d44d3d9a4663", "subnet-0f87f640e56934cbc")) @@ -56,10 +48,24 @@ var _ = Describe("Upgrade", Ordered, func() { c.Region(cmv1.NewCloudRegion().ID("us-east-1")) c.State(cmv1.ClusterStateReady) c.Hypershift(cmv1.NewHypershift().Enabled(true)) + c.Version(version4130) }) Expect(err).To(BeNil()) + // hypershiftClusterReady has no available upgrades var hypershiftClusterReady = test.FormatClusterList([]*cmv1.Cluster{mockClusterReady}) + version4130WithUpgrades := version4130.AvailableUpgrades("4.13.1") + mockClusterReadyWithUpgrades, err := test.MockOCMCluster(func(c *cmv1.ClusterBuilder) { + c.AWS(cmv1.NewAWS().SubnetIDs("subnet-0b761d44d3d9a4663", "subnet-0f87f640e56934cbc")) + c.Region(cmv1.NewCloudRegion().ID("us-east-1")) + c.State(cmv1.ClusterStateReady) + c.Hypershift(cmv1.NewHypershift().Enabled(true)) + c.Version(version4130WithUpgrades) + }) + Expect(err).To(BeNil()) + // hypershiftClusterReadyWithUpdates has one available upgrade + var hypershiftClusterReadyWithUpdates = test.FormatClusterList([]*cmv1.Cluster{mockClusterReadyWithUpgrades}) + mockClassicCluster, err := test.MockOCMCluster(func(c *cmv1.ClusterBuilder) { c.AWS(cmv1.NewAWS().SubnetIDs("subnet-0b761d44d3d9a4663", "subnet-0f87f640e56934cbc")) c.Region(cmv1.NewCloudRegion().ID("us-east-1")) @@ -144,18 +150,6 @@ var _ = Describe("Upgrade", Ordered, func() { Expect(err).ToNot(BeNil()) Expect(err.Error()).To(ContainSubstring("Cluster 'cluster1' is not yet ready")) }) - It("Cluster is ready but no upgrade type specified", func() { - args.controlPlane = true - args.schedule = cronSchedule - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, hypershiftClusterReady)) - // No existing policy upgrade - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, - formatControlPlaneUpgradePolicyList([]*cmv1.ControlPlaneUpgradePolicy{}))) - err := runWithRuntime(testRuntime.RosaRuntime, Cmd) - Expect(err).ToNot(BeNil()) - // Missing the upgrade type - Expect(err.Error()).To(ContainSubstring("Failed to find available upgrades")) - }) It("Cluster is ready but existing upgrade scheduled", func() { args.controlPlane = true args.schedule = cronSchedule @@ -213,12 +207,12 @@ var _ = Describe("Upgrade", Ordered, func() { // Not a valid date format args.scheduleDate = dateSchedule args.scheduleTime = timeSchedule + // The version we want to update to + args.version = "4.13.4" testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, hypershiftClusterReady)) // No existing policy upgrade testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, formatControlPlaneUpgradePolicyList([]*cmv1.ControlPlaneUpgradePolicy{}))) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, formatVersion(version4130WithoutUpdate))) - stdout, stderr, err := test.RunWithOutputCapture(runWithRuntime, testRuntime.RosaRuntime, Cmd) Expect(err).To(BeNil()) Expect(stdout).To(BeEmpty()) @@ -229,11 +223,11 @@ var _ = Describe("Upgrade", Ordered, func() { args.schedule = "20 5 * * *" args.scheduleDate = "" args.scheduleTime = "" + args.version = "" testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, hypershiftClusterReady)) // No existing policy upgrade testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, formatControlPlaneUpgradePolicyList([]*cmv1.ControlPlaneUpgradePolicy{}))) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, formatVersion(version4130WithoutUpdate))) // POST - // /api/clusters_mgmt/v1/clusters/24vf9iitg3p6tlml88iml6j6mu095mh8/control_plane/upgrade_policies?dryRun=true testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusNoContent, "")) @@ -257,12 +251,10 @@ var _ = Describe("Upgrade", Ordered, func() { args.scheduleTime = timeSchedule // The version we want to update to args.version = "4.13.4" - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, hypershiftClusterReady)) + testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, hypershiftClusterReadyWithUpdates)) // No existing policy upgrade testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, formatControlPlaneUpgradePolicyList([]*cmv1.ControlPlaneUpgradePolicy{}))) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, formatVersion(version4130WithUpdate))) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, formatVersion(version4131))) err := runWithRuntime(testRuntime.RosaRuntime, Cmd) Expect(err).ToNot(BeNil()) @@ -277,25 +269,20 @@ var _ = Describe("Upgrade", Ordered, func() { args.scheduleTime = timeSchedule // The version we want to update to args.version = "4.13.1" - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, hypershiftClusterReady)) + testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, hypershiftClusterReadyWithUpdates)) // No existing policy upgrade testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, formatControlPlaneUpgradePolicyList([]*cmv1.ControlPlaneUpgradePolicy{}))) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, formatVersion(version4130WithUpdate))) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, formatVersion(version4131))) testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusNoContent, "")) - testRuntime.ApiServer.AppendHandlers( - RespondWithJSON( - http.StatusCreated, `{ - "kind": "ControlPlaneUpgradePolicy", - "enable_minor_version_upgrades": false, - "schedule": "35 12 * * *", - "schedule_type": "automatic", - "upgrade_type": "ControlPlane" - }`)) + + cpUpgradePolicy, err := cmv1.NewControlPlaneUpgradePolicy().UpgradeType(cmv1.UpgradeTypeControlPlane). + ScheduleType(cmv1.ScheduleTypeAutomatic).Schedule("30 12 * * *"). + EnableMinorVersionUpgrades(false).Build() + Expect(err).To(BeNil()) + testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusCreated, test.FormatResource(cpUpgradePolicy))) testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusNotFound, clusterNotFound)) testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, emptyClusterList)) - err := runWithRuntime(testRuntime.RosaRuntime, Cmd) + err = runWithRuntime(testRuntime.RosaRuntime, Cmd) Expect(err).ToNot(BeNil()) Expect(err.Error()).To(ContainSubstring("There is no cluster with identifier or name")) }) @@ -315,11 +302,3 @@ func formatControlPlaneUpgradePolicyList(upgradePolicies []*cmv1.ControlPlaneUpg "items": %s }`, len(upgradePolicies), len(upgradePolicies), policiesJson.String()) } - -func formatVersion(version *cmv1.Version) string { - var versionJson bytes.Buffer - - cmv1.MarshalVersion(version, &versionJson) - - return versionJson.String() -} diff --git a/cmd/upgrade/machinepool/cmd.go b/cmd/upgrade/machinepool/cmd.go index 29381b79c9..94b85fbc03 100644 --- a/cmd/upgrade/machinepool/cmd.go +++ b/cmd/upgrade/machinepool/cmd.go @@ -21,7 +21,6 @@ import ( "os" cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" - "github.com/openshift/rosa/pkg/helper/versions" "github.com/openshift/rosa/pkg/input" "github.com/openshift/rosa/pkg/interactive" "github.com/openshift/rosa/pkg/interactive/confirm" @@ -334,11 +333,9 @@ func checkNodePoolExistingScheduledUpgrade(r *rosa.Runtime, cluster *cmv1.Cluste func ComputeNodePoolVersion(r *rosa.Runtime, cmd *cobra.Command, cluster *cmv1.Cluster, nodePool *cmv1.NodePool, version string) (string, error) { + var err error channelGroup := cluster.Version().ChannelGroup() - filteredVersionList, err := GetAvailableVersion(r, cluster, nodePool) - if err != nil { - return "", err - } + filteredVersionList := ocm.GetNodePoolAvailableUpgrades(nodePool) // No updates available if len(filteredVersionList) == 0 { return "", nil @@ -368,21 +365,3 @@ func ComputeNodePoolVersion(r *rosa.Runtime, cmd *cobra.Command, cluster *cmv1.C } return version, nil } - -func GetAvailableVersion(r *rosa.Runtime, cluster *cmv1.Cluster, nodePool *cmv1.NodePool) ([]string, error) { - clusterVersion := cluster.Version().RawID() - nodePoolVersion := ocm.GetRawVersionId(nodePool.Version().ID()) - // This is called in HyperShift, but we don't want to exclude version which are HCP disabled for node pools - // so we pass the relative parameter as false - versionList, err := versions.GetVersionList(r, cluster.Version().ChannelGroup(), true, false, false) - if err != nil { - return nil, err - } - - // Filter the available list of versions for a hosted machine pool - filteredVersionList := versions.GetFilteredVersionListForUpdate(versionList, nodePoolVersion, clusterVersion) - if err != nil { - return nil, err - } - return filteredVersionList, nil -} diff --git a/cmd/upgrade/machinepool/cmd_test.go b/cmd/upgrade/machinepool/cmd_test.go index d1f6e32feb..42d59fc6b3 100644 --- a/cmd/upgrade/machinepool/cmd_test.go +++ b/cmd/upgrade/machinepool/cmd_test.go @@ -17,7 +17,7 @@ const ( validScheduleDate = "2023-12-25" cronSchedule = "* * * * *" invalidVersionError = `Expected a valid machine pool version: A valid version number must be specified -Valid versions: 4.12.26 4.12.25 4.12.24` +Valid versions: 4.12.26 4.12.25` ) var _ = Describe("Upgrade machine pool", func() { @@ -42,180 +42,11 @@ var _ = Describe("Upgrade machine pool", func() { Expect(err).To(BeNil()) hypershiftClusterReady := test.FormatClusterList([]*cmv1.Cluster{mockClusterReady}) - // nolint:lll - const versionListResponse = `{ - "kind": "VersionList", - "page": 1, - "size": 3, - "total": 3, - "items": [ - { - "kind": "Version", - "id": "openshift-v4.12.26", - "href": "/api/clusters_mgmt/v1/versions/openshift-v4.12.26", - "raw_id": "4.12.26", - "enabled": true, - "default": true, - "channel_group": "stable", - "rosa_enabled": true, - "hosted_control_plane_enabled": true, - "end_of_life_timestamp": "2024-05-17T00:00:00Z", - "ami_overrides": [ - { - "product": { - "kind": "ProductLink", - "id": "rosa", - "href": "/api/clusters_mgmt/v1/products/rosa" - }, - "region": { - "kind": "CloudRegionLink", - "id": "us-east-2", - "href": "/api/clusters_mgmt/v1/cloud_providers/aws/regions/us-east-2" - }, - "ami": "ami-0e677f92eb4180cc0" - }, - { - "product": { - "kind": "ProductLink", - "id": "rosa", - "href": "/api/clusters_mgmt/v1/products/rosa" - }, - "region": { - "kind": "CloudRegionLink", - "id": "us-east-1", - "href": "/api/clusters_mgmt/v1/cloud_providers/aws/regions/us-east-1" - }, - "ami": "ami-00354720d36d019f9" - } - ], - "release_image": "quay.io/openshift-release-dev/ocp-release@sha256:8d72f29227418d2ae12ee52e25cce9edef7cd645bdaea02410a89fe8a0ec6a47" - }, - { - "kind": "Version", - "id": "openshift-v4.12.25", - "href": "/api/clusters_mgmt/v1/versions/openshift-v4.12.25", - "raw_id": "4.12.25", - "enabled": true, - "default": false, - "channel_group": "stable", - "available_upgrades": [ - "4.12.26" - ], - "rosa_enabled": true, - "hosted_control_plane_enabled": true, - "end_of_life_timestamp": "2024-05-17T00:00:00Z", - "ami_overrides": [ - { - "product": { - "kind": "ProductLink", - "id": "rosa", - "href": "/api/clusters_mgmt/v1/products/rosa" - }, - "region": { - "kind": "CloudRegionLink", - "id": "us-east-1", - "href": "/api/clusters_mgmt/v1/cloud_providers/aws/regions/us-east-1" - }, - "ami": "ami-00354720d36d019f9" - }, - { - "product": { - "kind": "ProductLink", - "id": "rosa", - "href": "/api/clusters_mgmt/v1/products/rosa" - }, - "region": { - "kind": "CloudRegionLink", - "id": "us-east-2", - "href": "/api/clusters_mgmt/v1/cloud_providers/aws/regions/us-east-2" - }, - "ami": "ami-0e677f92eb4180cc0" - } - ], - "release_image": "quay.io/openshift-release-dev/ocp-release@sha256:5a4fb052cda1d14d1e306ce87e6b0ded84edddaa76f1cf401bcded99cef2ad84" - }, - { - "kind": "Version", - "id": "openshift-v4.12.24", - "href": "/api/clusters_mgmt/v1/versions/openshift-v4.12.24", - "raw_id": "4.12.24", - "enabled": true, - "default": false, - "channel_group": "stable", - "available_upgrades": [ - "4.12.25", - "4.12.26" - ], - "rosa_enabled": true, - "hosted_control_plane_enabled": true, - "end_of_life_timestamp": "2024-05-17T00:00:00Z", - "ami_overrides": [ - { - "product": { - "kind": "ProductLink", - "id": "rosa", - "href": "/api/clusters_mgmt/v1/products/rosa" - }, - "region": { - "kind": "CloudRegionLink", - "id": "us-east-2", - "href": "/api/clusters_mgmt/v1/cloud_providers/aws/regions/us-east-2" - }, - "ami": "ami-0e677f92eb4180cc0" - }, - { - "product": { - "kind": "ProductLink", - "id": "rosa", - "href": "/api/clusters_mgmt/v1/products/rosa" - }, - "region": { - "kind": "CloudRegionLink", - "id": "us-east-1", - "href": "/api/clusters_mgmt/v1/cloud_providers/aws/regions/us-east-1" - }, - "ami": "ami-00354720d36d019f9" - } - ], - "release_image": "quay.io/openshift-release-dev/ocp-release@sha256:b0b11eedf91175459b5d7aefcf3936d0cabf00f01ced756677483f5f26227328" - } - ] - }` - - // nolint:lll - const nodePoolResponse = `{ - "kind": "NodePool", - "href": "/api/clusters_mgmt/v1/clusters/243nmgjr5v2q9rn5sf3456euj2lcq5tn/node_pools/workers", - "id": "workers", - "replicas": 2, - "auto_repair": true, - "aws_node_pool": { - "instance_type": "m5.xlarge", - "instance_profile": "rosa-service-managed-integration-243nmgjr5v2q9rn5sf3456euj2lcq5tn-ad-int1-worker", - "tags": { - "api.openshift.com/environment": "integration", - "api.openshift.com/id": "243nmgjr5v2q9rn5sf3456euj2lcq5tn", - "api.openshift.com/legal-entity-id": "1jIHnIbrnLH9kQD57W0BuPm78f1", - "api.openshift.com/name": "ad-int1", - "api.openshift.com/nodepool-hypershift": "ad-int1-workers", - "api.openshift.com/nodepool-ocm": "workers", - "red-hat-clustertype": "rosa", - "red-hat-managed": "true" - } - }, - "availability_zone": "us-west-2a", - "subnet": "subnet-0e3a4046c1c2f1078", - "status": { - "current_replicas": 0, - "message": "WaitingForAvailableMachines: NodeProvisioning" - }, - "version": { - "kind": "VersionLink", - "id": "openshift-v4.12.%s", - "href": "/api/clusters_mgmt/v1/versions/openshift-v4.12.%s" - }, - "tuning_configs": [] - }` + version41224 := cmv1.NewVersion().ID("openshift-v4.12.24").RawID("4.12.24").ReleaseImage("1"). + HREF("/api/clusters_mgmt/v1/versions/openshift-v4.12.24").Enabled(true).ChannelGroup("stable"). + ROSAEnabled(true).HostedControlPlaneEnabled(true).AvailableUpgrades("4.12.25", "4.12.26") + nodePool, err := cmv1.NewNodePool().ID("workers").Replicas(2).AutoRepair(true).Version(version41224).Build() + Expect(err).To(BeNil()) upgradePolicies := make([]*cmv1.NodePoolUpgradePolicy, 0) upgradePolicies = append(upgradePolicies, buildNodePoolUpgradePolicy()) @@ -277,7 +108,7 @@ var _ = Describe("Upgrade machine pool", func() { }) It("Cluster is ready and there is a scheduled upgraded", func() { testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, hypershiftClusterReady)) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, nodePoolResponse)) + testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, test.FormatResource(nodePool))) testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, nodePoolUpgradePolicy)) _, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, testRuntime.RosaRuntime, Cmd, &[]string{nodePoolName}) @@ -285,9 +116,9 @@ var _ = Describe("Upgrade machine pool", func() { Expect(stderr).To(ContainSubstring( "WARN: There is already a scheduled upgrade to version 4.12.25 on 2023-08-07 15:22 UTC")) }) - It("Cluster is ready and there is a scheduled upgraded", func() { + It("Succeeds if cluster is ready and there is a scheduled upgraded", func() { testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, hypershiftClusterReady)) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, nodePoolResponse)) + testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, test.FormatResource(nodePool))) testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, nodePoolUpgradePolicy)) _, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, testRuntime.RosaRuntime, Cmd, &[]string{nodePoolName}) @@ -295,14 +126,13 @@ var _ = Describe("Upgrade machine pool", func() { Expect(stderr).To(ContainSubstring( "WARN: There is already a scheduled upgrade to version 4.12.25 on 2023-08-07 15:22 UTC")) }) - It("Cluster is ready and there is no scheduled upgraded but schedule date is invalid -> fail", func() { + It("Fails if cluster is ready and there is no scheduled upgraded but schedule date is invalid", func() { args.scheduleTime = scheduleTime args.scheduleDate = invalidScheduleDate Cmd.Flags().Set("interactive", "false") testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, hypershiftClusterReady)) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, nodePoolResponse)) + testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, test.FormatResource(nodePool))) testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, noNodePoolUpgradePolicy)) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, versionListResponse)) stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, testRuntime.RosaRuntime, Cmd, &[]string{nodePoolName}) Expect(err).ToNot(BeNil()) @@ -311,17 +141,16 @@ var _ = Describe("Upgrade machine pool", func() { Expect(stdout).To(BeEmpty()) Expect(stderr).To(BeEmpty()) }) - It("Cluster is ready and there is no scheduled upgraded and an invalid version is specified -> fail", + It("Fails if cluster is ready and there is no scheduled upgraded but a version not "+ + "in available upgrades is specified", func() { args.scheduleTime = scheduleTime args.scheduleDate = validScheduleDate Cmd.Flags().Set("version", "4.13.26") Cmd.Flags().Set("interactive", "false") testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, hypershiftClusterReady)) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, nodePoolResponse)) + testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, test.FormatResource(nodePool))) testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, noNodePoolUpgradePolicy)) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, versionListResponse)) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, "")) stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, testRuntime.RosaRuntime, Cmd, &[]string{nodePoolName}) Expect(err).ToNot(BeNil()) @@ -329,15 +158,14 @@ var _ = Describe("Upgrade machine pool", func() { Expect(stderr).To(BeEmpty()) Expect(stdout).To(BeEmpty()) }) - It("Cluster is ready and there is no scheduled upgraded and a version is specified -> success", func() { + It("Succeeds if cluster is ready and there is no scheduled upgraded and a version is specified", func() { args.scheduleTime = scheduleTime args.scheduleDate = validScheduleDate Cmd.Flags().Set("version", "4.12.26") Cmd.Flags().Set("interactive", "false") testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, hypershiftClusterReady)) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, nodePoolResponse)) + testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, test.FormatResource(nodePool))) testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, noNodePoolUpgradePolicy)) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, versionListResponse)) testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, "")) stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, testRuntime.RosaRuntime, Cmd, &[]string{nodePoolName}) @@ -346,14 +174,13 @@ var _ = Describe("Upgrade machine pool", func() { Expect(stdout).To(ContainSubstring( "Upgrade successfully scheduled for the machine pool 'nodepool85' on cluster 'cluster1")) }) - It("Cluster is ready and there is no scheduled upgraded -> success", func() { + It("Succeeds if cluster is ready and there is no scheduled upgraded", func() { args.scheduleTime = scheduleTime args.scheduleDate = validScheduleDate Cmd.Flags().Set("interactive", "false") testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, hypershiftClusterReady)) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, nodePoolResponse)) + testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, test.FormatResource(nodePool))) testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, noNodePoolUpgradePolicy)) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, versionListResponse)) testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, "")) stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, testRuntime.RosaRuntime, Cmd, &[]string{nodePoolName}) @@ -367,9 +194,8 @@ var _ = Describe("Upgrade machine pool", func() { args.scheduleDate = validScheduleDate Cmd.Flags().Set("interactive", "false") testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, hypershiftClusterReady)) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, nodePoolResponse)) + testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, test.FormatResource(nodePool))) testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, noNodePoolUpgradePolicy)) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, versionListResponse)) testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusBadRequest, "an error")) stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, testRuntime.RosaRuntime, Cmd, &[]string{nodePoolName}) @@ -386,9 +212,8 @@ var _ = Describe("Upgrade machine pool", func() { args.schedule = "* a" Cmd.Flags().Set("interactive", "false") testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, hypershiftClusterReady)) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, nodePoolResponse)) + testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, test.FormatResource(nodePool))) testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, noNodePoolUpgradePolicy)) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, versionListResponse)) testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, "")) _, _, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, testRuntime.RosaRuntime, Cmd, &[]string{nodePoolName}) @@ -403,10 +228,9 @@ var _ = Describe("Upgrade machine pool", func() { args.schedule = cronSchedule Cmd.Flags().Set("interactive", "false") testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, hypershiftClusterReady)) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, nodePoolResponse)) + testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, test.FormatResource(nodePool))) testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, noNodePoolUpgradePolicy)) - testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, versionListResponse)) - //testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, "")) + testRuntime.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, "")) stdout, stderr, err := test.RunWithOutputCaptureAndArgv(runWithRuntime, testRuntime.RosaRuntime, Cmd, &[]string{nodePoolName}) Expect(err).To(BeNil()) diff --git a/pkg/helper/versions/helpers.go b/pkg/helper/versions/helpers.go index d079d1da4a..f3a69b1d8c 100644 --- a/pkg/helper/versions/helpers.go +++ b/pkg/helper/versions/helpers.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/go-version" ver "github.com/hashicorp/go-version" + v1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" "github.com/openshift/rosa/pkg/ocm" "github.com/openshift/rosa/pkg/rosa" ) @@ -14,9 +15,15 @@ const ( MinorVersionsSupported = 2 ) -func GetVersionList(r *rosa.Runtime, channelGroup string, isSTS bool, isHostedCP bool, +func GetVersionList(r *rosa.Runtime, channelGroup string, isSTS bool, isHostedCP bool, filterHostedCP bool, defaultFirst bool) (versionList []string, err error) { - vs, err := r.OCMClient.GetVersions(channelGroup, defaultFirst) + var vs []*v1.Version + var product string + if isHostedCP { + product = ocm.HcpProduct + } + // Product can be empty. In this case, no filter will be applied + vs, err = r.OCMClient.GetVersionsWithProduct(product, channelGroup, defaultFirst) if err != nil { err = fmt.Errorf("Failed to retrieve versions: %s", err) return @@ -26,7 +33,7 @@ func GetVersionList(r *rosa.Runtime, channelGroup string, isSTS bool, isHostedCP if isSTS && !ocm.HasSTSSupport(v.RawID(), v.ChannelGroup()) { continue } - if isHostedCP { + if filterHostedCP { valid, err := ocm.HasHostedCPSupport(v) if err != nil { return versionList, fmt.Errorf("failed to check HostedCP support: %v", err) @@ -46,15 +53,7 @@ func GetVersionList(r *rosa.Runtime, channelGroup string, isSTS bool, isHostedCP return } -func GetFilteredVersionListForCreation(versionList []string, minVersion string, maxVersion string) []string { - return getFilteredVersionList(versionList, minVersion, maxVersion, false) -} - -func GetFilteredVersionListForUpdate(versionList []string, minVersion string, maxVersion string) []string { - return getFilteredVersionList(versionList, minVersion, maxVersion, true) -} - -func getFilteredVersionList(versionList []string, minVersion string, maxVersion string, excludeCurrent bool) []string { +func GetFilteredVersionList(versionList []string, minVersion string, maxVersion string) []string { var filteredVersionList []string // Parse the versions for comparison @@ -71,10 +70,6 @@ func getFilteredVersionList(versionList []string, minVersion string, maxVersion continue } if ver.GreaterThanOrEqual(min) && ver.LessThanOrEqual(max) { - // For upgrades, we don't want to show the current version. For creation, it should be shown - if excludeCurrent && ver.Equal(min) { - continue - } filteredVersionList = append(filteredVersionList, version) } } diff --git a/pkg/helper/versions/helpers_test.go b/pkg/helper/versions/helpers_test.go index 32b0e921f1..ddcf02e93b 100644 --- a/pkg/helper/versions/helpers_test.go +++ b/pkg/helper/versions/helpers_test.go @@ -12,7 +12,7 @@ var _ = Describe("Version Helpers", Ordered, func() { Context("when creating a hosted machine pool ", func() { DescribeTable("Filtered versions", func(versionList []string, minVersion string, maxVersion string, expectedVersionList []string) { - filteredVersionList := getFilteredVersionList(versionList, minVersion, maxVersion, false) + filteredVersionList := GetFilteredVersionList(versionList, minVersion, maxVersion) Expect(filteredVersionList).To(BeEquivalentTo(expectedVersionList)) }, Entry("machinepool create", @@ -74,31 +74,5 @@ var _ = Describe("Version Helpers", Ordered, func() { ) }) - Context("when updating a hosted machine pool ", func() { - DescribeTable("Filtered versions", - func(versionList []string, minVersion string, maxVersion string, expectedVersionList []string) { - filteredVersionList := getFilteredVersionList(versionList, minVersion, maxVersion, true) - Expect(filteredVersionList).To(BeEquivalentTo(expectedVersionList)) - }, - Entry("machinepool update", - []string{ - "4.12.22", - "4.12.23", - "4.12.24", - "4.12.25", - "4.12.26", - "4.13.0-0.nightly-2023-02-22-192922", - }, - "4.12.22", - "4.12.26", - []string{ - "4.12.23", - "4.12.24", - "4.12.25", - "4.12.26", - }, - ), - ) - }) }) diff --git a/pkg/ocm/helpers.go b/pkg/ocm/helpers.go index 3d89e0954d..03a712ccd4 100644 --- a/pkg/ocm/helpers.go +++ b/pkg/ocm/helpers.go @@ -63,6 +63,8 @@ const ( USERRoleLabel = "sts_user_role" maxClusterNameLength = 15 + + HcpProduct = "hcp" ) // Regular expression to used to make sure that the identifier or name given by the user is diff --git a/pkg/ocm/versions.go b/pkg/ocm/versions.go index 9d14599833..d2e0601c17 100644 --- a/pkg/ocm/versions.go +++ b/pkg/ocm/versions.go @@ -62,6 +62,11 @@ func (c *Client) ManagedServiceVersionInquiry(serviceType string) (string, error } func (c *Client) GetVersions(channelGroup string, defaultFirst bool) (versions []*cmv1.Version, err error) { + return c.GetVersionsWithProduct("", channelGroup, defaultFirst) +} + +func (c *Client) GetVersionsWithProduct(product string, channelGroup string, + defaultFirst bool) (versions []*cmv1.Version, err error) { collection := c.ocm.ClustersMgmt().V1().Versions() page := 1 size := 100 @@ -72,12 +77,15 @@ func (c *Client) GetVersions(channelGroup string, defaultFirst bool) (versions [ } for { var response *cmv1.VersionsListResponse - response, err = collection.List(). + request := collection.List(). Search(filter). Order(order). Page(page). - Size(size). - Send() + Size(size) + if product != "" { + request.Parameter("product", product) + } + response, err = request.Send() if err != nil { return nil, handleErr(response.Error(), err) } @@ -204,6 +212,20 @@ func (c *Client) GetAvailableUpgrades(versionID string) ([]string, error) { return availableUpgrades, nil } +func GetAvailableUpgradesByCluster(cluster *cmv1.Cluster) []string { + if cluster == nil { + return []string{} + } + return sortVersionsDesc(cluster.Version().AvailableUpgrades()) +} + +func GetNodePoolAvailableUpgrades(nodePool *cmv1.NodePool) []string { + if nodePool == nil { + return []string{} + } + return sortVersionsDesc(nodePool.Version().AvailableUpgrades()) +} + func CreateVersionID(version string, channelGroup string) string { versionID := fmt.Sprintf("openshift-v%s", version) if channelGroup != DefaultChannelGroup { @@ -438,6 +460,7 @@ func (c *Client) ValidateVersion(version string, versionList []string, channelGr Search(filter). Page(1). Size(1). + Parameter("product", HcpProduct). Send() if err != nil { return "", handleErr(response.Error(), err) @@ -456,3 +479,16 @@ func (c *Client) ValidateVersion(version string, versionList []string, channelGr return CreateVersionID(version, channelGroup), nil } + +// sortVersionsDesc sorts list in descending order +func sortVersionsDesc(versions []string) []string { + sort.Slice(versions, func(i, j int) bool { + a, erra := ver.NewVersion(versions[i]) + b, errb := ver.NewVersion(versions[j]) + if erra != nil || errb != nil { + return false + } + return a.GreaterThan(b) + }) + return versions +} diff --git a/pkg/test/helpers.go b/pkg/test/helpers.go index 30e7db397c..e61507b0b1 100644 --- a/pkg/test/helpers.go +++ b/pkg/test/helpers.go @@ -6,6 +6,7 @@ import ( "io" "net/http" "os" + "reflect" "time" . "github.com/onsi/ginkgo/v2" @@ -141,6 +142,39 @@ func FormatNodePoolUpgradePolicyList(upgrades []*v1.NodePoolUpgradePolicy) strin }`, len(upgrades), len(upgrades), outputJson.String()) } +// FormatResource wraps the SDK marshalling and returns a string starting from an object +func FormatResource(resource interface{}) string { + var outputJson bytes.Buffer + var err error + switch reflect.TypeOf(resource).String() { + case "*v1.Version": + if res, ok := resource.(*v1.Version); ok { + err = v1.MarshalVersion(res, &outputJson) + } + case "*v1.NodePool": + if res, ok := resource.(*v1.NodePool); ok { + err = v1.MarshalNodePool(res, &outputJson) + } + case "*v1.MachinePool": + if res, ok := resource.(*v1.MachinePool); ok { + err = v1.MarshalMachinePool(res, &outputJson) + } + case "*v1.ControlPlaneUpgradePolicy": + if res, ok := resource.(*v1.ControlPlaneUpgradePolicy); ok { + err = v1.MarshalControlPlaneUpgradePolicy(res, &outputJson) + } + default: + { + return "NOTIMPLEMENTED" + } + } + if err != nil { + return err.Error() + } + + return outputJson.String() +} + // TestingRuntime is a wrapper for the structure used for testing type TestingRuntime struct { SsoServer *ghttp.Server