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