Samuel A. Falvo II | b5d93f2 | 2014-02-21 15:00:20 -0800 | [diff] [blame^] | 1 | // +build acceptance |
| 2 | |
Samuel A. Falvo II | 0abdb10 | 2014-02-13 23:19:26 -0800 | [diff] [blame] | 3 | package openstack |
| 4 | |
| 5 | import ( |
Samuel A. Falvo II | e246ac0 | 2014-02-13 23:20:09 -0800 | [diff] [blame] | 6 | "crypto/rand" |
Samuel A. Falvo II | 0abdb10 | 2014-02-13 23:19:26 -0800 | [diff] [blame] | 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" |
Samuel A. Falvo II | 0abdb10 | 2014-02-13 23:19:26 -0800 | [diff] [blame] | 14 | ) |
| 15 | |
| 16 | var errTimeout = fmt.Errorf("Timeout.") |
| 17 | |
| 18 | type testState struct { |
Samuel A. Falvo II | e246ac0 | 2014-02-13 23:20:09 -0800 | [diff] [blame] | 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 |
Samuel A. Falvo II | 0abdb10 | 2014-02-13 23:19:26 -0800 | [diff] [blame] | 29 | createdServer *servers.Server |
Samuel A. Falvo II | e246ac0 | 2014-02-13 23:20:09 -0800 | [diff] [blame] | 30 | gottenServer *servers.Server |
Samuel A. Falvo II | 0abdb10 | 2014-02-13 23:19:26 -0800 | [diff] [blame] | 31 | updatedServer *servers.Server |
Samuel A. Falvo II | e246ac0 | 2014-02-13 23:20:09 -0800 | [diff] [blame] | 32 | serverName string |
Samuel A. Falvo II | 0abdb10 | 2014-02-13 23:19:26 -0800 | [diff] [blame] | 33 | alternateName string |
| 34 | } |
| 35 | |
| 36 | func setupForList() (*testState, error) { |
| 37 | var err error |
| 38 | |
| 39 | ts := new(testState) |
| 40 | |
| 41 | ts.o, err = utils.AuthOptions() |
| 42 | if err != nil { |
| 43 | return ts, err |
| 44 | } |
| 45 | |
| 46 | ts.a, err = identity.Authenticate(ts.o) |
| 47 | if err != nil { |
| 48 | return ts, err |
| 49 | } |
| 50 | |
| 51 | ts.sc, err = identity.GetServiceCatalog(ts.a) |
| 52 | if err != nil { |
| 53 | return ts, err |
| 54 | } |
| 55 | |
| 56 | ts.eps, err = findAllComputeEndpoints(ts.sc) |
| 57 | if err != nil { |
| 58 | return ts, err |
| 59 | } |
| 60 | |
| 61 | ts.w = new(tabwriter.Writer) |
| 62 | ts.w.Init(os.Stdout, 2, 8, 2, ' ', 0) |
| 63 | |
| 64 | return ts, nil |
| 65 | } |
| 66 | |
| 67 | func setupForCRUD() (*testState, error) { |
| 68 | ts, err := setupForList() |
| 69 | if err != nil { |
| 70 | return ts, err |
| 71 | } |
| 72 | |
| 73 | ts.imageId = os.Getenv("OS_IMAGE_ID") |
| 74 | if ts.imageId == "" { |
| 75 | return ts, fmt.Errorf("Expected OS_IMAGE_ID environment variable to be set") |
| 76 | } |
| 77 | |
| 78 | ts.flavorId = os.Getenv("OS_FLAVOR_ID") |
| 79 | if ts.flavorId == "" { |
| 80 | return ts, fmt.Errorf("Expected OS_FLAVOR_ID environment variable to be set") |
| 81 | } |
| 82 | |
| 83 | ts.region = os.Getenv("OS_REGION_NAME") |
| 84 | if ts.region == "" { |
| 85 | ts.region = ts.eps[0].Region |
| 86 | } |
| 87 | |
| 88 | ts.ep, err = findEndpointForRegion(ts.eps, ts.region) |
| 89 | if err != nil { |
| 90 | return ts, err |
| 91 | } |
| 92 | |
| 93 | return ts, err |
| 94 | } |
| 95 | |
| 96 | func findAllComputeEndpoints(sc *identity.ServiceCatalog) ([]identity.Endpoint, error) { |
| 97 | ces, err := sc.CatalogEntries() |
| 98 | if err != nil { |
| 99 | return nil, err |
| 100 | } |
| 101 | |
| 102 | for _, ce := range ces { |
| 103 | if ce.Type == "compute" { |
| 104 | return ce.Endpoints, nil |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | return nil, fmt.Errorf("Compute endpoint not found.") |
| 109 | } |
| 110 | |
| 111 | func findEndpointForRegion(eps []identity.Endpoint, r string) (string, error) { |
| 112 | for _, ep := range eps { |
| 113 | if ep.Region == r { |
| 114 | return ep.PublicURL, nil |
| 115 | } |
| 116 | } |
| 117 | return "", fmt.Errorf("Unknown region %s", r) |
| 118 | } |
| 119 | |
| 120 | func countDown(ts *testState, timeout int) (bool, int, error) { |
| 121 | if timeout < 1 { |
| 122 | return false, 0, errTimeout |
| 123 | } |
| 124 | time.Sleep(1 * time.Second) |
| 125 | timeout-- |
| 126 | |
| 127 | gr, err := servers.GetDetail(ts.client, ts.createdServer.Id) |
| 128 | if err != nil { |
| 129 | return false, timeout, err |
| 130 | } |
| 131 | |
| 132 | ts.gottenServer, err = servers.GetServer(gr) |
| 133 | if err != nil { |
| 134 | return false, timeout, err |
| 135 | } |
| 136 | |
| 137 | return true, timeout, nil |
| 138 | } |
| 139 | |
| 140 | func createServer(ts *testState) error { |
| 141 | ts.serverName = randomString("ACPTTEST", 16) |
| 142 | fmt.Printf("Attempting to create server: %s\n", ts.serverName) |
| 143 | |
| 144 | ts.client = servers.NewClient(ts.ep, ts.a, ts.o) |
| 145 | |
| 146 | cr, err := servers.Create(ts.client, map[string]interface{}{ |
| 147 | "flavorRef": ts.flavorId, |
Samuel A. Falvo II | e246ac0 | 2014-02-13 23:20:09 -0800 | [diff] [blame] | 148 | "imageRef": ts.imageId, |
| 149 | "name": ts.serverName, |
Samuel A. Falvo II | 0abdb10 | 2014-02-13 23:19:26 -0800 | [diff] [blame] | 150 | }) |
| 151 | if err != nil { |
| 152 | return err |
| 153 | } |
| 154 | |
| 155 | ts.createdServer, err = servers.GetServer(cr) |
| 156 | return err |
| 157 | } |
| 158 | |
| 159 | func waitForStatus(ts *testState, s string) error { |
| 160 | var ( |
| 161 | inProgress bool |
Samuel A. Falvo II | e246ac0 | 2014-02-13 23:20:09 -0800 | [diff] [blame] | 162 | timeout int |
| 163 | err error |
Samuel A. Falvo II | 0abdb10 | 2014-02-13 23:19:26 -0800 | [diff] [blame] | 164 | ) |
| 165 | |
Samuel A. Falvo II | e246ac0 | 2014-02-13 23:20:09 -0800 | [diff] [blame] | 166 | for inProgress, timeout, err = countDown(ts, 300); inProgress; inProgress, timeout, err = countDown(ts, timeout) { |
Samuel A. Falvo II | 0abdb10 | 2014-02-13 23:19:26 -0800 | [diff] [blame] | 167 | if ts.gottenServer.Id != ts.createdServer.Id { |
| 168 | return fmt.Errorf("created server id (%s) != gotten server id (%s)", ts.createdServer.Id, ts.gottenServer.Id) |
| 169 | } |
| 170 | |
| 171 | if ts.gottenServer.Status == s { |
| 172 | fmt.Printf("Server created after %d seconds (approximately)\n", 300-timeout) |
| 173 | break |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | if err == errTimeout { |
| 178 | fmt.Printf("I'm not waiting around.\n") |
| 179 | err = nil |
| 180 | } |
| 181 | |
| 182 | return err |
| 183 | } |
| 184 | |
| 185 | func changeServerName(ts *testState) error { |
| 186 | var ( |
| 187 | inProgress bool |
Samuel A. Falvo II | e246ac0 | 2014-02-13 23:20:09 -0800 | [diff] [blame] | 188 | timeout int |
Samuel A. Falvo II | 0abdb10 | 2014-02-13 23:19:26 -0800 | [diff] [blame] | 189 | ) |
| 190 | |
| 191 | ts.alternateName = randomString("ACPTTEST", 16) |
| 192 | for ts.alternateName == ts.serverName { |
| 193 | ts.alternateName = randomString("ACPTTEST", 16) |
| 194 | } |
| 195 | fmt.Println("Attempting to change server name") |
| 196 | |
| 197 | ur, err := servers.Update(ts.client, ts.createdServer.Id, map[string]interface{}{ |
| 198 | "name": ts.alternateName, |
| 199 | }) |
| 200 | if err != nil { |
| 201 | return err |
| 202 | } |
| 203 | |
| 204 | ts.updatedServer, err = servers.GetServer(ur) |
| 205 | if err != nil { |
| 206 | return err |
| 207 | } |
| 208 | |
| 209 | if ts.updatedServer.Id != ts.createdServer.Id { |
| 210 | return fmt.Errorf("Expected updated and created server to share the same ID") |
| 211 | } |
| 212 | |
Samuel A. Falvo II | e246ac0 | 2014-02-13 23:20:09 -0800 | [diff] [blame] | 213 | for inProgress, timeout, err = countDown(ts, 300); inProgress; inProgress, timeout, err = countDown(ts, timeout) { |
Samuel A. Falvo II | 0abdb10 | 2014-02-13 23:19:26 -0800 | [diff] [blame] | 214 | if ts.gottenServer.Id != ts.updatedServer.Id { |
| 215 | return fmt.Errorf("Updated server ID (%s) != gotten server ID (%s)", ts.updatedServer.Id, ts.gottenServer.Id) |
| 216 | } |
| 217 | |
| 218 | if ts.gottenServer.Name == ts.alternateName { |
| 219 | fmt.Printf("Server updated after %d seconds (approximately)\n", 300-timeout) |
| 220 | break |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | if err == errTimeout { |
| 225 | fmt.Printf("I'm not waiting around.\n") |
| 226 | err = nil |
| 227 | } |
| 228 | |
| 229 | return err |
| 230 | } |
| 231 | |
| 232 | // randomString generates a string of given length, but random content. |
| 233 | // All content will be within the ASCII graphic character set. |
| 234 | // (Implementation from Even Shaw's contribution on |
| 235 | // http://stackoverflow.com/questions/12771930/what-is-the-fastest-way-to-generate-a-long-random-string-in-go). |
| 236 | func randomString(prefix string, n int) string { |
| 237 | const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" |
| 238 | var bytes = make([]byte, n) |
| 239 | rand.Read(bytes) |
| 240 | for i, b := range bytes { |
| 241 | bytes[i] = alphanum[b%byte(len(alphanum))] |
| 242 | } |
| 243 | return prefix + string(bytes) |
| 244 | } |