-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrax.go
186 lines (166 loc) · 5.06 KB
/
rax.go
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/* Rackspace API wrapper */
package main
import (
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"strconv"
"strings"
)
const (
identityUrl = "https://identity.api.rackspacecloud.com/v2.0/tokens"
authRequest = `{ "auth": {
"RAX-KSKEY:apiKeyCredentials": {
"apiKey": "%s",
"username": "kontsevoy.api"
}}}`
)
// This data structure is returned as JSON when we authenticate.
// Generated by ChimeraCoder/gojson from curl/auth_output.json
type RaxSession struct {
Access struct {
ServiceCatalog []struct {
Endpoints []struct {
PublicURL string `json:"publicURL"`
Region string `json:"region"`
TenantID string `json:"tenantId"`
} `json:"endpoints"`
Name string `json:"name"`
Type string `json:"type"`
} `json:"serviceCatalog"`
Token struct {
RAX_AUTH_authenticatedBy []string `json:"RAX-AUTH:authenticatedBy"`
Expires string `json:"expires"`
ID string `json:"id"`
Tenant struct {
ID string `json:"id"`
Name string `json:"name"`
} `json:"tenant"`
} `json:"token"`
User struct {
RAX_AUTH_defaultRegion string `json:"RAX-AUTH:defaultRegion"`
ID string `json:"id"`
Name string `json:"name"`
Roles []struct {
Description string `json:"description"`
ID string `json:"id"`
Name string `json:"name"`
TenantID string `json:"tenantId"`
} `json:"roles"`
} `json:"user"`
} `json:"access"`
Region string
}
// Authenticates against Rackspace Auth and returns an authentication token
func authenticate(apiKey string) (session RaxSession, err error) {
requestBody := fmt.Sprintf(authRequest, apiKey)
response, err := http.Post(identityUrl, "application/json", strings.NewReader(requestBody))
if err != nil {
return
}
if response.StatusCode != http.StatusOK {
err = errors.New(response.Status)
return
}
responseBody, err := ioutil.ReadAll(response.Body)
if err != nil {
return
}
err = json.Unmarshal(responseBody, &session)
if err != nil {
return
}
return
}
// Extracts an entry point for a given region + service type from the JSON
// data returned by Auth API call
func (ai *RaxSession) getEntryPoint(region string, serviceType string) (entryPoint string) {
for _, service := range ai.Access.ServiceCatalog {
if service.Type == serviceType {
for _, ep := range service.Endpoints {
if ep.Region == region {
entryPoint = ep.PublicURL
return entryPoint
}
}
}
}
return
}
// return object store URL
func (s *RaxSession) getObjectStoreUrl() string {
return s.getEntryPoint(s.Region, "object-store")
}
// return a CDN URL
func (s *RaxSession) getCdnUrl() string {
return s.getEntryPoint(s.Region, "rax:object-cdn")
}
// listContainers() makes a GET request to list the root containers
// under customer account
func (s *RaxSession) listContainers() (err error) {
request := s.makeRequest("GET", s.getObjectStoreUrl()+"?format=text", nil)
response, err := http.DefaultClient.Do(request)
if err != nil {
return
}
body, _ := ioutil.ReadAll(response.Body)
fmt.Println(string(body))
return
}
// listObjects() makes a GET request to list all files(objects) in a container
func (s *RaxSession) listObjects(container string) (err error) {
request := s.makeRequest("GET", s.getObjectStoreUrl()+"/"+container, nil)
response, err := http.DefaultClient.Do(request)
if err != nil {
return
}
body, _ := ioutil.ReadAll(response.Body)
fmt.Println(string(body))
return
}
// Create/update CloudFiles object (file)
func (s *RaxSession) upsertObject(r io.Reader, container string, objectName string, ttl int) (urls []string) {
url := strings.Join([]string{s.getObjectStoreUrl(), container, objectName}, "/")
request := s.makeRequest("PUT", url, r)
if ttl > 0 {
request.Header.Add("X-Delete-After", strconv.Itoa(ttl))
}
resp, err := http.DefaultClient.Do(request)
panicIf(err)
fmt.Println(resp.Status)
// get CDN URL for the uploaded file:
url = strings.Join([]string{s.getCdnUrl(), container}, "/")
request = s.makeRequest("HEAD", url, nil)
resp, err = http.DefaultClient.Do(request)
panicIf(err)
// return the URLs for the uploaded object
if resp.Header["X-Cdn-Uri"] != nil {
urls = []string{resp.Header["X-Cdn-Uri"][0] + "/" + objectName,
resp.Header["X-Cdn-Ssl-Uri"][0] + "/" + objectName}
}
return
}
// Delete cloud files object:
func (s *RaxSession) deleteObject(container string, objectName string) {
url := strings.Join([]string{s.getObjectStoreUrl(), container, objectName}, "/")
request := s.makeRequest("DELETE", url, nil)
r, err := http.DefaultClient.Do(request)
if err != nil {
return
}
if r.StatusCode == http.StatusNoContent {
fmt.Println("File deleted")
} else {
fmt.Println(r.Status)
}
}
// makeRequest is a helper function: it prepares HTTP requests with X-Auth-Token set
func (s *RaxSession) makeRequest(verb string, url string, reader io.Reader) (request *http.Request) {
request, err := http.NewRequest(verb, url, reader)
panicIf(err)
request.Header.Add("X-Auth-Token", s.Access.Token.ID)
return
}