client.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  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. "crypto/tls"
  12. "crypto/x509"
  13. "io/ioutil"
  14. "log"
  15. "net/http"
  16. "strings"
  17. "time"
  18. )
  19. // Represents stateless context needed to call photon APIs.
  20. type Client struct {
  21. options ClientOptions
  22. restClient *restClient
  23. logger *log.Logger
  24. Endpoint string
  25. Status *StatusAPI
  26. Tenants *TenantsAPI
  27. Tasks *TasksAPI
  28. Projects *ProjectsAPI
  29. Flavors *FlavorsAPI
  30. Images *ImagesAPI
  31. Disks *DisksAPI
  32. VMs *VmAPI
  33. Hosts *HostsAPI
  34. Deployments *DeploymentsAPI
  35. ResourceTickets *ResourceTicketsAPI
  36. Networks *NetworksAPI
  37. VirtualSubnets *VirtualSubnetsAPI
  38. Services *ServicesAPI
  39. Auth *AuthAPI
  40. AvailabilityZones *AvailabilityZonesAPI
  41. Info *InfoAPI
  42. Routers *RoutersAPI
  43. Subnets *SubnetsAPI
  44. }
  45. // Represents Tokens
  46. type TokenOptions struct {
  47. AccessToken string `json:"access_token"`
  48. ExpiresIn int `json:"expires_in"`
  49. RefreshToken string `json:"refresh_token,omitempty"`
  50. IdToken string `json:"id_token"`
  51. TokenType string `json:"token_type"`
  52. }
  53. type TokenCallback func(string)
  54. // Options for Client
  55. type ClientOptions struct {
  56. // When using the Tasks.Wait APIs, defines the duration of how long
  57. // the SDK should continue to poll the server. Default is 30 minutes.
  58. // TasksAPI.WaitTimeout() can be used to specify timeout on
  59. // individual calls.
  60. TaskPollTimeout time.Duration
  61. // Whether or not to ignore any TLS errors when talking to photon,
  62. // false by default.
  63. IgnoreCertificate bool
  64. // List of root CA's to use for server validation
  65. // nil by default.
  66. RootCAs *x509.CertPool
  67. // For tasks APIs, defines the delay between each polling attempt.
  68. // Default is 100 milliseconds.
  69. TaskPollDelay time.Duration
  70. // For tasks APIs, defines the number of retries to make in the event
  71. // of an error. Default is 3.
  72. TaskRetryCount int
  73. // Tokens for user authentication. Default is empty.
  74. TokenOptions *TokenOptions
  75. // A function to be called if the access token was refreshed
  76. // The client can save the new access token for future API
  77. // calls so that it doesn't need to be refreshed again.
  78. UpdateAccessTokenCallback TokenCallback
  79. }
  80. // Creates a new photon client with specified options. If options
  81. // is nil, default options will be used.
  82. func NewClient(endpoint string, options *ClientOptions, logger *log.Logger) (c *Client) {
  83. defaultOptions := &ClientOptions{
  84. TaskPollTimeout: 30 * time.Minute,
  85. TaskPollDelay: 100 * time.Millisecond,
  86. TaskRetryCount: 3,
  87. TokenOptions: &TokenOptions{},
  88. IgnoreCertificate: false,
  89. RootCAs: nil,
  90. }
  91. if options != nil {
  92. if options.TaskPollTimeout != 0 {
  93. defaultOptions.TaskPollTimeout = options.TaskPollTimeout
  94. }
  95. if options.TaskPollDelay != 0 {
  96. defaultOptions.TaskPollDelay = options.TaskPollDelay
  97. }
  98. if options.TaskRetryCount != 0 {
  99. defaultOptions.TaskRetryCount = options.TaskRetryCount
  100. }
  101. if options.TokenOptions != nil {
  102. defaultOptions.TokenOptions = options.TokenOptions
  103. }
  104. if options.RootCAs != nil {
  105. defaultOptions.RootCAs = options.RootCAs
  106. }
  107. defaultOptions.IgnoreCertificate = options.IgnoreCertificate
  108. defaultOptions.UpdateAccessTokenCallback = options.UpdateAccessTokenCallback
  109. }
  110. if logger == nil {
  111. logger = createPassThroughLogger()
  112. }
  113. tr := &http.Transport{
  114. TLSClientConfig: &tls.Config{
  115. InsecureSkipVerify: defaultOptions.IgnoreCertificate,
  116. RootCAs: defaultOptions.RootCAs},
  117. }
  118. endpoint = strings.TrimRight(endpoint, "/")
  119. tokenCallback := func(newToken string) {
  120. c.options.TokenOptions.AccessToken = newToken
  121. if c.options.UpdateAccessTokenCallback != nil {
  122. c.options.UpdateAccessTokenCallback(newToken)
  123. }
  124. }
  125. restClient := &restClient{
  126. httpClient: &http.Client{Transport: tr},
  127. logger: logger,
  128. UpdateAccessTokenCallback: tokenCallback,
  129. }
  130. c = &Client{Endpoint: endpoint, restClient: restClient, logger: logger}
  131. // Ensure a copy of options is made, rather than using a pointer
  132. // which may change out from underneath if misused by the caller.
  133. c.options = *defaultOptions
  134. c.Status = &StatusAPI{c}
  135. c.Tenants = &TenantsAPI{c}
  136. c.Tasks = &TasksAPI{c}
  137. c.Projects = &ProjectsAPI{c}
  138. c.Flavors = &FlavorsAPI{c}
  139. c.Images = &ImagesAPI{c}
  140. c.Disks = &DisksAPI{c}
  141. c.VMs = &VmAPI{c}
  142. c.Hosts = &HostsAPI{c}
  143. c.Deployments = &DeploymentsAPI{c}
  144. c.ResourceTickets = &ResourceTicketsAPI{c}
  145. c.Networks = &NetworksAPI{c}
  146. c.VirtualSubnets = &VirtualSubnetsAPI{c}
  147. c.Services = &ServicesAPI{c}
  148. c.Auth = &AuthAPI{c}
  149. c.AvailabilityZones = &AvailabilityZonesAPI{c}
  150. c.Info = &InfoAPI{c}
  151. c.Routers = &RoutersAPI{c}
  152. c.Subnets = &SubnetsAPI{c}
  153. // Tell the restClient about the Auth API so it can request new
  154. // acces tokens when they expire
  155. restClient.Auth = c.Auth
  156. return
  157. }
  158. // Creates a new photon client with specified options and http.Client.
  159. // Useful for functional testing where http calls must be mocked out.
  160. // If options is nil, default options will be used.
  161. func NewTestClient(endpoint string, options *ClientOptions, httpClient *http.Client) (c *Client) {
  162. c = NewClient(endpoint, options, nil)
  163. c.restClient.httpClient = httpClient
  164. return
  165. }
  166. func createPassThroughLogger() (l *log.Logger) {
  167. // ioutil.Discard makes all logging operation be a no-op.
  168. return log.New(ioutil.Discard, "", log.LstdFlags)
  169. }