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