From e37a05a043202f57021e44d3656accd12bfd2221 Mon Sep 17 00:00:00 2001 From: John Mark Gabriel Caguicla Date: Sat, 30 Jul 2022 14:17:26 +0800 Subject: [PATCH] `servers modify` bugfix spec flags were implicitly being filled with default values and could cause unexpected changes in the server configuration still up for review if the options should just be positional args since they're required anyway (you can't cherry-pick which specs you want to change) --- README.md | 22 +++++++-- api/client.go | 35 ++++++++------ commands/servers.go | 115 ++++++++++++++++++++++++++++---------------- 3 files changed, 112 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index dfaeea1..1dee6ff 100644 --- a/README.md +++ b/README.md @@ -60,10 +60,11 @@ tensordock-cli servers manage server_id ```sh tensordock-cli servers deploy \ - [--gpuModel gpu_model \] [--location location \] [--instanceType instance_type \] + [--gpuModel gpu_model \] [--gpuCount gpu_count \] + [--cpuModel cpu_model \] [--vcpus vcpus \] [--storage storage \] [--storageClass storage_class \] @@ -74,6 +75,8 @@ tensordock-cli servers deploy \ admin_pass ``` +**Tip**: try `tensordock-cli stock list [--type cpu]` to find out available values for `gpu_model`, `location` and `cpu_model` + #### Deploy a GPU Server ```sh @@ -86,16 +89,29 @@ tensordock-cli servers deploy server_name admin_user admin_pass --gpuCount 2 --g tensordock-cli servers deploy server_name admin_user admin_pass --instanceType cpu --cpuModel Intel_Xeon_V4 ``` +#### Modify a server + +```sh +tensordock-cli servers modify server_id \ + --instanceType instance_type \ + --gpuModel gpu_mdoel \ + --gpuCount gpu_count \ + --cpuModel cpu_model \ + --storage storage \ + --vcpus vcpus \ + --ram ram +``` + #### Convert a server to a CPU instance ```sh -tensordock-cli servers modify server_id --instanceType cpu --cpuModel Intel_Xeon_V4 +tensordock-cli servers modify server_id --instanceType cpu --cpuModel Intel_Xeon_V4 --storage 20 --vcpus 2 --ram 4 ``` #### Convert a server to a GPU instance ```sh -tensordock-cli servers modify server_id --instanceType gpu --gpuModel Quadro_4000 --gpuCount 2 +tensordock-cli servers modify server_id --instanceType gpu --gpuModel Quadro_4000 --gpuCount 2 --storage 20 --vcpus 2 --ram 4 ``` ### Get billing info diff --git a/api/client.go b/api/client.go index 6bc2de7..8c79eaa 100644 --- a/api/client.go +++ b/api/client.go @@ -53,14 +53,14 @@ type DeployServerRequest struct { } type ModifyServerRequest struct { - ServerId string `mapstructure:"server_id"` - InstanceType string `mapstructure:"instance_type"` - GPUModel string `mapstructure:"gpu_model,omitempty"` - GPUCount int `mapstructure:"gpu_count,omitempty"` - CPUModel string `mapstructure:"cpu_model,omitempty"` - VCPUs int `mapstructure:"vcpus"` - RAM int `mapstructure:"ram"` - Storage int `mapstructure:"storage"` + ServerId string `mapstructure:"server_id"` + InstanceType *string `mapstructure:"instance_type"` + GPUModel *string `mapstructure:"gpu_model,omitempty"` + GPUCount *int `mapstructure:"gpu_count,omitempty"` + CPUModel *string `mapstructure:"cpu_model,omitempty"` + VCPUs *int `mapstructure:"vcpus"` + RAM *int `mapstructure:"ram"` + Storage *int `mapstructure:"storage"` } type DeployServerResponse struct { @@ -87,10 +87,6 @@ type ListCpuStockResponse struct { } `json:"stock"` } -type ModifyServerResponse struct { - Response -} - type Client struct { BaseUrl string ApiKey string @@ -368,17 +364,24 @@ func (client *Client) ListCpuStock() (*ListCpuStockResponse, error) { return &res, nil } -func (client *Client) ModifyServer(req ModifyServerRequest) (*ModifyServerResponse, error) { +func (client *Client) ModifyServer(req ModifyServerRequest) (*Response, error) { var rawBody map[string]interface{} err := mapstructure.Decode(req, &rawBody) if err != nil { return nil, err } + // conver to map[string]string skipping nil pointers body := map[string]string{} for key, elem := range rawBody { - str := fmt.Sprintf("%v", elem) - body[key] = str + val := reflect.ValueOf(elem) + if val.Kind() == reflect.Ptr { + if val.IsNil() { + continue + } + val = val.Elem() + } + body[key] = fmt.Sprintf("%v", val) } raw, err := client.post("modify/single/custom", body, true) @@ -386,7 +389,7 @@ func (client *Client) ModifyServer(req ModifyServerRequest) (*ModifyServerRespon return nil, err } - var res ModifyServerResponse + var res Response if err := json.Unmarshal(*raw, &res); err != nil { return nil, err } diff --git a/commands/servers.go b/commands/servers.go index 1b21d04..778606e 100644 --- a/commands/servers.go +++ b/commands/servers.go @@ -64,10 +64,11 @@ var ( RunE: manageServer, } restartCmd = &cobra.Command{ - Use: "restart [flags] server_id", - Short: "Restart a server", - Args: cobra.ExactArgs(1), - RunE: restartServer, + Use: "restart [flags] server_id", + Short: "Restart a server", + Args: cobra.ExactArgs(1), + RunE: restartServer, + PostRun: logAction("success"), } modifyCmd = &cobra.Command{ Use: "modify [flags] server_id", @@ -106,8 +107,8 @@ func init() { serversCmd.AddCommand(restartCmd) serversCmd.AddCommand(modifyCmd) - modifyCmd.Flags().String("gpuModel", "Quadro_4000", "The GPU model that you would like to provision") modifyCmd.Flags().String("instanceType", "gpu", "Either \"gpu\" or \"cpu\"") + modifyCmd.Flags().String("gpuModel", "Quadro_4000", "The GPU model that you would like to provision") modifyCmd.Flags().Int("gpuCount", 1, "The number of GPUs of the model you specified earlier") modifyCmd.Flags().String("cpuModel", "Intel_Xeon_v4", "The CPU model that you would like to provision") modifyCmd.Flags().Int("vcpus", 2, "Number of vCPUs that you would like") @@ -360,41 +361,73 @@ func modifyServer(cmd *cobra.Command, args []string) error { serverId := args[0] - instanceType, err := flags.GetString("instanceType") - if err != nil { - return err - } - - gpuModel, err := flags.GetString("gpuModel") - if err != nil { - return err - } - - gpuCount, err := flags.GetInt("gpuCount") - if err != nil { - return err - } - - cpuModel, err := flags.GetString("cpuModel") - if err != nil { - return err - } - - vcpus, err := flags.GetInt("vcpus") - if err != nil { - return err - } - - ram, err := flags.GetInt("ram") - if err != nil { - return err - } - - storage, err := flags.GetInt("storage") - if err != nil { - return err - } - + var instanceType *string = nil + if flags.Changed("instanceType") { + instanceTypeVal, err := flags.GetString("instanceType") + if err != nil { + return err + } + instanceType = &instanceTypeVal + } + + var gpuModel *string = nil + if flags.Changed("gpuModel") { + gpuModelVal, err := flags.GetString("gpuModel") + if err != nil { + return err + } + gpuModel = &gpuModelVal + } + + var gpuCount *int = nil + if flags.Changed("gpuCount") { + gpuCountVal, err := flags.GetInt("gpuCount") + if err != nil { + return err + } + gpuCount = &gpuCountVal + } + + var cpuModel *string = nil + if flags.Changed("cpuModel") { + cpuModelVal, err := flags.GetString("cpuModel") + if err != nil { + return err + } + cpuModel = &cpuModelVal + } + + var vcpus *int = nil + if flags.Changed("vcpus") { + vcpusVal, err := flags.GetInt("vcpus") + if err != nil { + return err + } + vcpus = &vcpusVal + } + + var ram *int = nil + if flags.Changed("ram") { + ramVal, err := flags.GetInt("ram") + if err != nil { + return err + } + ram = &ramVal + } + + var storage *int = nil + if flags.Changed("storage") { + storageVal, err := flags.GetInt("storage") + if err != nil { + return err + } + storage = &storageVal + } + + // based on tests, it seems that the endpoint does not + // support specifying only parts of the spec to modify + // (e.g. adjust VCPUs only) and that you need to specify + // the entirety of the server configuration on every call req := api.ModifyServerRequest{ ServerId: serverId, InstanceType: instanceType, @@ -403,7 +436,7 @@ func modifyServer(cmd *cobra.Command, args []string) error { Storage: storage, } - switch instanceType { + switch *instanceType { case "cpu": req.CPUModel = cpuModel case "gpu":