util.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. // Copyright (c) 2016 VMware, Inc. All Rights Reserved.
  2. //
  3. // This product is licensed to you under the Apache License, Version 2.0 (the "License").
  4. // You may not use this product except in compliance with the License.
  5. //
  6. // This product may include a number of subcomponents with separate copyright notices and
  7. // license terms. Your use of these subcomponents is subject to the terms and conditions
  8. // of the subcomponent's license, as noted in the LICENSE file.
  9. package photon
  10. import (
  11. "bytes"
  12. "encoding/json"
  13. "fmt"
  14. "io/ioutil"
  15. "net/http"
  16. "net/url"
  17. "reflect"
  18. )
  19. // Reads an error out of the HTTP response, or does nothing if
  20. // no error occured.
  21. func getError(res *http.Response) (*http.Response, error) {
  22. // Do nothing if the response is a successful 2xx
  23. if res.StatusCode/100 == 2 {
  24. return res, nil
  25. }
  26. var apiError ApiError
  27. // ReadAll is usually a bad practice, but here we need to read the response all
  28. // at once because we may attempt to use the data twice. It's preferable to use
  29. // methods that take io.Reader, e.g. json.NewDecoder
  30. body, err := ioutil.ReadAll(res.Body)
  31. if err != nil {
  32. return nil, err
  33. }
  34. err = json.Unmarshal(body, &apiError)
  35. if err != nil {
  36. // If deserializing into ApiError fails, return a generic HttpError instead
  37. return nil, HttpError{res.StatusCode, string(body[:])}
  38. }
  39. apiError.HttpStatusCode = res.StatusCode
  40. return nil, apiError
  41. }
  42. // Reads a task object out of the HTTP response. Takes an error argument
  43. // so that GetTask can easily wrap GetError. This function will do nothing
  44. // if e is not nil.
  45. // e.g. res, err := getTask(getError(someApi.Get()))
  46. func getTask(res *http.Response, e error) (*Task, error) {
  47. if e != nil {
  48. return nil, e
  49. }
  50. var task Task
  51. err := json.NewDecoder(res.Body).Decode(&task)
  52. if err != nil {
  53. return nil, err
  54. }
  55. if task.State == "ERROR" {
  56. // Critical: return task as well, so that it can be examined
  57. // for error details.
  58. return &task, TaskError{task.ID, getFailedStep(&task)}
  59. }
  60. return &task, nil
  61. }
  62. // Converts an options struct into a query string.
  63. // E.g. type Foo struct {A int; B int} might return "?a=5&b=10".
  64. // Will return an empty string if no options are set.
  65. func getQueryString(options interface{}) string {
  66. buffer := bytes.Buffer{}
  67. buffer.WriteString("?")
  68. strct := reflect.ValueOf(options).Elem()
  69. typ := strct.Type()
  70. for i := 0; i < strct.NumField(); i++ {
  71. field := strct.Field(i)
  72. value := fmt.Sprint(field.Interface())
  73. if value != "" {
  74. buffer.WriteString(typ.Field(i).Tag.Get("urlParam") + "=" + url.QueryEscape(value))
  75. if i < strct.NumField()-1 {
  76. buffer.WriteString("&")
  77. }
  78. }
  79. }
  80. uri := buffer.String()
  81. if uri == "?" {
  82. return ""
  83. }
  84. return uri
  85. }
  86. // Sets security groups for a given entity (deployment/tenant/project)
  87. func setSecurityGroups(client *Client, entityUrl string, securityGroups *SecurityGroupsSpec) (task *Task, err error) {
  88. body, err := json.Marshal(securityGroups)
  89. if err != nil {
  90. return
  91. }
  92. url := entityUrl + "/set_security_groups"
  93. res, err := client.restClient.Post(
  94. url,
  95. "application/json",
  96. bytes.NewReader(body),
  97. client.options.TokenOptions)
  98. if err != nil {
  99. return
  100. }
  101. defer res.Body.Close()
  102. task, err = getTask(getError(res))
  103. return
  104. }