blob: ebb660a27aa3cbe32f41c57ff68a1e5998a2a232 [file] [log] [blame]
Samuel A. Falvo II43d83532014-07-31 14:34:48 -07001// +build acceptance
2
3package tools
4
5import (
6 "crypto/rand"
7 "fmt"
8 "github.com/rackspace/gophercloud/openstack/compute/servers"
9 "github.com/rackspace/gophercloud/openstack/identity"
10 "github.com/rackspace/gophercloud/openstack/utils"
11 "os"
12 "text/tabwriter"
13 "time"
14)
15
16var errTimeout = fmt.Errorf("Timeout.")
17
18type testState struct {
19 O identity.AuthOptions
20 A identity.AuthResults
21 SC *identity.ServiceCatalog
22 EPs []identity.Endpoint
23 W *tabwriter.Writer
24 ImageId string
25 FlavorId string
26 Region string
27 EP string
28 Client *servers.Client
29 CreatedServer *servers.Server
30 GottenServer *servers.Server
31 UpdatedServer *servers.Server
32 ServerName string
33 AlternateName string
34 FlavorIdResize string
35}
36
37func SetupForList(service string) (*testState, error) {
38 var err error
39
40 ts := new(testState)
41
42 ts.O, err = utils.AuthOptions()
43 if err != nil {
44 return ts, err
45 }
46
47 ts.A, err = identity.Authenticate(ts.O)
48 if err != nil {
49 return ts, err
50 }
51
52 ts.SC, err = identity.GetServiceCatalog(ts.A)
53 if err != nil {
54 return ts, err
55 }
56
57 ts.EPs, err = FindAllEndpoints(ts.SC, service)
58 if err != nil {
59 return ts, err
60 }
61
62 ts.W = new(tabwriter.Writer)
63 ts.W.Init(os.Stdout, 2, 8, 2, ' ', 0)
64
65 return ts, nil
66}
67
68func SetupForCRUD() (*testState, error) {
69 ts, err := SetupForList("compute")
70 if err != nil {
71 return ts, err
72 }
73
74 ts.ImageId = os.Getenv("OS_IMAGE_ID")
75 if ts.ImageId == "" {
76 return ts, fmt.Errorf("Expected OS_IMAGE_ID environment variable to be set")
77 }
78
79 ts.FlavorId = os.Getenv("OS_FLAVOR_ID")
80 if ts.FlavorId == "" {
81 return ts, fmt.Errorf("Expected OS_FLAVOR_ID environment variable to be set")
82 }
83
84 ts.FlavorIdResize = os.Getenv("OS_FLAVOR_ID_RESIZE")
85 if ts.FlavorIdResize == "" {
86 return ts, fmt.Errorf("Expected OS_FLAVOR_ID_RESIZE environment variable to be set")
87 }
88
89 if ts.FlavorIdResize == ts.FlavorId {
90 return ts, fmt.Errorf("OS_FLAVOR_ID and OS_FLAVOR_ID_RESIZE cannot be the same")
91 }
92
93 ts.Region = os.Getenv("OS_REGION_NAME")
94 if ts.Region == "" {
95 ts.Region = ts.EPs[0].Region
96 }
97
98 ts.EP, err = FindEndpointForRegion(ts.EPs, ts.Region)
99 if err != nil {
100 return ts, err
101 }
102
103 return ts, err
104}
105
106func FindAllEndpoints(sc *identity.ServiceCatalog, service string) ([]identity.Endpoint, error) {
107 ces, err := sc.CatalogEntries()
108 if err != nil {
109 return nil, err
110 }
111
112 for _, ce := range ces {
113 if ce.Type == service {
114 return ce.Endpoints, nil
115 }
116 }
117
118 return nil, fmt.Errorf(service + " endpoint not found.")
119}
120
121func FindEndpointForRegion(eps []identity.Endpoint, r string) (string, error) {
122 for _, ep := range eps {
123 if ep.Region == r {
124 return ep.PublicURL, nil
125 }
126 }
127 return "", fmt.Errorf("Unknown region %s", r)
128}
129
130func CountDown(ts *testState, timeout int) (bool, int, error) {
131 if timeout < 1 {
132 return false, 0, errTimeout
133 }
134 time.Sleep(1 * time.Second)
135 timeout--
136
137 gr, err := servers.GetDetail(ts.Client, ts.CreatedServer.Id)
138 if err != nil {
139 return false, timeout, err
140 }
141
142 ts.GottenServer, err = servers.GetServer(gr)
143 if err != nil {
144 return false, timeout, err
145 }
146
147 return true, timeout, nil
148}
149
150func CreateServer(ts *testState) error {
151 ts.ServerName = RandomString("ACPTTEST", 16)
152 fmt.Printf("Attempting to create server: %s\n", ts.ServerName)
153
154 ts.Client = servers.NewClient(ts.EP, ts.A, ts.O)
155
156 cr, err := servers.Create(ts.Client, map[string]interface{}{
157 "flavorRef": ts.FlavorId,
158 "imageRef": ts.ImageId,
159 "name": ts.ServerName,
160 })
161 if err != nil {
162 return err
163 }
164
165 ts.CreatedServer, err = servers.GetServer(cr)
166 return err
167}
168
169func WaitForStatus(ts *testState, s string) error {
170 var (
171 inProgress bool
172 timeout int
173 err error
174 )
175
176 for inProgress, timeout, err = CountDown(ts, 300); inProgress; inProgress, timeout, err = CountDown(ts, timeout) {
177 if ts.GottenServer.Id != ts.CreatedServer.Id {
178 return fmt.Errorf("created server id (%s) != gotten server id (%s)", ts.CreatedServer.Id, ts.GottenServer.Id)
179 }
180
181 if ts.GottenServer.Status == s {
182 fmt.Printf("Server reached state %s after %d seconds (approximately)\n", s, 300-timeout)
183 break
184 }
185 }
186
187 if err == errTimeout {
188 fmt.Printf("Time out -- I'm not waiting around.\n")
189 err = nil
190 }
191
192 return err
193}
194
195func ChangeServerName(ts *testState) error {
196 var (
197 inProgress bool
198 timeout int
199 )
200
201 ts.AlternateName = RandomString("ACPTTEST", 16)
202 for ts.AlternateName == ts.ServerName {
203 ts.AlternateName = RandomString("ACPTTEST", 16)
204 }
205 fmt.Println("Attempting to change server name")
206
207 ur, err := servers.Update(ts.Client, ts.CreatedServer.Id, map[string]interface{}{
208 "name": ts.AlternateName,
209 })
210 if err != nil {
211 return err
212 }
213
214 ts.UpdatedServer, err = servers.GetServer(ur)
215 if err != nil {
216 return err
217 }
218
219 if ts.UpdatedServer.Id != ts.CreatedServer.Id {
220 return fmt.Errorf("Expected updated and created server to share the same ID")
221 }
222
223 for inProgress, timeout, err = CountDown(ts, 300); inProgress; inProgress, timeout, err = CountDown(ts, timeout) {
224 if ts.GottenServer.Id != ts.UpdatedServer.Id {
225 return fmt.Errorf("Updated server ID (%s) != gotten server ID (%s)", ts.UpdatedServer.Id, ts.GottenServer.Id)
226 }
227
228 if ts.GottenServer.Name == ts.AlternateName {
229 fmt.Printf("Server updated after %d seconds (approximately)\n", 300-timeout)
230 break
231 }
232 }
233
234 if err == errTimeout {
235 fmt.Printf("I'm not waiting around.\n")
236 err = nil
237 }
238
239 return err
240}
241
242func MakeNewPassword(oldPass string) string {
243 fmt.Println("Current password: "+oldPass)
244 randomPassword := RandomString("", 16)
245 for randomPassword == oldPass {
246 randomPassword = RandomString("", 16)
247 }
248 fmt.Println(" New password: "+randomPassword)
249 return randomPassword
250}
251
252func ChangeAdminPassword(ts *testState) error {
253 randomPassword := MakeNewPassword(ts.CreatedServer.AdminPass)
254
255 err := servers.ChangeAdminPassword(ts.Client, ts.CreatedServer.Id, randomPassword)
256 if err != nil {
257 return err
258 }
259
260 err = WaitForStatus(ts, "PASSWORD")
261 if err != nil {
262 return err
263 }
264
265 return WaitForStatus(ts, "ACTIVE")
266}
267
268func RebootServer(ts *testState) error {
269 fmt.Println("Attempting reboot of server "+ts.CreatedServer.Id)
270 err := servers.Reboot(ts.Client, ts.CreatedServer.Id, servers.OSReboot)
271 if err != nil {
272 return err
273 }
274
275 err = WaitForStatus(ts, "REBOOT")
276 if err != nil {
277 return err
278 }
279
280 return WaitForStatus(ts, "ACTIVE")
281}
282
283func RebuildServer(ts *testState) error {
284 fmt.Println("Attempting to rebuild server "+ts.CreatedServer.Id)
285
286 newPassword := MakeNewPassword(ts.CreatedServer.AdminPass)
287 newName := RandomString("ACPTTEST", 16)
288 sr, err := servers.Rebuild(ts.Client, ts.CreatedServer.Id, newName, newPassword, ts.ImageId, nil)
289 if err != nil {
290 return err
291 }
292
293 s, err := servers.GetServer(sr)
294 if err != nil {
295 return err
296 }
297 if s.Id != ts.CreatedServer.Id {
298 return fmt.Errorf("Expected rebuilt server ID of %s; got %s", ts.CreatedServer.Id, s.Id)
299 }
300
301 err = WaitForStatus(ts, "REBUILD")
302 if err != nil {
303 return err
304 }
305
306 return WaitForStatus(ts, "ACTIVE")
307}
308
309func ResizeServer(ts *testState) error {
310 fmt.Println("Attempting to resize server "+ts.CreatedServer.Id)
311
312 err := servers.Resize(ts.Client, ts.CreatedServer.Id, ts.FlavorIdResize)
313 if err != nil {
314 return err
315 }
316
317 err = WaitForStatus(ts, "RESIZE")
318 if err != nil {
319 return err
320 }
321
322 return WaitForStatus(ts, "VERIFY_RESIZE")
323}
324
325func ConfirmResize(ts *testState) error {
326 fmt.Println("Attempting to confirm resize for server "+ts.CreatedServer.Id)
327
328 err := servers.ConfirmResize(ts.Client, ts.CreatedServer.Id)
329 if err != nil {
330 return err
331 }
332
333 return WaitForStatus(ts, "ACTIVE")
334}
335
336func RevertResize(ts *testState) error {
337 fmt.Println("Attempting to revert resize for server "+ts.CreatedServer.Id)
338
339 err := servers.RevertResize(ts.Client, ts.CreatedServer.Id)
340 if err != nil {
341 return err
342 }
343
344 err = WaitForStatus(ts, "REVERT_RESIZE")
345 if err != nil {
346 return err
347 }
348
349 return WaitForStatus(ts, "ACTIVE")
350}
351
352// randomString generates a string of given length, but random content.
353// All content will be within the ASCII graphic character set.
354// (Implementation from Even Shaw's contribution on
355// http://stackoverflow.com/questions/12771930/what-is-the-fastest-way-to-generate-a-long-random-string-in-go).
356func RandomString(prefix string, n int) string {
357 const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
358 var bytes = make([]byte, n)
359 rand.Read(bytes)
360 for i, b := range bytes {
361 bytes[i] = alphanum[b%byte(len(alphanum))]
362 }
363 return prefix + string(bytes)
364}