From 45096000b14e12c77acf49f94abea88e64440a24 Mon Sep 17 00:00:00 2001 From: cdavid Date: Tue, 14 Mar 2023 08:58:45 -0700 Subject: [PATCH] Restructure and add commands for endpoints and certificates (#94) This PR achieves the following: * updates documentation (needs more work) * complete the rename from configure to auth (we left some artifacts behind) * adds support for downloading the root CA cert via cluster cert download * adds support for listing the cluster endpoints via cluster network endpoint list * moves NAL assign to cluster network allow-list assign/unassign * moves read-replica from top level to cluster read-replica * moves regions from cluster describe-regions to region list * moves instance-types from cluster describe-instances to region instance list * moves vpc-peering from top-level to vpc peering --- README.md | 40 ++++----- cmd/{configure.go => auth.go} | 3 +- cmd/cluster/cert/cert.go | 80 +++++++++++++++++ cmd/cluster/cluster.go | 19 ++-- cmd/cluster/describe_cluster.go | 2 +- cmd/cluster/list_cluster.go | 2 +- cmd/cluster/network/endpoint/endpoint.go | 33 +++++++ cmd/cluster/network/endpoint/list_endpoint.go | 89 +++++++++++++++++++ .../{ => network/nal}/assign_nal_cluster.go | 7 +- cmd/cluster/network/nal/nal.go | 33 +++++++ .../{ => network/nal}/unassign_nal_cluster.go | 7 +- cmd/cluster/network/network.go | 39 ++++++++ .../read-replica}/read_replica.go | 49 ++++------ cmd/{cluster => region}/cloud_regions.go | 25 ++++-- cmd/{cluster => region}/instance_types.go | 23 +++-- cmd/root.go | 8 +- cmd/util/util.go | 12 +++ .../peering}/vpc_peering.go | 14 +-- cmd/vpc/vpc.go | 3 + go.mod | 14 +-- go.sum | 10 +++ internal/client/client.go | 11 +++ internal/formatter/endpoints.go | 88 ++++++++++++++++++ 23 files changed, 505 insertions(+), 106 deletions(-) rename cmd/{configure.go => auth.go} (96%) create mode 100644 cmd/cluster/cert/cert.go create mode 100644 cmd/cluster/network/endpoint/endpoint.go create mode 100644 cmd/cluster/network/endpoint/list_endpoint.go rename cmd/cluster/{ => network/nal}/assign_nal_cluster.go (91%) create mode 100644 cmd/cluster/network/nal/nal.go rename cmd/cluster/{ => network/nal}/unassign_nal_cluster.go (91%) create mode 100644 cmd/cluster/network/network.go rename cmd/{readreplica => cluster/read-replica}/read_replica.go (86%) rename cmd/{cluster => region}/cloud_regions.go (74%) rename cmd/{cluster => region}/instance_types.go (82%) rename cmd/{vpcpeering => vpc/peering}/vpc_peering.go (97%) create mode 100644 internal/formatter/endpoints.go diff --git a/README.md b/README.md index 871730e7..223a4be0 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ This CLI support 3 possibles configurations: ``` * Using a configuration file called `.ybm-cli.yaml` under your `$HOME` directory. - You can use the command `ybm configure` to help to setup the file + You can use the command `ybm auth` to help to setup the file * Using environment variables (all need to start with `YBM_`) ```sh @@ -56,12 +56,12 @@ ybm cluster create #### List Clusters ```sh -ybm cluster get +ybm cluster list ``` -#### Get Cluster +#### Describe Cluster ```sh -ybm cluster get \ +ybm cluster describe \ --cluster-name=test-cluster-1 ``` @@ -83,18 +83,18 @@ ybm network-allow-list create \ #### List Network Allow Lists ```sh -ybm network-allow-list get +ybm network-allow-list list ``` -#### Get Network Allow List +#### Filter Network Allow List ```sh -ybm network-allow-list get \ +ybm network-allow-list list \ --name=admins ``` #### Assign network allow list to cluster ```sh -ybm cluster assign network-allow-list \ +ybm cluster network allow-list assign \ --cluster-name=test-cluster \ --network-allow-list=admins ``` @@ -109,7 +109,7 @@ ybm network-allow-list delete \ #### Create Read Replica ```sh -ybm read-replica create \ +ybm cluster read-replica create \ --cluster-name=test-cluster \ --replica=num-cores=,memory-mb=,disk-size-gb=,code=,region=,num-nodes=,vpc=,num-replicas=,multi-zone= ``` @@ -117,12 +117,12 @@ The `--replica` tag is optional. If omitted, a single read replica will be creat #### List Read Replicas ```sh -ybm read-replica get \ +ybm cluster read-replica list \ --cluster-name=test-cluster ``` #### Update Read Replicas ```sh -ybm read-replica update \ +ybm cluster read-replica update \ --cluster-name=test-cluster \ --replica=num-cores=,memory-mb=,disk-size-gb=,code=,region=,num-nodes=,vpc=,num-replicas=,multi-zone= ``` @@ -130,7 +130,7 @@ The `--replica` tag is optional. If omitted, the cluster will be updated with a #### Delete Read Replicas ```sh -ybm read-replica delete \ +ybm cluster read-replica delete \ --cluster-name=test-cluster ``` @@ -148,11 +148,11 @@ ybm vpc create \ #### List VPCs ```sh -ybm vpc get +ybm vpc list ``` -#### Get VPC +#### Filter VPC ```sh -ybm vpc get \ +ybm vpc list \ --name=demo-vpc ``` @@ -167,7 +167,7 @@ ybm vpc delete \ #### Create VPC Peering ```sh -ybm vpc-peering create \ +ybm vpc peering create \ --name=demo-peer \ --vpc-name=demo-vpc \ --cloud=GCP \ @@ -179,18 +179,18 @@ ybm vpc-peering create \ #### List VPC Peerings ```sh -ybm vpc-peering get +ybm vpc peering list ``` -#### Get VPC Peering +#### Filter VPC Peering ```sh -ybm vpc-peering get \ +ybm vpc peering list \ --name=demo-peer ``` #### Delete VPC Peering ```sh -ybm vpc-peering delete \ +ybm vpc peering delete \ --name=demo-peer ``` diff --git a/cmd/configure.go b/cmd/auth.go similarity index 96% rename from cmd/configure.go rename to cmd/auth.go index 54765fc7..e277e327 100644 --- a/cmd/configure.go +++ b/cmd/auth.go @@ -28,8 +28,7 @@ import ( "golang.org/x/term" ) -// configureCmd represents the configure command -var configureCmd = &cobra.Command{ +var authCmd = &cobra.Command{ Use: "auth", Short: "Authenticate ybm CLI", Long: "Authenticate the ybm CLI through this command by providing the API Key.", diff --git a/cmd/cluster/cert/cert.go b/cmd/cluster/cert/cert.go new file mode 100644 index 00000000..23b380dd --- /dev/null +++ b/cmd/cluster/cert/cert.go @@ -0,0 +1,80 @@ +// Licensed to Yugabyte, Inc. under one or more contributor license +// agreements. See the NOTICE file distributed with this work for +// additional information regarding copyright ownership. Yugabyte +// licenses this file to you under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package cert + +import ( + "fmt" + "os" + + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + ybmAuthClient "github.com/yugabyte/ybm-cli/internal/client" +) + +var CertCmd = &cobra.Command{ + Use: "cert", + Short: "Get the root CA certificate", + Long: "Get the root CA certificate for your YBM clusters", + Run: func(cmd *cobra.Command, args []string) { + cmd.Help() + }, +} + +var downloadCertificate = &cobra.Command{ + Use: "download", + Short: "Download the root CA certificate", + Long: `Download the root CA certificate`, + Run: func(cmd *cobra.Command, args []string) { + authApi, err := ybmAuthClient.NewAuthApiClient() + if err != nil { + logrus.Fatalf("Could not initiate api client: %s", err.Error()) + } + authApi.GetInfo("", "") + certificate, err := authApi.GetConnectionCertificate() + if err != nil { + logrus.Fatal("Fail to retrieve connection certificate: ", err) + } + + if output, _ := cmd.Flags().GetString("out"); output != "" { + // check if the file exists + if _, err := os.Stat(output); err == nil { + // Only overwrite if force is set + if !cmd.Flags().Changed("force") { + logrus.Fatalf("File %s already exists", output) + } + } + + f, err := os.Create(output) + if err != nil { + logrus.Fatal("Fail to create output file: ", err) + } + defer f.Close() + _, err = f.WriteString(certificate) + if err != nil { + logrus.Fatal("Fail to write to output file: ", err) + } + } else { + fmt.Println(certificate) + } + + }, +} + +func init() { + CertCmd.AddCommand(downloadCertificate) + downloadCertificate.Flags().StringP("out", "", "", "[OPTIONAL] Output file name (default: stdout)") + downloadCertificate.Flags().BoolP("force", "f", false, "[OPTIONAL] Overwrite the output file if it exists") +} diff --git a/cmd/cluster/cluster.go b/cmd/cluster/cluster.go index 10c8986e..29a0eb76 100644 --- a/cmd/cluster/cluster.go +++ b/cmd/cluster/cluster.go @@ -17,6 +17,9 @@ package cluster import ( "github.com/spf13/cobra" + "github.com/yugabyte/ybm-cli/cmd/cluster/cert" + "github.com/yugabyte/ybm-cli/cmd/cluster/network" + readreplica "github.com/yugabyte/ybm-cli/cmd/cluster/read-replica" ) // getCmd represents the list command @@ -30,15 +33,13 @@ var ClusterCmd = &cobra.Command{ } func init() { - ClusterCmd.AddCommand() + ClusterCmd.AddCommand(cert.CertCmd) - // Here you will define your flags and configuration settings. + ClusterCmd.AddCommand(network.NetworkCmd) + network.NetworkCmd.PersistentFlags().StringVarP(&network.ClusterName, "cluster-name", "c", "", "[REQUIRED] The name of the cluster.") + network.NetworkCmd.MarkPersistentFlagRequired("cluster-name") - // Cobra supports Persistent Flags which will work for this command - // and all subcommands, e.g.: - // listCmd.PersistentFlags().String("foo", "", "A help for foo") - - // Cobra supports local flags which will only run when this command - // is called directly, e.g.: - // listCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + ClusterCmd.AddCommand(readreplica.ReadReplicaCmd) + readreplica.ReadReplicaCmd.PersistentFlags().StringVarP(&readreplica.ClusterName, "cluster-name", "c", "", "[REQUIRED] The name of the cluster.") + readreplica.ReadReplicaCmd.MarkPersistentFlagRequired("cluster-name") } diff --git a/cmd/cluster/describe_cluster.go b/cmd/cluster/describe_cluster.go index 4abde9a3..bc71b500 100644 --- a/cmd/cluster/describe_cluster.go +++ b/cmd/cluster/describe_cluster.go @@ -27,7 +27,7 @@ import ( var describeClusterCmd = &cobra.Command{ Use: "describe", - Short: "Describe a cluster in YugabyteDB Managed", + Short: "Describe a cluster", Long: "Describe a cluster in YugabyteDB Managed", Run: func(cmd *cobra.Command, args []string) { authApi, err := ybmAuthClient.NewAuthApiClient() diff --git a/cmd/cluster/list_cluster.go b/cmd/cluster/list_cluster.go index 09424b3c..fc0ef5d3 100644 --- a/cmd/cluster/list_cluster.go +++ b/cmd/cluster/list_cluster.go @@ -27,7 +27,7 @@ import ( var listClusterCmd = &cobra.Command{ Use: "list", - Short: "List clusters in YugabyteDB Managed", + Short: "List clusters", Long: "List clusters in YugabyteDB Managed", Run: func(cmd *cobra.Command, args []string) { authApi, err := ybmAuthClient.NewAuthApiClient() diff --git a/cmd/cluster/network/endpoint/endpoint.go b/cmd/cluster/network/endpoint/endpoint.go new file mode 100644 index 00000000..7a44cf5d --- /dev/null +++ b/cmd/cluster/network/endpoint/endpoint.go @@ -0,0 +1,33 @@ +// Licensed to Yugabyte, Inc. under one or more contributor license +// agreements. See the NOTICE file distributed with this work for +// additional information regarding copyright ownership. Yugabyte +// licenses this file to you under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package endpoint + +import ( + "github.com/spf13/cobra" +) + +var EndpointCmd = &cobra.Command{ + Use: "endpoint", + Short: "Manage endpoints for a cluster", + Long: "Manage endpoints for a cluster", + Run: func(cmd *cobra.Command, args []string) { + cmd.Help() + }, +} + +func init() { + EndpointCmd.AddCommand() +} diff --git a/cmd/cluster/network/endpoint/list_endpoint.go b/cmd/cluster/network/endpoint/list_endpoint.go new file mode 100644 index 00000000..e7cc4a2d --- /dev/null +++ b/cmd/cluster/network/endpoint/list_endpoint.go @@ -0,0 +1,89 @@ +// Licensed to Yugabyte, Inc. under one or more contributor license +// agreements. See the NOTICE file distributed with this work for +// additional information regarding copyright ownership. Yugabyte +// licenses this file to you under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package endpoint + +import ( + "os" + + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/yugabyte/ybm-cli/cmd/util" + ybmAuthClient "github.com/yugabyte/ybm-cli/internal/client" + ybmclient "github.com/yugabyte/yugabytedb-managed-go-client-internal" + + "github.com/yugabyte/ybm-cli/internal/formatter" +) + +var listEndpointCmd = &cobra.Command{ + Use: "list", + Short: "List endpoints for a cluster", + Long: "List endpoints for a cluster", + Run: func(cmd *cobra.Command, args []string) { + authApi, err := ybmAuthClient.NewAuthApiClient() + if err != nil { + logrus.Fatalf("Could not initiate api client: %s", err.Error()) + } + authApi.GetInfo("", "") + + clusterName, _ := cmd.Flags().GetString("cluster-name") + clusterListRequest := authApi.ListClusters() + // if user filters by name, add it to the request + clusterListRequest = clusterListRequest.Name(clusterName) + + resp, r, err := clusterListRequest.Execute() + if err != nil { + logrus.Debugf("Full HTTP response: %v", r) + logrus.Fatalf("Error when calling `ClusterApi.ListClusters`: %s", ybmAuthClient.GetApiErrorDetails(err)) + } + + if len(resp.GetData()) == 0 { + logrus.Fatalf("Cluster not found") + } + + clusterEndpoints := resp.GetData()[0].Info.ClusterEndpoints + + region, _ := cmd.Flags().GetString("region") + if region != "" { + clusterEndpoints = util.Filter(clusterEndpoints, func(endpoint ybmclient.Endpoint) bool { + return endpoint.GetRegion() == region + }) + } + + accessibility, _ := cmd.Flags().GetString("accessibility") + if accessibility != "" { + clusterEndpoints = util.Filter(clusterEndpoints, func(endpoint ybmclient.Endpoint) bool { + return string(endpoint.GetAccessibilityType()) == accessibility + }) + } + + if len(clusterEndpoints) == 0 { + logrus.Fatalf("No endpoints found") + } + + endpointsCtx := formatter.Context{ + Output: os.Stdout, + Format: formatter.NewEndpointFormat(viper.GetString("output")), + } + formatter.EndpointWrite(endpointsCtx, clusterEndpoints) + }, +} + +func init() { + EndpointCmd.AddCommand(listEndpointCmd) + listEndpointCmd.Flags().String("accessibility", "", "[OPTIONAL] Accessibility of the endpoint") + listEndpointCmd.Flags().String("region", "", "[OPTIONAL] Region of the endpoint") +} diff --git a/cmd/cluster/assign_nal_cluster.go b/cmd/cluster/network/nal/assign_nal_cluster.go similarity index 91% rename from cmd/cluster/assign_nal_cluster.go rename to cmd/cluster/network/nal/assign_nal_cluster.go index 231fbad7..423712f7 100644 --- a/cmd/cluster/assign_nal_cluster.go +++ b/cmd/cluster/network/nal/assign_nal_cluster.go @@ -13,7 +13,7 @@ // specific language governing permissions and limitations // under the License. -package cluster +package nal import ( "fmt" @@ -84,10 +84,7 @@ var assignClusterCmd = &cobra.Command{ } func init() { - ClusterCmd.AddCommand(assignClusterCmd) - assignClusterCmd.Flags().String("cluster-name", "", "[REQUIRED] The name of the cluster to be assigned.") - assignClusterCmd.MarkFlagRequired("cluster-name") + AllowListCmd.AddCommand(assignClusterCmd) assignClusterCmd.Flags().String("network-allow-list", "", "[REQUIRED] The name of the network allow list to be assigned.") - // Marked as required for now since as of now network allow list is the only resource that can be assigned assignClusterCmd.MarkFlagRequired("network-allow-list") } diff --git a/cmd/cluster/network/nal/nal.go b/cmd/cluster/network/nal/nal.go new file mode 100644 index 00000000..2f94e021 --- /dev/null +++ b/cmd/cluster/network/nal/nal.go @@ -0,0 +1,33 @@ +// Licensed to Yugabyte, Inc. under one or more contributor license +// agreements. See the NOTICE file distributed with this work for +// additional information regarding copyright ownership. Yugabyte +// licenses this file to you under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package nal + +import ( + "github.com/spf13/cobra" +) + +var AllowListCmd = &cobra.Command{ + Use: "allow-list", + Short: "Manage network allow-list operations for a cluster", + Long: "Manage network allow-list operations for a cluster", + Run: func(cmd *cobra.Command, args []string) { + cmd.Help() + }, +} + +func init() { + AllowListCmd.AddCommand() +} diff --git a/cmd/cluster/unassign_nal_cluster.go b/cmd/cluster/network/nal/unassign_nal_cluster.go similarity index 91% rename from cmd/cluster/unassign_nal_cluster.go rename to cmd/cluster/network/nal/unassign_nal_cluster.go index c3a5fa8e..ee3e2e6f 100644 --- a/cmd/cluster/unassign_nal_cluster.go +++ b/cmd/cluster/network/nal/unassign_nal_cluster.go @@ -13,7 +13,7 @@ // specific language governing permissions and limitations // under the License. -package cluster +package nal import ( "fmt" @@ -93,10 +93,7 @@ var unassignClusterCmd = &cobra.Command{ } func init() { - ClusterCmd.AddCommand(unassignClusterCmd) - unassignClusterCmd.Flags().String("cluster-name", "", "[REQUIRED] The name of the cluster to be unassigned.") - unassignClusterCmd.MarkFlagRequired("cluster-name") + AllowListCmd.AddCommand(unassignClusterCmd) unassignClusterCmd.Flags().String("network-allow-list", "", "[REQUIRED] The name of the network allow list to be unassigned.") - // Marked as required for now since as of now network allow list is the only resource that can be unassigned unassignClusterCmd.MarkFlagRequired("network-allow-list") } diff --git a/cmd/cluster/network/network.go b/cmd/cluster/network/network.go new file mode 100644 index 00000000..619072ed --- /dev/null +++ b/cmd/cluster/network/network.go @@ -0,0 +1,39 @@ +// Licensed to Yugabyte, Inc. under one or more contributor license +// agreements. See the NOTICE file distributed with this work for +// additional information regarding copyright ownership. Yugabyte +// licenses this file to you under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package network + +import ( + "github.com/spf13/cobra" + "github.com/yugabyte/ybm-cli/cmd/cluster/network/endpoint" + "github.com/yugabyte/ybm-cli/cmd/cluster/network/nal" +) + +var ClusterName string + +var NetworkCmd = &cobra.Command{ + Use: "network", + Short: "Manage network operations", + Long: "Manage network operations for a cluster", + Run: func(cmd *cobra.Command, args []string) { + cmd.Help() + }, +} + +func init() { + NetworkCmd.AddCommand() + NetworkCmd.AddCommand(nal.AllowListCmd) + NetworkCmd.AddCommand(endpoint.EndpointCmd) +} diff --git a/cmd/readreplica/read_replica.go b/cmd/cluster/read-replica/read_replica.go similarity index 86% rename from cmd/readreplica/read_replica.go rename to cmd/cluster/read-replica/read_replica.go index e547604e..46e040c3 100644 --- a/cmd/readreplica/read_replica.go +++ b/cmd/cluster/read-replica/read_replica.go @@ -30,7 +30,7 @@ import ( ybmclient "github.com/yugabyte/yugabytedb-managed-go-client-internal" ) -var clusterName string +var ClusterName string var allReplicaOpt []string var ReadReplicaCmd = &cobra.Command{ @@ -174,25 +174,25 @@ func printReadReplicaOutput(resp ybmclient.ReadReplicaListResponse) { var getReadReplicaCmd = &cobra.Command{ Use: "get", - Short: "Get read replica in YugabyteDB Managed", + Short: "Get read replica", Long: "Get read replica in YugabyteDB Managed", Run: func(cmd *cobra.Command, args []string) { listReadReplicaCmd.Run(cmd, args) - logrus.Warnln("\nThe command `ybm read-replica get` is deprecated. Please use `ybm read-replica list` instead.") + logrus.Warnln("\nThe command `ybm cluster read-replica get` is deprecated. Please use `ybm cluster read-replica list` instead.") }, } var listReadReplicaCmd = &cobra.Command{ Use: "list", - Short: "List read replica in YugabyteDB Managed", - Long: "List read replica in YugabyteDB Managed", + Short: "List read replicas", + Long: "List read replicas in YugabyteDB Managed", Run: func(cmd *cobra.Command, args []string) { authApi, err := ybmAuthClient.NewAuthApiClient() if err != nil { logrus.Fatalf("could not initiate api client: %s", err.Error()) } authApi.GetInfo("", "") - clusterID, err := authApi.GetClusterIdByName(clusterName) + clusterID, err := authApi.GetClusterIdByName(ClusterName) if err != nil { logrus.Fatal(err) } @@ -209,7 +209,7 @@ var listReadReplicaCmd = &cobra.Command{ var createReadReplicaCmd = &cobra.Command{ Use: "create", - Short: "Create read replica in YugabyteDB Managed", + Short: "Create read replica", Long: "Create read replica in YugabyteDB Managed", Run: func(cmd *cobra.Command, args []string) { authApi, err := ybmAuthClient.NewAuthApiClient() @@ -217,7 +217,7 @@ var createReadReplicaCmd = &cobra.Command{ logrus.Fatalf("could not initiate api client: %s", err.Error()) } authApi.GetInfo("", "") - clusterID, err := authApi.GetClusterIdByName(clusterName) + clusterID, err := authApi.GetClusterIdByName(ClusterName) if err != nil { logrus.Fatal(err) } @@ -247,7 +247,7 @@ var createReadReplicaCmd = &cobra.Command{ logrus.Fatalf("Error when calling `ReadReplicaApi.CreateReadReplica`: %s", ybmAuthClient.GetApiErrorDetails(err)) } - msg := fmt.Sprintf("Read Replica is being created for cluster %s", formatter.Colorize(clusterName, formatter.GREEN_COLOR)) + msg := fmt.Sprintf("Read Replica is being created for cluster %s", formatter.Colorize(ClusterName, formatter.GREEN_COLOR)) if viper.GetBool("wait") { returnStatus, err := authApi.WaitForTaskCompletion(clusterID, "CLUSTER", "CREATE_READ_REPLICA", []string{"FAILED", "SUCCEEDED"}, msg, 1800) @@ -257,7 +257,7 @@ var createReadReplicaCmd = &cobra.Command{ if returnStatus != "SUCCEEDED" { logrus.Fatalf("Operation failed with error: %s", returnStatus) } - fmt.Printf("Read Replica has been created for cluster %s.\n", formatter.Colorize(clusterName, formatter.GREEN_COLOR)) + fmt.Printf("Read Replica has been created for cluster %s.\n", formatter.Colorize(ClusterName, formatter.GREEN_COLOR)) resp, r, err = authApi.ListReadReplicas(clusterID).Execute() if err != nil { @@ -273,7 +273,7 @@ var createReadReplicaCmd = &cobra.Command{ var updateReadReplicaCmd = &cobra.Command{ Use: "update", - Short: "Edit read replica in YugabyteDB Managed", + Short: "Edit read replica", Long: "Edit read replica in YugabyteDB Managed", Run: func(cmd *cobra.Command, args []string) { authApi, err := ybmAuthClient.NewAuthApiClient() @@ -281,7 +281,7 @@ var updateReadReplicaCmd = &cobra.Command{ logrus.Fatalf("could not initiate api client: %s", err.Error()) } authApi.GetInfo("", "") - clusterID, err := authApi.GetClusterIdByName(clusterName) + clusterID, err := authApi.GetClusterIdByName(ClusterName) if err != nil { logrus.Fatal(err) } @@ -310,7 +310,7 @@ var updateReadReplicaCmd = &cobra.Command{ logrus.Debugf("Full HTTP response: %v", r) logrus.Fatalf("Error when calling `ReadReplicaApi.EditReadReplicas`: %s", ybmAuthClient.GetApiErrorDetails(err)) } - msg := fmt.Sprintf("Read Replica is being updated for cluster %s", formatter.Colorize(clusterName, formatter.GREEN_COLOR)) + msg := fmt.Sprintf("Read Replica is being updated for cluster %s", formatter.Colorize(ClusterName, formatter.GREEN_COLOR)) if viper.GetBool("wait") { returnStatus, err := authApi.WaitForTaskCompletion(clusterID, "CLUSTER", "EDIT_READ_REPLICA", []string{"FAILED", "SUCCEEDED"}, msg, 1800) @@ -320,7 +320,7 @@ var updateReadReplicaCmd = &cobra.Command{ if returnStatus != "SUCCEEDED" { logrus.Fatalf("Operation failed with error: %s", returnStatus) } - fmt.Printf("Read Replica has been updated for cluster %s.\n", formatter.Colorize(clusterName, formatter.GREEN_COLOR)) + fmt.Printf("Read Replica has been updated for cluster %s.\n", formatter.Colorize(ClusterName, formatter.GREEN_COLOR)) resp, r, err = authApi.ListReadReplicas(clusterID).Execute() if err != nil { @@ -336,7 +336,7 @@ var updateReadReplicaCmd = &cobra.Command{ var deleteReadReplicaCmd = &cobra.Command{ Use: "delete", - Short: "Delete read replica from YugabyteDB Managed", + Short: "Delete read replica", Long: "Delete read replica from YugabyteDB Managed", Run: func(cmd *cobra.Command, args []string) { authApi, err := ybmAuthClient.NewAuthApiClient() @@ -344,7 +344,7 @@ var deleteReadReplicaCmd = &cobra.Command{ logrus.Fatalf("could not initiate api client: %s", err.Error()) } authApi.GetInfo("", "") - clusterID, err := authApi.GetClusterIdByName(clusterName) + clusterID, err := authApi.GetClusterIdByName(ClusterName) if err != nil { logrus.Fatal(err) } @@ -353,7 +353,7 @@ var deleteReadReplicaCmd = &cobra.Command{ logrus.Debugf("Full HTTP response: %v", r) logrus.Fatalf("Error when calling `ReadReplicaApi.DeleteReadReplica`: %s", ybmAuthClient.GetApiErrorDetails(err)) } - msg := fmt.Sprintf("Read Replica is being deleted for cluster %s", formatter.Colorize(clusterName, formatter.GREEN_COLOR)) + msg := fmt.Sprintf("Read Replica is being deleted for cluster %s", formatter.Colorize(ClusterName, formatter.GREEN_COLOR)) if viper.GetBool("wait") { returnStatus, err := authApi.WaitForTaskCompletion(clusterID, "CLUSTER", "DELETE_READ_REPLICA", []string{"FAILED", "SUCCEEDED"}, msg, 1800) @@ -363,35 +363,24 @@ var deleteReadReplicaCmd = &cobra.Command{ if returnStatus != "SUCCEEDED" { logrus.Fatalf("Operation failed with error: %s", returnStatus) } - fmt.Printf("All Read Replica has been deleted for cluster %s.\n", formatter.Colorize(clusterName, formatter.GREEN_COLOR)) + fmt.Printf("All Read Replica has been deleted for cluster %s.\n", formatter.Colorize(ClusterName, formatter.GREEN_COLOR)) return } - fmt.Printf("All Read Replica has been deleted for cluster %s.\n", formatter.Colorize(clusterName, formatter.GREEN_COLOR)) + fmt.Printf("All Read Replica has been deleted for cluster %s.\n", formatter.Colorize(ClusterName, formatter.GREEN_COLOR)) }, } func init() { ReadReplicaCmd.AddCommand(getReadReplicaCmd) - getReadReplicaCmd.Flags().StringVarP(&clusterName, "cluster-name", "c", "", "[REQUIRED] The name of the cluster.") - getReadReplicaCmd.MarkFlagRequired("cluster-name") ReadReplicaCmd.AddCommand(listReadReplicaCmd) - listReadReplicaCmd.Flags().StringVarP(&clusterName, "cluster-name", "c", "", "[REQUIRED] The name of the cluster.") - listReadReplicaCmd.MarkFlagRequired("cluster-name") ReadReplicaCmd.AddCommand(createReadReplicaCmd) - createReadReplicaCmd.Flags().StringVarP(&clusterName, "cluster-name", "c", "", "[REQUIRED] The name of the cluster.") - createReadReplicaCmd.MarkFlagRequired("cluster-name") createReadReplicaCmd.Flags().StringArrayVarP(&allReplicaOpt, "replica", "r", []string{}, `[OPTIONAL] Region information for the cluster. Please provide key value pairs num-cores=,disk-size-gb=,code=,region=,num-nodes=,vpc=,num-replicas=,multi-zone=.`) ReadReplicaCmd.AddCommand(updateReadReplicaCmd) - updateReadReplicaCmd.Flags().StringVarP(&clusterName, "cluster-name", "c", "", "[REQUIRED] The name of the cluster.") - updateReadReplicaCmd.MarkFlagRequired("cluster-name") updateReadReplicaCmd.Flags().StringArrayVarP(&allReplicaOpt, "replica", "r", []string{}, `[OPTIONAL] Region information for the cluster. Please provide key value pairs num-cores=,disk-size-gb=,code=,region=,num-nodes=,vpc=,num-replicas=,multi-zone=.`) ReadReplicaCmd.AddCommand(deleteReadReplicaCmd) - deleteReadReplicaCmd.Flags().StringVarP(&clusterName, "cluster-name", "c", "", "[REQUIRED] The name of the cluster.") - deleteReadReplicaCmd.MarkFlagRequired("cluster-name") - } diff --git a/cmd/cluster/cloud_regions.go b/cmd/region/cloud_regions.go similarity index 74% rename from cmd/cluster/cloud_regions.go rename to cmd/region/cloud_regions.go index 9b42c2c6..85dc148b 100644 --- a/cmd/cluster/cloud_regions.go +++ b/cmd/region/cloud_regions.go @@ -13,7 +13,7 @@ // specific language governing permissions and limitations // under the License. -package cluster +package region import ( "os" @@ -25,10 +25,19 @@ import ( "github.com/yugabyte/ybm-cli/internal/formatter" ) -var getCloudRegionsCmd = &cobra.Command{ - Use: "describe-regions", - Short: "Describe Cloud Regions", - Long: `Describe Cloud Regions`, +var CloudRegionsCmd = &cobra.Command{ + Use: "region", + Short: "Manage cloud regions", + Long: "Manage cloud regions for your YBM clusters", + Run: func(cmd *cobra.Command, args []string) { + cmd.Help() + }, +} + +var listCloudRegionsCmd = &cobra.Command{ + Use: "list", + Short: "List Cloud Provider Regions", + Long: `List Cloud Provider Regions`, Run: func(cmd *cobra.Command, args []string) { authApi, err := ybmAuthClient.NewAuthApiClient() if err != nil { @@ -55,8 +64,8 @@ var getCloudRegionsCmd = &cobra.Command{ } func init() { - ClusterCmd.AddCommand(getCloudRegionsCmd) - getCloudRegionsCmd.Flags().String("cloud-provider", "", "[REQUIRED] The cloud provider for which the regions have to be fetched. AWS or GCP.") - getCloudRegionsCmd.MarkFlagRequired("cloud-provider") + CloudRegionsCmd.AddCommand(listCloudRegionsCmd) + listCloudRegionsCmd.Flags().String("cloud-provider", "", "[REQUIRED] The cloud provider for which the regions have to be fetched. AWS or GCP.") + listCloudRegionsCmd.MarkFlagRequired("cloud-provider") } diff --git a/cmd/cluster/instance_types.go b/cmd/region/instance_types.go similarity index 82% rename from cmd/cluster/instance_types.go rename to cmd/region/instance_types.go index 6da8d226..874c105a 100644 --- a/cmd/cluster/instance_types.go +++ b/cmd/region/instance_types.go @@ -13,7 +13,7 @@ // specific language governing permissions and limitations // under the License. -package cluster +package region import ( "os" @@ -26,10 +26,19 @@ import ( "github.com/yugabyte/ybm-cli/internal/formatter" ) +var InstanceCmd = &cobra.Command{ + Use: "instance", + Short: "Manage instance types", + Long: "Manage instance types for your YBM clusters", + Run: func(cmd *cobra.Command, args []string) { + cmd.Help() + }, +} + var getInstanceTypesCmd = &cobra.Command{ - Use: "describe-instances", - Short: "Get Instance Types", - Long: `Get Instance Types`, + Use: "list", + Short: "List the Instance Types for a region", + Long: `List the Instance Types for a region`, Run: func(cmd *cobra.Command, args []string) { authApi, err := ybmAuthClient.NewAuthApiClient() if err != nil { @@ -63,13 +72,15 @@ var getInstanceTypesCmd = &cobra.Command{ } func init() { - ClusterCmd.AddCommand(getInstanceTypesCmd) + CloudRegionsCmd.AddCommand(InstanceCmd) + InstanceCmd.AddCommand(getInstanceTypesCmd) + getInstanceTypesCmd.Flags().SortFlags = false getInstanceTypesCmd.Flags().String("cloud-provider", "", "[REQUIRED] The cloud provider for which the regions have to be fetched. AWS or GCP.") getInstanceTypesCmd.MarkFlagRequired("cloud-provider") getInstanceTypesCmd.Flags().String("region", "", "[REQUIRED] The region in the cloud provider for which the instance types have to fetched.") getInstanceTypesCmd.MarkFlagRequired("region") getInstanceTypesCmd.Flags().String("tier", "Dedicated", "[OPTIONAL] Tier. Sandbox or Dedicated.") - getInstanceTypesCmd.Flags().Bool("show-disabled", false, "[OPTIONAL] Whether to show disabled instance types. true or false.") + getInstanceTypesCmd.Flags().Bool("show-disabled", true, "[OPTIONAL] Whether to show disabled instance types. true or false.") } diff --git a/cmd/root.go b/cmd/root.go index 62624ca1..01201e9e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -25,11 +25,10 @@ import ( "github.com/yugabyte/ybm-cli/cmd/cdc" "github.com/yugabyte/ybm-cli/cmd/cluster" "github.com/yugabyte/ybm-cli/cmd/nal" - "github.com/yugabyte/ybm-cli/cmd/readreplica" + "github.com/yugabyte/ybm-cli/cmd/region" "github.com/yugabyte/ybm-cli/cmd/signup" "github.com/yugabyte/ybm-cli/cmd/util" "github.com/yugabyte/ybm-cli/cmd/vpc" - "github.com/yugabyte/ybm-cli/cmd/vpcpeering" "github.com/yugabyte/ybm-cli/internal/log" ) @@ -98,11 +97,10 @@ func init() { rootCmd.AddCommand(cluster.ClusterCmd) rootCmd.AddCommand(backup.BackupCmd) rootCmd.AddCommand(nal.NalCmd) - rootCmd.AddCommand(readreplica.ReadReplicaCmd) rootCmd.AddCommand(vpc.VPCCmd) - rootCmd.AddCommand(vpcpeering.VPCPeeringCmd) - rootCmd.AddCommand(configureCmd) + rootCmd.AddCommand(authCmd) rootCmd.AddCommand(signup.SignUpCmd) + rootCmd.AddCommand(region.CloudRegionsCmd) util.AddCommandIfFeatureFlag(rootCmd, cdc.CdcCmd, util.CDC) } diff --git a/cmd/util/util.go b/cmd/util/util.go index d60ac568..152dd0b9 100644 --- a/cmd/util/util.go +++ b/cmd/util/util.go @@ -87,3 +87,15 @@ func IsJwtTokenExpiredWithTime(tokenStr string, now time.Time) (bool, error) { func IsJwtTokenExpired(tokenStr string) (bool, error) { return IsJwtTokenExpiredWithTime(tokenStr, time.Now()) } + +// Inspired from here: +// https://stackoverflow.com/questions/37562873/most-idiomatic-way-to-select-elements-from-an-array-in-golang +// This allows us to filter a slice of any type using a function that returns a bool +func Filter[T any](ss []T, test func(T) bool) (ret []T) { + for _, s := range ss { + if test(s) { + ret = append(ret, s) + } + } + return +} diff --git a/cmd/vpcpeering/vpc_peering.go b/cmd/vpc/peering/vpc_peering.go similarity index 97% rename from cmd/vpcpeering/vpc_peering.go rename to cmd/vpc/peering/vpc_peering.go index 758b567b..70d4e9fc 100644 --- a/cmd/vpcpeering/vpc_peering.go +++ b/cmd/vpc/peering/vpc_peering.go @@ -13,7 +13,7 @@ // specific language governing permissions and limitations // under the License. -package vpcpeering +package peering import ( "errors" @@ -30,7 +30,7 @@ import ( ) var VPCPeeringCmd = &cobra.Command{ - Use: "vpc-peering", + Use: "peering", Short: "Manage VPC Peerings", Long: "Manage VPC Peerings", Run: func(cmd *cobra.Command, args []string) { @@ -49,8 +49,8 @@ func findVpcPeering(vpcPeerings []ybmclient.VpcPeeringData, name string) (ybmcli var getVpcPeeringCmd = &cobra.Command{ Use: "get", - Short: "Get VPC peering in YugabyteDB Managed", - Long: "Get VPC peering in YugabyteDB Managed", + Short: "Get VPC peerings", + Long: "Get VPC peerings in YugabyteDB Managed", Run: func(cmd *cobra.Command, args []string) { listVpcPeeringCmd.Run(cmd, args) logrus.Warnln("\nThe command `ybm vpc-peering get` is deprecated. Please use `ybm vpc-peering list` instead.") @@ -59,7 +59,7 @@ var getVpcPeeringCmd = &cobra.Command{ var listVpcPeeringCmd = &cobra.Command{ Use: "list", - Short: "List VPC peerings in YugabyteDB Managed", + Short: "List VPC peerings", Long: "List VPC peerings in YugabyteDB Managed", Run: func(cmd *cobra.Command, args []string) { authApi, err := ybmAuthClient.NewAuthApiClient() @@ -96,7 +96,7 @@ var listVpcPeeringCmd = &cobra.Command{ var createVpcPeeringCmd = &cobra.Command{ Use: "create", - Short: "Create VPC peering in YugabyteDB Managed", + Short: "Create VPC peering", Long: "Create VPC peering in YugabyteDB Managed", Run: func(cmd *cobra.Command, args []string) { vpcPeeringName, _ := cmd.Flags().GetString("name") @@ -225,7 +225,7 @@ var createVpcPeeringCmd = &cobra.Command{ var deleteVpcPeeringCmd = &cobra.Command{ Use: "delete", - Short: "Delete VPC peering in YugabyteDB Managed", + Short: "Delete VPC peering", Long: "Delete VPC peering in YugabyteDB Managed", Run: func(cmd *cobra.Command, args []string) { vpcPeeringName, _ := cmd.Flags().GetString("name") diff --git a/cmd/vpc/vpc.go b/cmd/vpc/vpc.go index 590cd5be..b9b7a80f 100644 --- a/cmd/vpc/vpc.go +++ b/cmd/vpc/vpc.go @@ -23,6 +23,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/yugabyte/ybm-cli/cmd/util" + "github.com/yugabyte/ybm-cli/cmd/vpc/peering" ybmAuthClient "github.com/yugabyte/ybm-cli/internal/client" "github.com/yugabyte/ybm-cli/internal/formatter" ybmclient "github.com/yugabyte/yugabytedb-managed-go-client-internal" @@ -220,6 +221,8 @@ var deleteVpcCmd = &cobra.Command{ } func init() { + VPCCmd.AddCommand(peering.VPCPeeringCmd) + VPCCmd.AddCommand(getVpcCmd) getVpcCmd.Flags().String("name", "", "[OPTIONAL] Name for the VPC.") diff --git a/go.mod b/go.mod index ff4384a2..9aab35af 100644 --- a/go.mod +++ b/go.mod @@ -11,8 +11,8 @@ require ( github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf github.com/jayco/go-emoji-flag v0.0.0-20190810054606-01604da018da github.com/mattn/go-runewidth v0.0.14 - github.com/onsi/ginkgo/v2 v2.9.0 - github.com/onsi/gomega v1.27.3 + github.com/onsi/ginkgo/v2 v2.9.1 + github.com/onsi/gomega v1.27.4 github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.0 @@ -21,7 +21,7 @@ require ( github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816 github.com/yugabyte/yugabytedb-managed-go-client-internal v0.0.0-20230128004341-7bd09f253ed8 golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 - golang.org/x/term v0.5.0 + golang.org/x/term v0.6.0 gotest.tools/v3 v3.4.0 ) @@ -54,11 +54,11 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.4.2 // indirect golang.org/x/crypto v0.6.0 // indirect - golang.org/x/net v0.7.0 // indirect + golang.org/x/net v0.8.0 // indirect golang.org/x/oauth2 v0.5.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect - golang.org/x/tools v0.6.0 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/text v0.8.0 // indirect + golang.org/x/tools v0.7.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index b115248d..029303c8 100644 --- a/go.sum +++ b/go.sum @@ -198,10 +198,14 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/onsi/ginkgo/v2 v2.9.0 h1:Tugw2BKlNHTMfG+CheOITkYvk4LAh6MFOvikhGVnhE8= github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk= +github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= +github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo= github.com/onsi/gomega v1.27.2 h1:SKU0CXeKE/WVgIV1T61kSa3+IRE8Ekrv9rdXDwwTqnY= github.com/onsi/gomega v1.27.2/go.mod h1:5mR3phAHpkAVIDkHEUBY6HGVsU+cpcEscrGPB4oPlZI= github.com/onsi/gomega v1.27.3 h1:5VwIwnBY3vbBDOJrNtA4rVdiTZCsq9B5F12pvy1Drmk= github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557cZ6Gw= +github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= +github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= @@ -336,6 +340,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -366,6 +372,8 @@ golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -420,6 +428,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/client/client.go b/internal/client/client.go index a4a1f85e..fa18546a 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -352,6 +352,17 @@ func (a *AuthApiClient) GetClusterCloudById(clusterId string) (ybmclient.CloudEn return clusterCloud, nil } +func (a *AuthApiClient) GetConnectionCertificate() (string, error) { + certResp, resp, err := a.ApiClient.ClusterApi.GetConnectionCertificate(a.ctx).Execute() + if err != nil { + b, _ := httputil.DumpResponse(resp, true) + logrus.Debug(string(b)) + return "", err + } + certData := certResp.GetData() + return certData, nil +} + func (a *AuthApiClient) EditCluster(clusterId string) ybmclient.ApiEditClusterRequest { return a.ApiClient.ClusterApi.EditCluster(a.ctx, a.AccountID, a.ProjectID, clusterId) } diff --git a/internal/formatter/endpoints.go b/internal/formatter/endpoints.go new file mode 100644 index 00000000..aa248872 --- /dev/null +++ b/internal/formatter/endpoints.go @@ -0,0 +1,88 @@ +// Licensed to Yugabyte, Inc. under one or more contributor license +// agreements. See the NOTICE file distributed with this work for +// additional information regarding copyright ownership. Yugabyte +// licenses this file to you under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package formatter + +import ( + "encoding/json" + + ybmclient "github.com/yugabyte/yugabytedb-managed-go-client-internal" +) + +const ( + defaultEndpointListing = "table {{.Region}}\t{{.Accessibility}}\t{{.State}}\t{{.Host}}" + accessibilityHeader = "Accessibility" + hostHeader = "Host" +) + +type EndpointContext struct { + HeaderContext + Context + e ybmclient.Endpoint +} + +func NewEndpointFormat(source string) Format { + switch source { + case "table", "": + format := defaultEndpointListing + return Format(format) + default: // custom format or json or pretty + return Format(source) + } +} + +func EndpointWrite(ctx Context, endpoints []ybmclient.Endpoint) error { + render := func(format func(subContext SubContext) error) error { + for _, endpoint := range endpoints { + err := format(&EndpointContext{e: endpoint}) + if err != nil { + return err + } + } + return nil + } + return ctx.Write(NewEndpointContext(), render) +} + +func NewEndpointContext() *EndpointContext { + epCtx := EndpointContext{} + epCtx.Header = SubHeaderContext{ + "Region": regionHeader, + "Accessibility": accessibilityHeader, + "State": stateHeader, + "Host": hostHeader, + } + return &epCtx +} + +func (e *EndpointContext) Region() string { + return e.e.GetRegion() +} + +func (e *EndpointContext) Accessibility() string { + return string(e.e.GetAccessibilityType()) +} + +func (e *EndpointContext) State() string { + return string(e.e.GetState()) +} + +func (e *EndpointContext) Host() string { + return e.e.GetHost() +} + +func (e *EndpointContext) MarshalJSON() ([]byte, error) { + return json.Marshal(e.e) +}