Skip to content

Commit b84e27c

Browse files
Support lookup by name or ID for all VM, cluster, and network commands (#543)
* Add support for lookup by name or ID for all VM, cluster, and network commands Co-Authored-By: [email protected] <[email protected]> * Fix PR label check by adding required labels Co-Authored-By: [email protected] <[email protected]> * Deprecate --name flags in rm commands since name lookup is now supported in arguments Co-Authored-By: [email protected] <[email protected]> * Update vm_update and vm_update_ttl commands to support lookup by name Co-Authored-By: [email protected] <[email protected]> * Fix handling of multiple resources with the same name in rm commands Co-Authored-By: [email protected] <[email protected]> * Update help text to clarify behavior when removing multiple resources with the same name Co-Authored-By: [email protected] <[email protected]> * Add ID/name completion for additional commands and fix VM completion function - Added ID/name lookup to cluster_kubeconfig, cluster_shell, cluster_update, cluster_upgrade, and cluster_addon_create_objectstore commands - Fixed VM completion function to include resources outside of 'running' state - Updated help text to clarify behavior with multiple resources with the same name - Ensured consistent implementation across all commands Co-Authored-By: [email protected] <[email protected]> * Address PR comments: add deprecation warnings to --name flags and update error message format Co-Authored-By: [email protected] <[email protected]> * Address PR comments: add deprecation warnings to --id flags Co-Authored-By: [email protected] <[email protected]> * Update help text to use CLUSTER_ID_OR_NAME consistently across all commands Co-Authored-By: [email protected] <[email protected]> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: [email protected] <[email protected]>
1 parent 4e2766d commit b84e27c

32 files changed

+421
-166
lines changed

cli/cmd/cluster.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cmd
22

33
import (
4+
"fmt"
45
"github.com/pkg/errors"
56
"github.com/replicatedhq/replicated/pkg/credentials"
67
"github.com/replicatedhq/replicated/pkg/kotsclient"
@@ -143,3 +144,61 @@ func (r *runners) completeClusterInstanceTypes(cmd *cobra.Command, args []string
143144
}
144145
return instanceTypes, cobra.ShellCompDirectiveNoFileComp
145146
}
147+
148+
func (r *runners) completeClusterIDsAndNames(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
149+
err := r.initClusterClient()
150+
if err != nil {
151+
return nil, cobra.ShellCompDirectiveNoFileComp
152+
}
153+
154+
var completions []string
155+
clusters, err := r.kotsAPI.ListClusters(false, nil, nil)
156+
if err != nil {
157+
return nil, cobra.ShellCompDirectiveNoFileComp
158+
}
159+
160+
for _, cluster := range clusters {
161+
completions = append(completions, cluster.ID)
162+
if cluster.Name != "" {
163+
completions = append(completions, cluster.Name)
164+
}
165+
}
166+
return completions, cobra.ShellCompDirectiveNoFileComp
167+
}
168+
169+
func (r *runners) getClusterIDFromArg(arg string) (string, error) {
170+
_, err := r.kotsAPI.GetCluster(arg)
171+
if err == nil {
172+
return arg, nil
173+
}
174+
175+
cause := errors.Cause(err)
176+
if cause != platformclient.ErrNotFound && cause != platformclient.ErrForbidden {
177+
return "", errors.Wrap(err, "get cluster")
178+
}
179+
180+
clusters, err := r.kotsAPI.ListClusters(false, nil, nil)
181+
if errors.Cause(err) == platformclient.ErrForbidden {
182+
return "", ErrCompatibilityMatrixTermsNotAccepted
183+
} else if err != nil {
184+
return "", errors.Wrap(err, "list clusters")
185+
}
186+
187+
var matchingClusters []string
188+
for _, cluster := range clusters {
189+
if cluster.Name == arg {
190+
matchingClusters = append(matchingClusters, cluster.ID)
191+
}
192+
}
193+
194+
switch len(matchingClusters) {
195+
case 0:
196+
return "", errors.Errorf("Cluster with name or ID '%s' not found", arg)
197+
case 1:
198+
return matchingClusters[0], nil
199+
default:
200+
return "", errors.Errorf("Multiple clusters found with name '%s'. Please use the cluster ID instead. Matching clusters: %s. To view all cluster IDs run `replicated cluster ls`",
201+
arg,
202+
fmt.Sprintf("%s (and %d more)", matchingClusters[0], len(matchingClusters)-1))
203+
}
204+
}

cli/cmd/cluster_addon.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,16 @@ func (r *runners) InitClusterAddon(parent *cobra.Command) *cobra.Command {
1717
1818
You can use various subcommands to create, list, remove, or check the status of add-ons on a cluster. This command is useful for adding databases, object storage, monitoring, security, or other specialized tools to your cluster environment.`,
1919
Example: `# List all add-ons installed on a cluster
20-
replicated cluster addon ls CLUSTER_ID
20+
replicated cluster addon ls CLUSTER_ID_OR_NAME
2121
2222
# Remove an add-on from a cluster
23-
replicated cluster addon rm CLUSTER_ID --id ADDON_ID
23+
replicated cluster addon rm CLUSTER_ID_OR_NAME --id ADDON_ID
2424
2525
# Create an object store bucket add-on for a cluster
26-
replicated cluster addon create object-store CLUSTER_ID --bucket-prefix mybucket
26+
replicated cluster addon create object-store CLUSTER_ID_OR_NAME --bucket-prefix mybucket
2727
2828
# List add-ons with JSON output
29-
replicated cluster addon ls CLUSTER_ID --output json`,
29+
replicated cluster addon ls CLUSTER_ID_OR_NAME --output json`,
3030
}
3131
parent.AddCommand(cmd)
3232

cli/cmd/cluster_addon_create.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ func (r *runners) InitClusterAddonCreate(parent *cobra.Command) *cobra.Command {
1010
Short: "Create cluster add-ons.",
1111
Long: `Create new add-ons for a cluster. This command allows you to add functionality or services to a cluster by provisioning the required add-ons.`,
1212
Example: `# Create an object store bucket add-on for a cluster
13-
replicated cluster addon create object-store CLUSTER_ID --bucket-prefix mybucket
13+
replicated cluster addon create object-store CLUSTER_ID_OR_NAME --bucket-prefix mybucket
1414
1515
# Perform a dry run for creating an object store add-on
16-
replicated cluster addon create object-store CLUSTER_ID --bucket-prefix mybucket --dry-run`,
16+
replicated cluster addon create object-store CLUSTER_ID_OR_NAME --bucket-prefix mybucket --dry-run`,
1717
}
1818
parent.AddCommand(cmd)
1919

cli/cmd/cluster_addon_create_objectstore.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,28 +15,33 @@ import (
1515

1616
func (r *runners) InitClusterAddonCreateObjectStore(parent *cobra.Command) *cobra.Command {
1717
cmd := &cobra.Command{
18-
Use: "object-store CLUSTER_ID --bucket-prefix BUCKET_PREFIX",
18+
Use: "object-store CLUSTER_ID_OR_NAME --bucket-prefix BUCKET_PREFIX",
1919
Short: "Create an object store bucket for a cluster.",
2020
Long: `Creates an object store bucket for a cluster, requiring a bucket name prefix. The bucket name will be auto-generated using the format "[BUCKET_PREFIX]-[ADDON_ID]-cmx". This feature provisions an object storage bucket that can be used for storage in your cluster environment.`,
2121
Example: `# Create an object store bucket with a specified prefix
22-
replicated cluster addon create object-store 05929b24 --bucket-prefix mybucket
22+
replicated cluster addon create object-store CLUSTER_ID_OR_NAME --bucket-prefix mybucket
2323
2424
# Create an object store bucket and wait for it to be ready (up to 5 minutes)
25-
replicated cluster addon create object-store 05929b24 --bucket-prefix mybucket --wait 5m
25+
replicated cluster addon create object-store CLUSTER_ID_OR_NAME --bucket-prefix mybucket --wait 5m
2626
2727
# Perform a dry run to validate inputs without creating the bucket
28-
replicated cluster addon create object-store 05929b24 --bucket-prefix mybucket --dry-run
28+
replicated cluster addon create object-store CLUSTER_ID_OR_NAME --bucket-prefix mybucket --dry-run
2929
3030
# Create an object store bucket and output the result in JSON format
31-
replicated cluster addon create object-store 05929b24 --bucket-prefix mybucket --output json
31+
replicated cluster addon create object-store CLUSTER_ID_OR_NAME --bucket-prefix mybucket --output json
3232
3333
# Create an object store bucket with a custom prefix and wait for 10 minutes
34-
replicated cluster addon create object-store 05929b24 --bucket-prefix custom-prefix --wait 10m`,
34+
replicated cluster addon create object-store CLUSTER_ID_OR_NAME --bucket-prefix custom-prefix --wait 10m`,
3535
Args: cobra.ExactArgs(1),
3636
RunE: func(_ *cobra.Command, cmdArgs []string) error {
37-
r.args.clusterAddonCreateObjectStoreClusterID = cmdArgs[0]
37+
clusterID, err := r.getClusterIDFromArg(cmdArgs[0])
38+
if err != nil {
39+
return errors.Wrap(err, "get cluster id from arg")
40+
}
41+
r.args.clusterAddonCreateObjectStoreClusterID = clusterID
3842
return r.clusterAddonCreateObjectStoreCreateRun()
3943
},
44+
ValidArgsFunction: r.completeClusterIDsAndNames,
4045
}
4146
parent.AddCommand(cmd)
4247

cli/cmd/cluster_addon_ls.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,20 @@ func (r *runners) InitClusterAddonLs(parent *cobra.Command) *cobra.Command {
1414
args := clusterAddonLsArgs{}
1515

1616
cmd := &cobra.Command{
17-
Use: "ls CLUSTER_ID",
17+
Use: "ls CLUSTER_ID_OR_NAME",
1818
Aliases: []string{"list"},
1919
Short: "List cluster add-ons for a cluster.",
2020
Long: `The 'cluster addon ls' command allows you to list all add-ons for a specific cluster. This command provides a detailed overview of the add-ons currently installed on the cluster, including their status and any relevant configuration details.
2121
2222
This can be useful for monitoring the health and configuration of add-ons or performing troubleshooting tasks.`,
2323
Example: `# List add-ons for a cluster with default table output
24-
replicated cluster addon ls CLUSTER_ID
24+
replicated cluster addon ls CLUSTER_ID_OR_NAME
2525
2626
# List add-ons for a cluster with JSON output
27-
replicated cluster addon ls CLUSTER_ID --output json
27+
replicated cluster addon ls CLUSTER_ID_OR_NAME --output json
2828
2929
# List add-ons for a cluster with wide table output
30-
replicated cluster addon ls CLUSTER_ID --output wide`,
30+
replicated cluster addon ls CLUSTER_ID_OR_NAME --output wide`,
3131
Args: cobra.ExactArgs(1),
3232
RunE: func(_ *cobra.Command, cmdArgs []string) error {
3333
args.clusterID = cmdArgs[0]

cli/cmd/cluster_addon_rm.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ func (r *runners) InitClusterAddonRm(parent *cobra.Command) *cobra.Command {
1515
args := clusterAddonRmArgs{}
1616

1717
cmd := &cobra.Command{
18-
Use: "rm CLUSTER_ID --id ADDON_ID",
18+
Use: "rm CLUSTER_ID_OR_NAME --id ADDON_ID",
1919
Aliases: []string{"delete"},
2020
Short: "Remove cluster add-on by ID.",
21-
Long: `The 'cluster addon rm' command allows you to remove a specific add-on from a cluster by specifying the cluster ID and the add-on ID.
21+
Long: `The 'cluster addon rm' command allows you to remove a specific add-on from a cluster by specifying the cluster ID or name and the add-on ID.
2222
2323
This command is useful when you want to deprovision an add-on that is no longer needed or when troubleshooting issues related to specific add-ons. The add-on will be removed immediately, and you will receive confirmation upon successful removal.`,
2424
Example: `# Remove an add-on with ID 'abc123' from cluster 'cluster456'

cli/cmd/cluster_kubeconfig.go

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,37 +22,39 @@ const (
2222

2323
func (r *runners) InitClusterKubeconfig(parent *cobra.Command) *cobra.Command {
2424
cmd := &cobra.Command{
25-
Use: "kubeconfig [ID]",
25+
Use: "kubeconfig [ID_OR_NAME]",
2626
Short: "Download credentials for a test cluster.",
2727
Long: `The 'cluster kubeconfig' command downloads the credentials (kubeconfig) required to access a test cluster. You can either merge these credentials into your existing kubeconfig file or save them as a new file.
2828
29-
This command ensures that the kubeconfig is correctly configured for use with your Kubernetes tools. You can specify the cluster by ID or by name. Additionally, the kubeconfig can be written to a specific file path or printed to stdout.
29+
This command ensures that the kubeconfig is correctly configured for use with your Kubernetes tools. You can specify the cluster by ID or name directly as an argument, or by using the '--id' or '--name' flags. Additionally, the kubeconfig can be written to a specific file path or printed to stdout.
3030
3131
You can also use this command to automatically update your current Kubernetes context with the downloaded credentials.`,
3232
Example: `# Download and merge kubeconfig into your existing configuration
33-
replicated cluster kubeconfig CLUSTER_ID
33+
replicated cluster kubeconfig CLUSTER_ID_OR_NAME
3434
3535
# Save the kubeconfig to a specific file
36-
replicated cluster kubeconfig CLUSTER_ID --output-path ./kubeconfig
36+
replicated cluster kubeconfig CLUSTER_ID_OR_NAME --output-path ./kubeconfig
3737
3838
# Print the kubeconfig to stdout
39-
replicated cluster kubeconfig CLUSTER_ID --stdout
39+
replicated cluster kubeconfig CLUSTER_ID_OR_NAME --stdout
4040
41-
# Download kubeconfig for a cluster by name
41+
# Download kubeconfig for a cluster by name using a flag
4242
replicated cluster kubeconfig --name "My Cluster"
4343
44-
# Download kubeconfig for a cluster by ID
44+
# Download kubeconfig for a cluster by ID using a flag
4545
replicated cluster kubeconfig --id CLUSTER_ID`,
4646
RunE: r.kubeconfigCluster,
47-
ValidArgsFunction: r.completeClusterIDs,
47+
ValidArgsFunction: r.completeClusterIDsAndNames,
4848
}
4949
parent.AddCommand(cmd)
5050

5151
cmd.Flags().StringVar(&r.args.kubeconfigClusterName, "name", "", "name of the cluster to download credentials for (when id is not provided)")
5252
cmd.RegisterFlagCompletionFunc("name", r.completeClusterNames)
53+
cmd.Flag("name").Deprecated = "use ID_OR_NAME arguments instead"
5354

5455
cmd.Flags().StringVar(&r.args.kubeconfigClusterID, "id", "", "id of the cluster to download credentials for (when name is not provided)")
5556
cmd.RegisterFlagCompletionFunc("id", r.completeClusterIDs)
57+
cmd.Flag("id").Deprecated = "use ID_OR_NAME arguments instead"
5658

5759
cmd.Flags().StringVar(&r.args.kubeconfigPath, "output-path", "", "path to kubeconfig file to write to, if not provided, it will be merged into your existing kubeconfig")
5860
cmd.Flags().BoolVar(&r.args.kubeconfigStdout, "stdout", false, "write kubeconfig to stdout")
@@ -61,12 +63,15 @@ replicated cluster kubeconfig --id CLUSTER_ID`,
6163
}
6264

6365
func (r *runners) kubeconfigCluster(_ *cobra.Command, args []string) error {
64-
// by default, we look at args[0] as the id
65-
// but if it's not provided, we look for a viper flag named "name" and use it
66-
// as the name of the cluster, not the id
66+
// by default, we look at args[0] as the id or name
67+
// but if it's not provided, we look for a flag named "name" or "id"
6768
clusterID := ""
6869
if len(args) > 0 {
69-
clusterID = args[0]
70+
var err error
71+
clusterID, err = r.getClusterIDFromArg(args[0])
72+
if err != nil {
73+
return errors.Wrap(err, "get cluster id from arg")
74+
}
7075
} else if r.args.kubeconfigClusterName != "" {
7176
clusters, err := r.kotsAPI.ListClusters(false, nil, nil)
7277
if errors.Cause(err) == platformclient.ErrForbidden {

cli/cmd/cluster_nodegroup.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ func (r *runners) InitClusterNodeGroup(parent *cobra.Command) *cobra.Command {
1212
1313
Node groups define a set of nodes with specific configurations, such as instance types, node counts, or scaling rules. You can use subcommands to perform various actions on node groups.`,
1414
Example: `# List all node groups for a cluster
15-
replicated cluster nodegroup ls CLUSTER_ID`,
15+
replicated cluster nodegroup ls CLUSTER_ID_OR_NAME`,
1616
}
1717
parent.AddCommand(cmd)
1818

cli/cmd/cluster_nodegroup_ls.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,22 @@ import (
99

1010
func (r *runners) InitClusterNodeGroupList(parent *cobra.Command) *cobra.Command {
1111
cmd := &cobra.Command{
12-
Use: "ls [ID]",
12+
Use: "ls [ID_OR_NAME]",
1313
Aliases: []string{"list"},
1414
Short: "List node groups for a cluster.",
1515
Long: `The 'cluster nodegroup ls' command lists all the node groups associated with a given cluster. Each node group defines a specific set of nodes with particular configurations, such as instance types and scaling options.
1616
1717
You can view information about the node groups within the specified cluster, including their ID, name, node count, and other configuration details.
1818
19-
You must provide the cluster ID to list its node groups.`,
19+
You must provide the cluster ID or name to list its node groups.`,
2020
Example: `# List all node groups in a cluster with default table output
21-
replicated cluster nodegroup ls CLUSTER_ID
21+
replicated cluster nodegroup ls CLUSTER_ID_OR_NAME
2222
2323
# List node groups with JSON output
24-
replicated cluster nodegroup ls CLUSTER_ID --output json
24+
replicated cluster nodegroup ls CLUSTER_ID_OR_NAME --output json
2525
2626
# List node groups with wide table output
27-
replicated cluster nodegroup ls CLUSTER_ID --output wide`,
27+
replicated cluster nodegroup ls CLUSTER_ID_OR_NAME --output wide`,
2828
Args: cobra.ExactArgs(1),
2929
RunE: r.listNodeGroups,
3030
ValidArgsFunction: r.completeClusterIDs,

cli/cmd/cluster_port.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ func (r *runners) InitClusterPort(parent *cobra.Command) *cobra.Command {
1212
1313
This command provides flexibility for handling ports in various test clusters, ensuring efficient management of cluster networking settings.`,
1414
Example: `# List all exposed ports in a cluster
15-
replicated cluster port ls [CLUSTER_ID]
15+
replicated cluster port ls [CLUSTER_ID_OR_NAME]
1616
1717
# Remove an exposed port from a cluster
18-
replicated cluster port rm [CLUSTER_ID] [PORT]
18+
replicated cluster port rm [CLUSTER_ID_OR_NAME] [PORT]
1919
2020
# Expose a new port in a cluster
21-
replicated cluster port expose [CLUSTER_ID] [PORT]`,
21+
replicated cluster port expose [CLUSTER_ID_OR_NAME] [PORT]`,
2222
SilenceUsage: true,
2323
Hidden: false,
2424
}

0 commit comments

Comments
 (0)