blob: e753c8b6705e115ebd335dab40856f9bc71d4a25 [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"
Marc Abramowitz4b9f1c82014-08-12 23:24:27 -07007 "github.com/tonnerre/golang-pretty"
Samuel A. Falvo II5d0d74c2013-06-25 17:23:18 -07008)
9
Samuel A. Falvo II2e2b8772013-07-04 15:40:15 -070010// Provider structures exist for each tangible provider of OpenStack service.
11// For example, Rackspace, Hewlett-Packard, and NASA might have their own instance of this structure.
12//
13// At a minimum, a provider must expose an authentication endpoint.
14type Provider struct {
15 AuthEndpoint string
16}
17
Samuel A. Falvo II1206f852013-07-15 17:56:51 -070018// ReauthHandlerFunc functions are responsible for somehow performing the task of
19// reauthentication.
Samuel A. Falvo II2f50b142013-07-16 11:38:03 -070020type ReauthHandlerFunc func(AccessProvider) error
Samuel A. Falvo II1206f852013-07-15 17:56:51 -070021
Samuel A. Falvo II4e895182013-06-26 15:44:18 -070022// Context structures encapsulate Gophercloud-global state in a manner which
23// facilitates easier unit testing. As a user of this SDK, you'll never
24// have to use this structure, except when contributing new code to the SDK.
Samuel A. Falvo IIfd78c302013-06-25 16:35:32 -070025type Context struct {
Samuel A. Falvo II5d0d74c2013-06-25 17:23:18 -070026 // providerMap serves as a directory of supported providers.
Samuel A. Falvo II4e895182013-06-26 15:44:18 -070027 providerMap map[string]Provider
Samuel A. Falvo II5d0d74c2013-06-25 17:23:18 -070028
29 // httpClient refers to the current HTTP client interface to use.
30 httpClient *http.Client
Samuel A. Falvo II1206f852013-07-15 17:56:51 -070031
32 // reauthHandler provides the functionality needed to re-authenticate
33 // if that feature is enabled. Note: in order to allow for automatic
34 // re-authentication, the Context object will need to remember your
35 // username, password, and tenant ID as provided in the initial call
36 // to Authenticate(). If you do not desire this, you'll need to handle
37 // reauthentication yourself through other means. Two methods exist:
38 // the first approach is to just handle errors yourself at the application
39 // layer, and the other is through a custom reauthentication handler
40 // set through the WithReauthHandler() method.
41 reauthHandler ReauthHandlerFunc
Samuel A. Falvo IIfd78c302013-06-25 16:35:32 -070042}
43
Samuel A. Falvo II4e895182013-06-26 15:44:18 -070044// TestContext yields a new Context instance, pre-initialized with a barren
45// state suitable for per-unit-test customization. This configuration consists
46// of:
47//
48// * An empty provider map.
49//
50// * 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 -070051func TestContext() *Context {
52 return &Context{
Samuel A. Falvo II4e895182013-06-26 15:44:18 -070053 providerMap: make(map[string]Provider),
Samuel A. Falvo II839428e2013-06-25 18:02:24 -070054 httpClient: &http.Client{},
Samuel A. Falvo II9e64f6b2013-07-16 14:26:50 -070055 reauthHandler: func(acc AccessProvider) error {
56 return acc.Reauthenticate()
57 },
Samuel A. Falvo IIfd78c302013-06-25 16:35:32 -070058 }
59}
Samuel A. Falvo II5d0d74c2013-06-25 17:23:18 -070060
Samuel A. Falvo II4e895182013-06-26 15:44:18 -070061// UseCustomClient configures the context to use a customized HTTP client
62// instance. By default, TestContext() will return a Context which uses
Samuel A. Falvo IIfca35b72013-07-02 18:30:28 -070063// the net/http package's default client instance.
Samuel A. Falvo II2e2b8772013-07-04 15:40:15 -070064func (c *Context) UseCustomClient(hc *http.Client) *Context {
Samuel A. Falvo II839428e2013-06-25 18:02:24 -070065 c.httpClient = hc
Samuel A. Falvo II2e2b8772013-07-04 15:40:15 -070066 return c
67}
68
69// RegisterProvider allows a unit test to register a mythical provider convenient for testing.
70// If the provider structure lacks adequate configuration, or the configuration given has some
71// detectable error, an ErrConfiguration error will result.
72func (c *Context) RegisterProvider(name string, p Provider) error {
73 if p.AuthEndpoint == "" {
74 return ErrConfiguration
75 }
76
77 c.providerMap[name] = p
78 return nil
79}
80
81// WithProvider offers convenience for unit tests.
82func (c *Context) WithProvider(name string, p Provider) *Context {
83 err := c.RegisterProvider(name, p)
84 if err != nil {
85 panic(err)
86 }
87 return c
88}
89
90// ProviderByName will locate a provider amongst those previously registered, if it exists.
91// If the named provider has not been registered, an ErrProvider error will result.
Samuel A. Falvo II32d297d2013-10-24 16:45:58 -070092//
93// You may also specify a custom Identity API URL.
94// Any provider name that contains the characters "://", in that order, will be treated as a custom Identity API URL.
95// Custom URLs, important for private cloud deployments, overrides all provider configurations.
Samuel A. Falvo II2e2b8772013-07-04 15:40:15 -070096func (c *Context) ProviderByName(name string) (p Provider, err error) {
97 for provider, descriptor := range c.providerMap {
98 if name == provider {
99 return descriptor, nil
100 }
101 }
Justin Santa Barbara23f2a3a2013-08-31 17:54:59 -0700102 if strings.Contains(name, "://") {
Jon Perritt0c1629d2013-12-06 19:51:36 -0600103 p = Provider{
Justin Santa Barbara23f2a3a2013-08-31 17:54:59 -0700104 AuthEndpoint: name,
105 }
106 return p, nil
107 }
Samuel A. Falvo II2e2b8772013-07-04 15:40:15 -0700108 return Provider{}, ErrProvider
109}
110
Marc Abramowitz4b9f1c82014-08-12 23:24:27 -0700111func getServiceCatalogFromAccessProvider(provider AccessProvider) ([]CatalogEntry) {
112 access, found := provider.(*Access)
113 if found {
114 return access.ServiceCatalog
115 } else {
116 return nil
117 }
118}
119
Samuel A. Falvo II1dd740a2013-07-08 15:48:40 -0700120// Instantiates a Cloud Servers API for the provider given.
Marc Abramowitz4b9f1c82014-08-12 23:24:27 -0700121func (c *Context) ServersApi(provider AccessProvider, criteria ApiCriteria) (CloudServersProvider, error) {
122 url := provider.FirstEndpointUrlByCriteria(criteria)
Samuel A. Falvo II2e2b8772013-07-04 15:40:15 -0700123 if url == "" {
Marc Abramowitza042d122014-08-12 16:58:44 -0700124 var err = fmt.Errorf(
Marc Abramowitz4b9f1c82014-08-12 23:24:27 -0700125 "Missing endpoint, or insufficient privileges to access endpoint; criteria = %# v; serviceCatalog = %# v",
126 pretty.Formatter(criteria),
127 pretty.Formatter(getServiceCatalogFromAccessProvider(provider)))
Marc Abramowitza042d122014-08-12 16:58:44 -0700128 return nil, err
Samuel A. Falvo II2e2b8772013-07-04 15:40:15 -0700129 }
130
Samuel A. Falvo II1dd740a2013-07-08 15:48:40 -0700131 gcp := &genericServersProvider{
Samuel A. Falvo II2e2b8772013-07-04 15:40:15 -0700132 endpoint: url,
133 context: c,
Marc Abramowitz4b9f1c82014-08-12 23:24:27 -0700134 access: provider,
Samuel A. Falvo II2e2b8772013-07-04 15:40:15 -0700135 }
136
137 return gcp, nil
Samuel A. Falvo II5d0d74c2013-06-25 17:23:18 -0700138}
Samuel A. Falvo II1206f852013-07-15 17:56:51 -0700139
140// WithReauthHandler configures the context to handle reauthentication attempts using the supplied
141// funtion. By default, reauthentication happens by invoking Authenticate(), which is unlikely to be
142// useful in a unit test.
143//
144// Do not confuse this function with WithReauth()! Although they work together to support reauthentication,
145// WithReauth() actually contains the decision-making logic to determine when to perform a reauth,
146// while WithReauthHandler() is used to configure what a reauth actually entails.
147func (c *Context) WithReauthHandler(f ReauthHandlerFunc) *Context {
148 c.reauthHandler = f
149 return c
150}