1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
package client
import (
"errors"
"fmt"
gooseerrors "launchpad.net/~gophers/goose/unstable-001/errors"
goosehttp "launchpad.net/~gophers/goose/unstable-001/http"
"launchpad.net/~gophers/goose/unstable-001/identity"
"net/http"
)
const (
apiTokens = "/tokens"
// The HTTP request methods.
GET = "GET"
POST = "POST"
PUT = "PUT"
DELETE = "DELETE"
HEAD = "HEAD"
COPY = "COPY"
)
type Client interface {
MakeServiceURL(serviceType string, parts []string) (string, error)
SendRequest(method, svcType, apiCall string, requestData *goosehttp.RequestData,
context string, contextArgs ...interface{}) (err error)
}
type OpenStackClient struct {
client *goosehttp.Client
creds *identity.Credentials
auth identity.Authenticator
//TODO - store service urls by region.
ServiceURLs map[string]string
Token string
TenantId string
UserId string
}
func NewClient(creds *identity.Credentials, auth_method identity.AuthMethod) *OpenStackClient {
client_creds := *creds
client_creds.URL = client_creds.URL + apiTokens
client := OpenStackClient{creds: &client_creds}
switch auth_method {
default:
panic(fmt.Errorf("Invalid identity authorisation method: %d", auth_method))
case identity.AuthLegacy:
client.auth = &identity.Legacy{}
case identity.AuthUserPass:
client.auth = &identity.UserPass{}
}
return &client
}
func (c *OpenStackClient) Authenticate() (err error) {
err = nil
if c.auth == nil {
return fmt.Errorf("Authentication method has not been specified")
}
authDetails, err := c.auth.Auth(c.creds)
if err != nil {
err = gooseerrors.AddContext(err, "authentication failed")
return
}
c.Token = authDetails.Token
c.TenantId = authDetails.TenantId
c.UserId = authDetails.UserId
c.ServiceURLs = authDetails.ServiceURLs
return nil
}
func (c *OpenStackClient) IsAuthenticated() bool {
return c.Token != ""
}
// MakeServiceURL prepares a full URL to a service endpoint, with optional
// URL parts. It uses the first endpoint it can find for the given service type.
func (c *OpenStackClient) MakeServiceURL(serviceType string, parts []string) (string, error) {
url, ok := c.ServiceURLs[serviceType]
if !ok {
return "", errors.New("no endpoints known for service type: " + serviceType)
}
for _, part := range parts {
url += part
}
return url, nil
}
func (c *OpenStackClient) SendRequest(method, svcType, apiCall string, requestData *goosehttp.RequestData,
context string, contextArgs ...interface{}) (err error) {
if c.creds != nil && !c.IsAuthenticated() {
err = c.Authenticate()
if err != nil {
err = gooseerrors.AddContext(err, context, contextArgs...)
return
}
}
url, err := c.MakeServiceURL(svcType, []string{apiCall})
if err != nil {
err = gooseerrors.AddContext(err, "cannot find a '%s' node endpoint", svcType)
return
}
if c.client == nil {
c.client = &goosehttp.Client{http.Client{CheckRedirect: nil}, c.Token}
}
if requestData.ReqValue != nil || requestData.RespValue != nil {
err = c.client.JsonRequest(method, url, requestData)
} else {
err = c.client.BinaryRequest(method, url, requestData)
}
if err != nil {
err = gooseerrors.AddContext(err, context, contextArgs...)
}
return
}
|