From 10411309201578d69b9a88e56bcccb32b64dc7dc Mon Sep 17 00:00:00 2001 From: Guilherme Branco Date: Tue, 12 Mar 2024 17:43:28 -0300 Subject: [PATCH] OCM-6527 | feat: add describe ingress command --- cmd/describe/cmd.go | 23 +++-- cmd/describe/ingress/cmd.go | 165 ++++++++++++++++++++++++++++++++++++ cmd/edit/ingress/cmd.go | 33 ++------ pkg/ocm/ingresses.go | 26 ++++++ 4 files changed, 212 insertions(+), 35 deletions(-) create mode 100644 cmd/describe/ingress/cmd.go diff --git a/cmd/describe/cmd.go b/cmd/describe/cmd.go index f8c5547756..bdb3e4704a 100644 --- a/cmd/describe/cmd.go +++ b/cmd/describe/cmd.go @@ -22,6 +22,7 @@ import ( "github.com/openshift/rosa/cmd/describe/addon" "github.com/openshift/rosa/cmd/describe/admin" "github.com/openshift/rosa/cmd/describe/cluster" + "github.com/openshift/rosa/cmd/describe/ingress" "github.com/openshift/rosa/cmd/describe/installation" "github.com/openshift/rosa/cmd/describe/kubeletconfig" "github.com/openshift/rosa/cmd/describe/machinepool" @@ -39,17 +40,21 @@ var Cmd = &cobra.Command{ } func init() { - Cmd.AddCommand(addon.Cmd) - Cmd.AddCommand(admin.Cmd) - Cmd.AddCommand(cluster.Cmd) - Cmd.AddCommand(service.Cmd) - Cmd.AddCommand(installation.Cmd) - Cmd.AddCommand(upgrade.Cmd) - Cmd.AddCommand(tuningconfigs.Cmd) - Cmd.AddCommand(machinepool.Cmd) - Cmd.AddCommand(kubeletconfig.Cmd) + cmds := []*cobra.Command{ + addon.Cmd, admin.Cmd, cluster.Cmd, service.Cmd, + installation.Cmd, upgrade.Cmd, tuningconfigs.Cmd, + machinepool.Cmd, kubeletconfig.Cmd, ingress.Cmd, + } + for _, cmd := range cmds { + Cmd.AddCommand(cmd) + } flags := Cmd.PersistentFlags() arguments.AddProfileFlag(flags) arguments.AddRegionFlag(flags) + + globallyAvailableCommands := []*cobra.Command{ + ingress.Cmd, + } + arguments.MarkRegionHidden(Cmd, globallyAvailableCommands) } diff --git a/cmd/describe/ingress/cmd.go b/cmd/describe/ingress/cmd.go new file mode 100644 index 0000000000..455c960914 --- /dev/null +++ b/cmd/describe/ingress/cmd.go @@ -0,0 +1,165 @@ +package ingress + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "regexp" + "sort" + "strconv" + "strings" + + cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" + "github.com/spf13/cobra" + + "github.com/openshift/rosa/pkg/helper" + "github.com/openshift/rosa/pkg/ocm" + "github.com/openshift/rosa/pkg/output" + "github.com/openshift/rosa/pkg/rosa" +) + +// Regular expression to used to make sure that the identifier given by the +// user is safe and that it there is no risk of SQL injection: +var ingressKeyRE = regexp.MustCompile(`^[a-z0-9]{3,5}$`) + +var Cmd = &cobra.Command{ + Use: "ingress", + Short: "Show details of the specified ingress within cluster", + Example: `rosa describe ingress -c mycluster`, + Run: run, + Args: func(_ *cobra.Command, argv []string) error { + if len(argv) != 1 { + return fmt.Errorf( + "Expected exactly one command line parameter containing the id of the ingress", + ) + } + return nil + }, +} + +func init() { + ocm.AddClusterFlag(Cmd) + output.AddFlag(Cmd) +} + +func run(_ *cobra.Command, argv []string) { + r := rosa.NewRuntime().WithOCM() + defer r.Cleanup() + + ingressKey := argv[0] + if !ingressKeyRE.MatchString(ingressKey) { + r.Reporter.Errorf( + "Ingress identifier '%s' isn't valid: it must contain only letters or digits", + ingressKey, + ) + os.Exit(1) + } + + cluster := r.FetchCluster() + + ingress, err := r.OCMClient.GetIngress(cluster.ID(), ingressKey) + if err != nil { + r.Reporter.Errorf("Failed to fetch ingress: %v", err) + os.Exit(1) + } + if output.HasFlag() { + var b bytes.Buffer + err := cmv1.MarshalIngress(ingress, &b) + if err != nil { + r.Reporter.Errorf("Failed to generate output for ingress '%s': %v", ingress.ID(), err) + os.Exit(1) + } + ret := make(map[string]interface{}) + err = json.Unmarshal(b.Bytes(), &ret) + if err != nil { + r.Reporter.Errorf("Failed to generate output for ingress '%s': %v", ingress.ID(), err) + os.Exit(1) + } + err = output.Print(ret) + if err != nil { + r.Reporter.Errorf("Failed to output ingress '%s': %v", ingress.ID(), err) + os.Exit(1) + } + return + } + entries := generateEntriesOutput(cluster, ingress) + ingressOutput := "" + keys := helper.MapKeys(entries) + sort.Strings(keys) + minWidth := getMinWidth(keys) + for _, key := range keys { + ingressOutput += fmt.Sprintf("%s: %s\n", key, strings.Repeat(" ", minWidth-len(key))+entries[key]) + } + fmt.Print(ingressOutput) +} + +func getMinWidth(keys []string) int { + minWidth := 0 + for _, key := range keys { + if len(key) > minWidth { + minWidth = len(key) + } + } + return minWidth +} + +func generateEntriesOutput(cluster *cmv1.Cluster, ingress *cmv1.Ingress) map[string]string { + private := false + if ingress.Listening() == cmv1.ListeningMethodInternal { + private = true + } + entries := map[string]string{ + "ID": ingress.ID(), + "Cluster ID": cluster.ID(), + "Default": strconv.FormatBool(ingress.Default()), + "Private": strconv.FormatBool(private), + "LB-Type": string(ingress.LoadBalancerType()), + } + // These are only available for ingress v2 + wildcardPolicy := string(ingress.RouteWildcardPolicy()) + if wildcardPolicy != "" { + entries["Wildcard Policy"] = string(ingress.RouteWildcardPolicy()) + } + namespaceOwnershipPolicy := string(ingress.RouteNamespaceOwnershipPolicy()) + if namespaceOwnershipPolicy != "" { + entries["Namespace Ownership Policy"] = namespaceOwnershipPolicy + } + routeSelectors := "" + if len(ingress.RouteSelectors()) > 0 { + routeSelectors = fmt.Sprintf("%v", ingress.RouteSelectors()) + } + if routeSelectors != "" { + entries["Route Selectors"] = routeSelectors + } + excludedNamespaces := helper.SliceToSortedString(ingress.ExcludedNamespaces()) + if excludedNamespaces != "" { + entries["Excluded Namespaces"] = excludedNamespaces + } + componentRoutes := "" + for component, value := range ingress.ComponentRoutes() { + keys := helper.MapKeys(entries) + minWidth := getMinWidth(keys) + depth := 4 + componentRouteEntries := map[string]string{ + "Hostname": value.Hostname(), + "TLS Secret Ref": value.TlsSecretRef(), + } + componentRoutes += fmt.Sprintf("%s: \n", strings.Repeat(" ", depth)+component) + depth *= 2 + for key, entry := range componentRouteEntries { + componentRoutes += fmt.Sprintf( + "%s: %s\n", + strings.Repeat(" ", depth)+key, + strings.Repeat(" ", minWidth-len(key)-depth)+entry, + ) + } + } + if componentRoutes != "" { + componentRoutes = fmt.Sprintf("\n%s", componentRoutes) + //remove extra \n at the end + componentRoutes = componentRoutes[:len(componentRoutes)-1] + entries["Component Routes"] = componentRoutes + } + return entries +} diff --git a/cmd/edit/ingress/cmd.go b/cmd/edit/ingress/cmd.go index a955a1a063..564050c715 100644 --- a/cmd/edit/ingress/cmd.go +++ b/cmd/edit/ingress/cmd.go @@ -156,11 +156,11 @@ func run(cmd *cobra.Command, argv []string) { r := rosa.NewRuntime().WithAWS().WithOCM() defer r.Cleanup() - ingressID := argv[0] - if !ingressKeyRE.MatchString(ingressID) { + ingressKey := argv[0] + if !ingressKeyRE.MatchString(ingressKey) { r.Reporter.Errorf( "Ingress identifier '%s' isn't valid: it must contain only letters or digits", - ingressID, + ingressKey, ) os.Exit(1) } @@ -223,7 +223,7 @@ func run(cmd *cobra.Command, argv []string) { private = &privArg } // Edit API endpoint instead of ingresses - if ingressID == "api" { + if ingressKey == "api" { clusterConfig := ocm.Spec{ Private: private, } @@ -233,32 +233,13 @@ func run(cmd *cobra.Command, argv []string) { r.Reporter.Errorf("Failed to update cluster API on cluster '%s': %v", clusterKey, err) os.Exit(1) } - r.Reporter.Infof("Updated ingress '%s' on cluster '%s'", ingressID, clusterKey) + r.Reporter.Infof("Updated ingress '%s' on cluster '%s'", ingressKey, clusterKey) os.Exit(0) } - // Try to find the ingress: - r.Reporter.Debugf("Loading ingresses for cluster '%s'", clusterKey) - ingresses, err := r.OCMClient.GetIngresses(cluster.ID()) + ingress, err := r.OCMClient.GetIngress(cluster.ID(), ingressKey) if err != nil { - r.Reporter.Errorf("Failed to get ingresses for cluster '%s': %v", clusterKey, err) - os.Exit(1) - } - - var ingress *cmv1.Ingress - for _, item := range ingresses { - if ingressID == "apps" && item.Default() { - ingress = item - } - if ingressID == "apps2" && !item.Default() { - ingress = item - } - if item.ID() == ingressID { - ingress = item - } - } - if ingress == nil { - r.Reporter.Errorf("Failed to get ingress '%s' for cluster '%s'", ingressID, clusterKey) + r.Reporter.Errorf("Failed to fetch ingress: %v", err) os.Exit(1) } diff --git a/pkg/ocm/ingresses.go b/pkg/ocm/ingresses.go index e9b2179880..beeafc6764 100644 --- a/pkg/ocm/ingresses.go +++ b/pkg/ocm/ingresses.go @@ -17,9 +17,35 @@ limitations under the License. package ocm import ( + "fmt" + cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" ) +func (c *Client) GetIngress(clusterId string, ingressKey string) (*cmv1.Ingress, error) { + ingresses, err := c.GetIngresses(clusterId) + if err != nil { + return nil, err + } + + var ingress *cmv1.Ingress + for _, item := range ingresses { + if ingressKey == "apps" && item.Default() { + ingress = item + } + if ingressKey == "apps2" && !item.Default() { + ingress = item + } + if item.ID() == ingressKey { + ingress = item + } + } + if ingress == nil { + return nil, fmt.Errorf("Failed to get ingress '%s' for cluster '%s'", ingressKey, clusterId) + } + return ingress, nil +} + func (c *Client) GetIngresses(clusterID string) ([]*cmv1.Ingress, error) { response, err := c.ocm.ClustersMgmt().V1(). Clusters().Cluster(clusterID).