diff --git a/README.md b/README.md index e680cedc80..c5c0ba5029 100644 --- a/README.md +++ b/README.md @@ -1128,7 +1128,7 @@ $ scw inspect myserver | jq '.[0].public_ip.address' #### Features * `scw info` now prints user/organization info from the API ([#142](https://github.com/scaleway/scaleway-cli/issues/130) -* +* Added helpers to manipulate new `user_data` API ([#150](https://github.com/scaleway/scaleway-cli/issues/150)) #### Fixes diff --git a/pkg/api/api.go b/pkg/api/api.go index d1838e306c..7083c085ec 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -12,6 +12,7 @@ import ( "crypto/tls" "encoding/json" "fmt" + "io/ioutil" "net/http" "net/url" "os" @@ -1322,6 +1323,98 @@ func (s *ScalewayAPI) GetBootscript(bootscriptID string) (*ScalewayBootscript, e return &oneBootscript.Bootscript, nil } +type ScalewayUserdatas struct { + UserData []string `json:"user_data"` +} + +// GetUserdatas gets list of userdata for a server +func (s *ScalewayAPI) GetUserdatas(serverID string) (*ScalewayUserdatas, error) { + resp, err := s.GetResponse("servers/" + serverID + "/user_data") + if err != nil { + return nil, err + } + defer resp.Body.Close() + var userdatas ScalewayUserdatas + decoder := json.NewDecoder(resp.Body) + err = decoder.Decode(&userdatas) + if err != nil { + return nil, err + } + return &userdatas, nil +} + +type ScalewayUserdata []byte + +func (s *ScalewayUserdata) String() string { + return string(*s) +} + +// GetUserdata gets a specific userdata for a server +func (s *ScalewayAPI) GetUserdata(serverID string, key string) (*ScalewayUserdata, error) { + var data ScalewayUserdata + var err error + resp, err := s.GetResponse("servers/" + serverID + "/user_data/" + key) + if err != nil { + return nil, err + } + if resp.StatusCode != 200 { + return nil, fmt.Errorf("no such user_data %q (%d)", key, resp.StatusCode) + } + + defer resp.Body.Close() + data, err = ioutil.ReadAll(resp.Body) + return &data, err +} + +// PatchUserdata sets a user data +func (s *ScalewayAPI) PatchUserdata(serverID string, key string, value []byte) error { + resource := fmt.Sprintf("servers/%s/user_data/%s", serverID, key) + uri := fmt.Sprintf("%s/%s", strings.TrimRight(s.APIUrl, "/"), resource) + payload := new(bytes.Buffer) + payload.Write(value) + + req, err := http.NewRequest("PATCH", uri, payload) + if err != nil { + return err + } + + req.Header.Set("X-Auth-Token", s.Token) + req.Header.Set("Content-Type", "text/plain") + + curl, err := http2curl.GetCurlCommand(req) + if os.Getenv("SCW_SENSITIVE") != "1" { + log.Debug(s.HideAPICredentials(curl.String())) + } else { + log.Debug(curl.String()) + } + + resp, err := s.client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode == 204 { + return nil + } + + return fmt.Errorf("cannot set user_data (%d)", resp.StatusCode) +} + +// DeleteUserdata deletes a server user_data +func (s *ScalewayAPI) DeleteUserdata(serverID string, key string) error { + resp, err := s.DeleteResponse(fmt.Sprintf("servers/%s/user_data/%s", serverID, key)) + if err != nil { + return err + } + + // Succeed POST code + if resp.StatusCode == 204 { + return nil + } + return fmt.Errorf("cannot delete user_data (%d)", resp.StatusCode) +} + // GetTasks get the list of tasks from the ScalewayAPI func (s *ScalewayAPI) GetTasks() (*[]ScalewayTask, error) { query := url.Values{} diff --git a/pkg/cli/x_userdata.go b/pkg/cli/x_userdata.go index 24bcc9c505..8abaa7dcf5 100644 --- a/pkg/cli/x_userdata.go +++ b/pkg/cli/x_userdata.go @@ -4,7 +4,10 @@ package cli -import "fmt" +import ( + "fmt" + "strings" +) var cmdUserdata = &Command{ Exec: runUserdata, @@ -35,7 +38,48 @@ func runUserdata(cmd *Command, args []string) error { return cmd.PrintShortUsage() } - fmt.Println("Not implemented") + ctx := cmd.GetContext(args) + serverID := ctx.API.GetServerID(args[0]) + + switch len(args) { + case 1: + // List userdata + res, err := ctx.API.GetUserdatas(serverID) + if err != nil { + return err + } + for _, key := range res.UserData { + fmt.Fprintln(ctx.Stdout, key) + } + default: + parts := strings.Split(args[1], "=") + key := parts[0] + switch len(parts) { + case 1: + // Get userdatas + res, err := ctx.API.GetUserdata(serverID, key) + if err != nil { + return err + } + fmt.Fprintf(ctx.Stdout, "%s\n", res.String()) + default: + value := parts[1] + if value != "" { + // Set userdata + err := ctx.API.PatchUserdata(serverID, key, []byte(value)) + if err != nil { + return err + } + fmt.Fprintln(ctx.Stdout, key) + } else { + // Delete userdata + err := ctx.API.DeleteUserdata(serverID, key) + if err != nil { + return err + } + } + } + } return nil }