-
Notifications
You must be signed in to change notification settings - Fork 193
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
OCM-5677 | feat: Added rosa describe autoscaler
command
#1837
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package autoscaler | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/spf13/cobra" | ||
|
||
"github.com/openshift/rosa/pkg/clusterautoscaler" | ||
"github.com/openshift/rosa/pkg/ocm" | ||
"github.com/openshift/rosa/pkg/output" | ||
"github.com/openshift/rosa/pkg/rosa" | ||
) | ||
|
||
const ( | ||
use = "autoscaler" | ||
short = "Show details of the autoscaler for a cluster" | ||
long = short | ||
example = ` # Describe the autoscaler for cluster 'foo' | ||
rosa describe autoscaler --cluster foo` | ||
) | ||
|
||
func NewDescribeAutoscalerCommand() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: use, | ||
Short: short, | ||
Long: long, | ||
Example: example, | ||
Args: cobra.NoArgs, | ||
Run: rosa.DefaultRunner(rosa.RuntimeWithOCM(), DescribeAutoscalerRunner()), | ||
} | ||
|
||
output.AddFlag(cmd) | ||
ocm.AddClusterFlag(cmd) | ||
return cmd | ||
} | ||
|
||
func DescribeAutoscalerRunner() rosa.CommandRunner { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Command logic is executed by a |
||
return func(_ context.Context, runtime *rosa.Runtime, _ *cobra.Command, _ []string) error { | ||
cluster, err := runtime.OCMClient.GetCluster(runtime.GetClusterKey(), runtime.Creator) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = clusterautoscaler.IsAutoscalerSupported(runtime, cluster) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
autoscaler, err := runtime.OCMClient.GetClusterAutoscaler(cluster.ID()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if autoscaler == nil { | ||
return fmt.Errorf("No autoscaler exists for cluster '%s'", runtime.ClusterKey) | ||
} | ||
Comment on lines
+45
to
+57
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does it make sense to decompose this further into a new
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's ok to keep it that way, it seems like we return nil for With that being said, we could return a boolean from the function for |
||
|
||
if output.HasFlag() { | ||
output.Print(autoscaler) | ||
} else { | ||
fmt.Print(clusterautoscaler.PrintAutoscaler(autoscaler)) | ||
} | ||
return nil | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
package autoscaler | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/http" | ||
"testing" | ||
|
||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" | ||
. "github.com/openshift-online/ocm-sdk-go/testing" | ||
|
||
"github.com/openshift/rosa/pkg/clusterautoscaler" | ||
"github.com/openshift/rosa/pkg/output" | ||
. "github.com/openshift/rosa/pkg/test" | ||
) | ||
|
||
func TestDescribeAutoscaler(t *testing.T) { | ||
RegisterFailHandler(Fail) | ||
RunSpecs(t, "rosa describe autoscaler") | ||
} | ||
|
||
var _ = Describe("rosa describe autoscaler", func() { | ||
Context("Create Command", func() { | ||
It("Returns Command", func() { | ||
|
||
cmd := NewDescribeAutoscalerCommand() | ||
Expect(cmd).NotTo(BeNil()) | ||
|
||
Expect(cmd.Use).To(Equal(use)) | ||
Expect(cmd.Example).To(Equal(example)) | ||
Expect(cmd.Short).To(Equal(short)) | ||
Expect(cmd.Long).To(Equal(long)) | ||
Expect(cmd.Args).NotTo(BeNil()) | ||
Expect(cmd.Run).NotTo(BeNil()) | ||
|
||
flag := cmd.Flags().Lookup("cluster") | ||
Expect(flag).NotTo(BeNil()) | ||
|
||
flag = cmd.Flags().Lookup("output") | ||
Expect(flag).NotTo(BeNil()) | ||
}) | ||
}) | ||
|
||
Context("Execute command", func() { | ||
|
||
var t *TestingRuntime | ||
|
||
BeforeEach(func() { | ||
t = NewTestRuntime() | ||
output.SetOutput("") | ||
}) | ||
|
||
AfterEach(func() { | ||
output.SetOutput("") | ||
}) | ||
|
||
It("Returns an error if the cluster does not exist", func() { | ||
|
||
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, FormatClusterList(make([]*cmv1.Cluster, 0)))) | ||
t.SetCluster("cluster", nil) | ||
|
||
runner := DescribeAutoscalerRunner() | ||
err := runner(context.Background(), t.RosaRuntime, nil, nil) | ||
Expect(err).To(HaveOccurred()) | ||
Expect(err.Error()).To( | ||
Equal("There is no cluster with identifier or name 'cluster'")) | ||
}) | ||
|
||
It("Returns an error if the cluster is HCP", func() { | ||
cluster := MockCluster(func(c *cmv1.ClusterBuilder) { | ||
h := &cmv1.HypershiftBuilder{} | ||
h.Enabled(true) | ||
c.Hypershift(h) | ||
}) | ||
|
||
t.SetCluster(cluster.Name(), cluster) | ||
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, FormatClusterList([]*cmv1.Cluster{cluster}))) | ||
|
||
runner := DescribeAutoscalerRunner() | ||
err := runner(context.Background(), t.RosaRuntime, nil, nil) | ||
|
||
Expect(err).NotTo(BeNil()) | ||
Expect(err.Error()).To(ContainSubstring(clusterautoscaler.NoHCPAutoscalerSupportMessage)) | ||
}) | ||
|
||
It("Returns an error if the cluster is not ready", func() { | ||
cluster := MockCluster(func(c *cmv1.ClusterBuilder) { | ||
c.State(cmv1.ClusterStateInstalling) | ||
}) | ||
|
||
t.SetCluster(cluster.Name(), cluster) | ||
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, FormatClusterList([]*cmv1.Cluster{cluster}))) | ||
|
||
runner := DescribeAutoscalerRunner() | ||
err := runner(context.Background(), t.RosaRuntime, nil, nil) | ||
|
||
Expect(err).NotTo(BeNil()) | ||
Expect(err.Error()).To(ContainSubstring(" is not yet ready")) | ||
}) | ||
|
||
It("Returns an error if OCM API fails to return autoscaler", func() { | ||
cluster := MockCluster(func(c *cmv1.ClusterBuilder) { | ||
c.State(cmv1.ClusterStateReady) | ||
}) | ||
|
||
t.SetCluster(cluster.Name(), cluster) | ||
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, FormatClusterList([]*cmv1.Cluster{cluster}))) | ||
t.ApiServer.RouteToHandler( | ||
"GET", | ||
fmt.Sprintf("/api/clusters_mgmt/v1/clusters/%s/autoscaler", cluster.ID()), | ||
RespondWithJSON(http.StatusInternalServerError, "{}")) | ||
|
||
runner := DescribeAutoscalerRunner() | ||
err := runner(context.Background(), t.RosaRuntime, nil, nil) | ||
|
||
Expect(err).NotTo(BeNil()) | ||
Expect(err.Error()).To(ContainSubstring("status is 500")) | ||
|
||
}) | ||
|
||
It("Returns an error if no autoscaler exists", func() { | ||
cluster := MockCluster(func(c *cmv1.ClusterBuilder) { | ||
c.State(cmv1.ClusterStateReady) | ||
}) | ||
|
||
t.SetCluster(cluster.Name(), cluster) | ||
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, FormatClusterList([]*cmv1.Cluster{cluster}))) | ||
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusNotFound, "{}")) | ||
|
||
runner := DescribeAutoscalerRunner() | ||
err := runner(context.Background(), t.RosaRuntime, nil, nil) | ||
|
||
Expect(err).NotTo(BeNil()) | ||
Expect(err.Error()).To(ContainSubstring("No autoscaler exists for cluster 'cluster'")) | ||
|
||
}) | ||
|
||
It("Prints the autoscaler to stdout", func() { | ||
cluster := MockCluster(func(c *cmv1.ClusterBuilder) { | ||
c.State(cmv1.ClusterStateReady) | ||
}) | ||
|
||
t.SetCluster(cluster.Name(), cluster) | ||
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, FormatClusterList([]*cmv1.Cluster{cluster}))) | ||
|
||
autoscaler := MockAutoscaler(nil) | ||
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, FormatResource(autoscaler))) | ||
|
||
runner := DescribeAutoscalerRunner() | ||
err := runner(context.Background(), t.RosaRuntime, nil, nil) | ||
|
||
Expect(err).NotTo(HaveOccurred()) | ||
}) | ||
|
||
It("Prints the autoscaler in JSON", func() { | ||
output.SetOutput("json") | ||
Expect(output.HasFlag()).To(BeTrue()) | ||
|
||
cluster := MockCluster(func(c *cmv1.ClusterBuilder) { | ||
c.State(cmv1.ClusterStateReady) | ||
}) | ||
|
||
t.SetCluster(cluster.Name(), cluster) | ||
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, FormatClusterList([]*cmv1.Cluster{cluster}))) | ||
|
||
autoscaler := MockAutoscaler(nil) | ||
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, FormatResource(autoscaler))) | ||
|
||
runner := DescribeAutoscalerRunner() | ||
err := runner(context.Background(), t.RosaRuntime, nil, nil) | ||
|
||
Expect(err).NotTo(HaveOccurred()) | ||
|
||
}) | ||
|
||
It("Prints the autoscaler in YAML", func() { | ||
output.SetOutput("yaml") | ||
Expect(output.HasFlag()).To(BeTrue()) | ||
|
||
cluster := MockCluster(func(c *cmv1.ClusterBuilder) { | ||
c.State(cmv1.ClusterStateReady) | ||
}) | ||
|
||
t.SetCluster(cluster.Name(), cluster) | ||
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, FormatClusterList([]*cmv1.Cluster{cluster}))) | ||
|
||
autoscaler := MockAutoscaler(nil) | ||
t.ApiServer.AppendHandlers(RespondWithJSON(http.StatusOK, FormatResource(autoscaler))) | ||
|
||
runner := DescribeAutoscalerRunner() | ||
err := runner(context.Background(), t.RosaRuntime, nil, nil) | ||
|
||
Expect(err).NotTo(HaveOccurred()) | ||
}) | ||
}) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The command is returned, fully initialised from a function. There is no use of global variables (ignoring the
output.AddFlag
andocm.AddClusterFlag
calls which are beyond the scope of this particular refactor). There is no use ofinit