| 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 | } |