blob: cf234e7e8af5b7ca012c1896b4e11c19574ca325 [file] [log] [blame]
Samuel A. Falvo II2dd7d2f2014-06-30 16:18:08 -07001// +build acceptance,old
2
Samuel A. Falvo II704a7502013-07-10 15:23:43 -07003package main
4
5import (
Samuel A. Falvo IIe91ff6d2013-07-11 15:46:10 -07006 "crypto/rand"
Mark Peekd27e2532013-08-27 07:58:09 -07007 "fmt"
Samuel A. Falvo II8935ca32013-07-25 21:27:06 -07008 "github.com/rackspace/gophercloud"
Mark Peekd27e2532013-08-27 07:58:09 -07009 "os"
Max Lincoln5b250e02013-12-13 14:49:38 -030010 "strings"
Samuel A. Falvo II002b6512013-07-29 16:30:40 -070011 "time"
Samuel A. Falvo II704a7502013-07-10 15:23:43 -070012)
13
14// getCredentials will verify existence of needed credential information
15// provided through environment variables. This function will not return
16// if at least one piece of required information is missing.
Rafael Garciae4a550e2013-12-06 17:00:32 -030017func getCredentials() (provider, username, password, apiKey string) {
Samuel A. Falvo II704a7502013-07-10 15:23:43 -070018 provider = os.Getenv("SDK_PROVIDER")
19 username = os.Getenv("SDK_USERNAME")
20 password = os.Getenv("SDK_PASSWORD")
Rafael Garcia752cb332013-12-12 22:16:58 -030021 apiKey = os.Getenv("SDK_API_KEY")
Max Lincoln9d9a8472014-06-25 16:21:28 -040022 var authURL = os.Getenv("OS_AUTH_URL")
Samuel A. Falvo II704a7502013-07-10 15:23:43 -070023
24 if (provider == "") || (username == "") || (password == "") {
25 fmt.Fprintf(os.Stderr, "One or more of the following environment variables aren't set:\n")
26 fmt.Fprintf(os.Stderr, " SDK_PROVIDER=\"%s\"\n", provider)
27 fmt.Fprintf(os.Stderr, " SDK_USERNAME=\"%s\"\n", username)
28 fmt.Fprintf(os.Stderr, " SDK_PASSWORD=\"%s\"\n", password)
29 os.Exit(1)
30 }
31
Max Lincoln9d9a8472014-06-25 16:21:28 -040032 if strings.Contains(provider, "rackspace") && (authURL != "") {
33 provider = authURL + "/v2.0/tokens"
34 }
35
Samuel A. Falvo II704a7502013-07-10 15:23:43 -070036 return
37}
Samuel A. Falvo IIe91ff6d2013-07-11 15:46:10 -070038
39// randomString generates a string of given length, but random content.
40// All content will be within the ASCII graphic character set.
41// (Implementation from Even Shaw's contribution on
42// http://stackoverflow.com/questions/12771930/what-is-the-fastest-way-to-generate-a-long-random-string-in-go).
Samuel A. Falvo IIe3b2d7a2013-07-12 11:08:02 -070043func randomString(prefix string, n int) string {
Mark Peekd27e2532013-08-27 07:58:09 -070044 const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
45 var bytes = make([]byte, n)
46 rand.Read(bytes)
47 for i, b := range bytes {
48 bytes[i] = alphanum[b%byte(len(alphanum))]
49 }
50 return prefix + string(bytes)
Samuel A. Falvo II8935ca32013-07-25 21:27:06 -070051}
52
53// aSuitableImage finds a minimal image for use in dynamically creating servers.
54// If none can be found, this function will panic.
55func aSuitableImage(api gophercloud.CloudServersProvider) string {
56 images, err := api.ListImages()
57 if err != nil {
58 panic(err)
59 }
60
61 // TODO(sfalvo):
62 // Works for Rackspace, might not work for your provider!
63 // Need to figure out why ListImages() provides 0 values for
64 // Ram and Disk fields.
65 //
66 // Until then, just return Ubuntu 12.04 LTS.
67 for i := 0; i < len(images); i++ {
Max Lincoln5b250e02013-12-13 14:49:38 -030068 if strings.Contains(images[i].Name, "Ubuntu 12.04 LTS") {
Samuel A. Falvo II8935ca32013-07-25 21:27:06 -070069 return images[i].Id
70 }
71 }
Max Lincoln5b250e02013-12-13 14:49:38 -030072 panic("Image for Ubuntu 12.04 LTS not found.")
Samuel A. Falvo II8935ca32013-07-25 21:27:06 -070073}
74
75// aSuitableFlavor finds the minimum flavor capable of running the test image
76// chosen by aSuitableImage. If none can be found, this function will panic.
77func aSuitableFlavor(api gophercloud.CloudServersProvider) string {
78 flavors, err := api.ListFlavors()
79 if err != nil {
80 panic(err)
81 }
82
83 // TODO(sfalvo):
84 // Works for Rackspace, might not work for your provider!
85 // Need to figure out why ListFlavors() provides 0 values for
86 // Ram and Disk fields.
87 //
88 // Until then, just return Ubuntu 12.04 LTS.
89 for i := 0; i < len(flavors); i++ {
90 if flavors[i].Id == "2" {
91 return flavors[i].Id
92 }
93 }
94 panic("Flavor 2 (512MB 1-core 20GB machine) not found.")
95}
96
97// createServer creates a new server in a manner compatible with acceptance testing.
98// In particular, it ensures that the name of the server always starts with "ACPTTEST--",
99// which the delete servers acceptance test relies on to identify servers to delete.
100// Passing in empty image and flavor references will force the use of reasonable defaults.
101// An empty name string will result in a dynamically created name prefixed with "ACPTTEST--".
102// A blank admin password will cause a password to be automatically generated; however,
103// at present no means of recovering this password exists, as no acceptance tests yet require
104// this data.
Samuel A. Falvo II80699602013-07-25 23:35:57 -0700105func createServer(servers gophercloud.CloudServersProvider, imageRef, flavorRef, name, adminPass string) (string, error) {
Samuel A. Falvo II8935ca32013-07-25 21:27:06 -0700106 if imageRef == "" {
107 imageRef = aSuitableImage(servers)
108 }
109
110 if flavorRef == "" {
111 flavorRef = aSuitableFlavor(servers)
112 }
113
114 if len(name) < 1 {
115 name = randomString("ACPTTEST", 16)
116 }
117
118 if (len(name) < 8) || (name[0:8] != "ACPTTEST") {
119 name = fmt.Sprintf("ACPTTEST--%s", name)
120 }
121
Samuel A. Falvo II80699602013-07-25 23:35:57 -0700122 newServer, err := servers.CreateServer(gophercloud.NewServer{
Samuel A. Falvo II8935ca32013-07-25 21:27:06 -0700123 Name: name,
124 ImageRef: imageRef,
125 FlavorRef: flavorRef,
126 AdminPass: adminPass,
127 })
128
Samuel A. Falvo II80699602013-07-25 23:35:57 -0700129 if err != nil {
130 return "", err
131 }
132
133 return newServer.Id, nil
Samuel A. Falvo II8935ca32013-07-25 21:27:06 -0700134}
Samuel A. Falvo II8512e9a2013-07-26 22:53:29 -0700135
136// findAlternativeFlavor locates a flavor to resize a server to. It is guaranteed to be different
137// than what aSuitableFlavor() returns. If none could be found, this function will panic.
138func findAlternativeFlavor() string {
Mark Peekd27e2532013-08-27 07:58:09 -0700139 return "3" // 1GB image, up from 512MB image
Samuel A. Falvo IIf722dbf2013-07-29 15:44:30 -0700140}
141
Samuel A. Falvo II414c15c2013-08-01 15:16:46 -0700142// findAlternativeImage locates an image to resize or rebuild a server with. It is guaranteed to be
143// different than what aSuitableImage() returns. If none could be found, this function will panic.
144func findAlternativeImage() string {
145 return "c6f9c411-e708-4952-91e5-62ded5ea4d3e"
146}
147
Samuel A. Falvo IIf722dbf2013-07-29 15:44:30 -0700148// withIdentity authenticates the user against the provider's identity service, and provides an
149// accessor for additional services.
Samuel A. Falvo II887d7802013-07-29 17:55:37 -0700150func withIdentity(ar bool, f func(gophercloud.AccessProvider)) {
Max Lincoln9d9a8472014-06-25 16:21:28 -0400151 _, _, _, apiKey := getCredentials()
152 if len(apiKey) == 0 {
153 withPasswordIdentity(ar, f)
154 } else {
155 withAPIKeyIdentity(ar, f)
156 }
157}
158
159func withPasswordIdentity(ar bool, f func(gophercloud.AccessProvider)) {
Rafael Garciae4a550e2013-12-06 17:00:32 -0300160 provider, username, password, _ := getCredentials()
Samuel A. Falvo IIf722dbf2013-07-29 15:44:30 -0700161 acc, err := gophercloud.Authenticate(
162 provider,
163 gophercloud.AuthOptions{
Mark Peekd27e2532013-08-27 07:58:09 -0700164 Username: username,
165 Password: password,
Samuel A. Falvo II887d7802013-07-29 17:55:37 -0700166 AllowReauth: ar,
Samuel A. Falvo IIf722dbf2013-07-29 15:44:30 -0700167 },
168 )
169 if err != nil {
170 panic(err)
171 }
172
173 f(acc)
174}
175
Max Lincoln9d9a8472014-06-25 16:21:28 -0400176func withAPIKeyIdentity(ar bool, f func(gophercloud.AccessProvider)) {
177 provider, username, _, apiKey := getCredentials()
178 acc, err := gophercloud.Authenticate(
179 provider,
180 gophercloud.AuthOptions{
181 Username: username,
182 ApiKey: apiKey,
183 AllowReauth: ar,
184 },
185 )
186 if err != nil {
187 panic(err)
188 }
189
190 f(acc)
191}
192
Samuel A. Falvo IIf722dbf2013-07-29 15:44:30 -0700193// withServerApi acquires the cloud servers API.
194func withServerApi(acc gophercloud.AccessProvider, f func(gophercloud.CloudServersProvider)) {
195 api, err := gophercloud.ServersApi(acc, gophercloud.ApiCriteria{
196 Name: "cloudServersOpenStack",
Samuel A. Falvo IIf722dbf2013-07-29 15:44:30 -0700197 VersionId: "2",
198 UrlChoice: gophercloud.PublicURL,
199 })
200 if err != nil {
201 panic(err)
202 }
203
204 f(api)
205}
Samuel A. Falvo II002b6512013-07-29 16:30:40 -0700206
207// waitForServerState polls, every 10 seconds, for a given server to appear in the indicated state.
208// This call will block forever if it never appears in the desired state, so if a timeout is required,
209// make sure to call this function in a goroutine.
210func waitForServerState(api gophercloud.CloudServersProvider, id, state string) error {
211 for {
212 s, err := api.ServerById(id)
213 if err != nil {
214 return err
215 }
216 if s.Status == state {
217 return nil
218 }
219 time.Sleep(10 * time.Second)
220 }
221 panic("Impossible")
Mark Peekd27e2532013-08-27 07:58:09 -0700222}
Mark Peekd2188c42013-08-27 08:16:28 -0700223
224// waitForImageState polls, every 10 seconds, for a given image to appear in the indicated state.
225// This call will block forever if it never appears in the desired state, so if a timeout is required,
226// make sure to call this function in a goroutine.
227func waitForImageState(api gophercloud.CloudServersProvider, id, state string) error {
228 for {
229 s, err := api.ImageById(id)
230 if err != nil {
231 return err
232 }
233 if s.Status == state {
234 return nil
235 }
236 time.Sleep(10 * time.Second)
237 }
238 panic("Impossible")
239}