blob: aca869cee07f6c7b6ae725032d4a944616b91a08 [file] [log] [blame]
Samuel A. Falvo IIfd78c302013-06-25 16:35:32 -07001package gophercloud
2
Samuel A. Falvo II5d0d74c2013-06-25 17:23:18 -07003import (
4 "net/http"
Justin Santa Barbara23f2a3a2013-08-31 17:54:59 -07005 "strings"
Marc Abramowitza042d122014-08-12 16:58:44 -07006 "fmt"
Samuel A. Falvo II5d0d74c2013-06-25 17:23:18 -07007)
8
Samuel A. Falvo II2e2b8772013-07-04 15:40:15 -07009// Provider structures exist for each tangible provider of OpenStack service.
10// For example, Rackspace, Hewlett-Packard, and NASA might have their own instance of this structure.
11//
12// At a minimum, a provider must expose an authentication endpoint.
13type Provider struct {
14 AuthEndpoint string
15}
16
Samuel A. Falvo II1206f852013-07-15 17:56:51 -070017// ReauthHandlerFunc functions are responsible for somehow performing the task of
18// reauthentication.
Samuel A. Falvo II2f50b142013-07-16 11:38:03 -070019type ReauthHandlerFunc func(AccessProvider) error
Samuel A. Falvo II1206f852013-07-15 17:56:51 -070020
Samuel A. Falvo II4e895182013-06-26 15:44:18 -070021// Context structures encapsulate Gophercloud-global state in a manner which
22// facilitates easier unit testing. As a user of this SDK, you'll never
23// have to use this structure, except when contributing new code to the SDK.
Samuel A. Falvo IIfd78c302013-06-25 16:35:32 -070024type Context struct {
Samuel A. Falvo II5d0d74c2013-06-25 17:23:18 -070025 // providerMap serves as a directory of supported providers.
Samuel A. Falvo II4e895182013-06-26 15:44:18 -070026 providerMap map[string]Provider
Samuel A. Falvo II5d0d74c2013-06-25 17:23:18 -070027
28 // httpClient refers to the current HTTP client interface to use.
29 httpClient *http.Client
Samuel A. Falvo II1206f852013-07-15 17:56:51 -070030
31 // reauthHandler provides the functionality needed to re-authenticate
32 // if that feature is enabled. Note: in order to allow for automatic
33 // re-authentication, the Context object will need to remember your
34 // username, password, and tenant ID as provided in the initial call
35 // to Authenticate(). If you do not desire this, you'll need to handle
36 // reauthentication yourself through other means. Two methods exist:
37 // the first approach is to just handle errors yourself at the application
38 // layer, and the other is through a custom reauthentication handler
39 // set through the WithReauthHandler() method.
40 reauthHandler ReauthHandlerFunc
Samuel A. Falvo IIfd78c302013-06-25 16:35:32 -070041}
42
Samuel A. Falvo II4e895182013-06-26 15:44:18 -070043// TestContext yields a new Context instance, pre-initialized with a barren
44// state suitable for per-unit-test customization. This configuration consists
45// of:
46//
47// * An empty provider map.
48//
49// * An HTTP client built by the net/http package (see http://godoc.org/net/http#Client).
Samuel A. Falvo IIfd78c302013-06-25 16:35:32 -070050func TestContext() *Context {
51 return &Context{
Samuel A. Falvo II4e895182013-06-26 15:44:18 -070052 providerMap: make(map[string]Provider),
Samuel A. Falvo II839428e2013-06-25 18:02:24 -070053 httpClient: &http.Client{},
Samuel A. Falvo II9e64f6b2013-07-16 14:26:50 -070054 reauthHandler: func(acc AccessProvider) error {
55 return acc.Reauthenticate()
56 },
Samuel A. Falvo IIfd78c302013-06-25 16:35:32 -070057 }
58}
Samuel A. Falvo II5d0d74c2013-06-25 17:23:18 -070059
Samuel A. Falvo II4e895182013-06-26 15:44:18 -070060// UseCustomClient configures the context to use a customized HTTP client
61// instance. By default, TestContext() will return a Context which uses
Samuel A. Falvo IIfca35b72013-07-02 18:30:28 -070062// the net/http package's default client instance.
Samuel A. Falvo II2e2b8772013-07-04 15:40:15 -070063func (c *Context) UseCustomClient(hc *http.Client) *Context {
Samuel A. Falvo II839428e2013-06-25 18:02:24 -070064 c.httpClient = hc
Samuel A. Falvo II2e2b8772013-07-04 15:40:15 -070065 return c
66}
67
68// RegisterProvider allows a unit test to register a mythical provider convenient for testing.
69// If the provider structure lacks adequate configuration, or the configuration given has some
70// detectable error, an ErrConfiguration error will result.
71func (c *Context) RegisterProvider(name string, p Provider) error {
72 if p.AuthEndpoint == "" {
73 return ErrConfiguration
74 }
75
76 c.providerMap[name] = p
77 return nil
78}
79
80// WithProvider offers convenience for unit tests.
81func (c *Context) WithProvider(name string, p Provider) *Context {
82 err := c.RegisterProvider(name, p)
83 if err != nil {
84 panic(err)
85 }
86 return c
87}
88
89// ProviderByName will locate a provider amongst those previously registered, if it exists.
90// If the named provider has not been registered, an ErrProvider error will result.
Samuel A. Falvo II32d297d2013-10-24 16:45:58 -070091//
92// You may also specify a custom Identity API URL.
93// Any provider name that contains the characters "://", in that order, will be treated as a custom Identity API URL.
94// Custom URLs, important for private cloud deployments, overrides all provider configurations.
Samuel A. Falvo II2e2b8772013-07-04 15:40:15 -070095func (c *Context) ProviderByName(name string) (p Provider, err error) {
96 for provider, descriptor := range c.providerMap {
97 if name == provider {
98 return descriptor, nil
99 }
100 }
Justin Santa Barbara23f2a3a2013-08-31 17:54:59 -0700101 if strings.Contains(name, "://") {
Jon Perritt0c1629d2013-12-06 19:51:36 -0600102 p = Provider{
Justin Santa Barbara23f2a3a2013-08-31 17:54:59 -0700103 AuthEndpoint: name,
104 }
105 return p, nil
106 }
Samuel A. Falvo II2e2b8772013-07-04 15:40:15 -0700107 return Provider{}, ErrProvider
108}
109
Samuel A. Falvo II1dd740a2013-07-08 15:48:40 -0700110// Instantiates a Cloud Servers API for the provider given.
111func (c *Context) ServersApi(acc AccessProvider, criteria ApiCriteria) (CloudServersProvider, error) {
Samuel A. Falvo II2e2b8772013-07-04 15:40:15 -0700112 url := acc.FirstEndpointUrlByCriteria(criteria)
113 if url == "" {
Marc Abramowitza042d122014-08-12 16:58:44 -0700114 var err = fmt.Errorf(
115 "Missing endpoint, or insufficient privileges to access endpoint; criteria = %# v",
116 criteria)
117 return nil, err
Samuel A. Falvo II2e2b8772013-07-04 15:40:15 -0700118 }
119
Samuel A. Falvo II1dd740a2013-07-08 15:48:40 -0700120 gcp := &genericServersProvider{
Samuel A. Falvo II2e2b8772013-07-04 15:40:15 -0700121 endpoint: url,
122 context: c,
Samuel A. Falvo IIbc0d54a2013-07-08 14:45:21 -0700123 access: acc,
Samuel A. Falvo II2e2b8772013-07-04 15:40:15 -0700124 }
125
126 return gcp, nil
Samuel A. Falvo II5d0d74c2013-06-25 17:23:18 -0700127}
Samuel A. Falvo II1206f852013-07-15 17:56:51 -0700128
129// WithReauthHandler configures the context to handle reauthentication attempts using the supplied
130// funtion. By default, reauthentication happens by invoking Authenticate(), which is unlikely to be
131// useful in a unit test.
132//
133// Do not confuse this function with WithReauth()! Although they work together to support reauthentication,
134// WithReauth() actually contains the decision-making logic to determine when to perform a reauth,
135// while WithReauthHandler() is used to configure what a reauth actually entails.
136func (c *Context) WithReauthHandler(f ReauthHandlerFunc) *Context {
137 c.reauthHandler = f
138 return c
139}