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