func TestImagesList(t *testing.T) { client, err := clients.NewComputeV2Client() if err != nil { t.Fatalf("Unable to create a compute: client: %v", err) } allPages, err := images.ListDetail(client, nil).AllPages() if err != nil { t.Fatalf("Unable to retrieve images: %v", err) } allImages, err := images.ExtractImages(allPages) if err != nil { t.Fatalf("Unable to extract image results: %v", err) } for _, image := range allImages { PrintImage(t, image) } }
// spawn achieves the aims of Spawn() func (p *openstackp) spawn(resources *Resources, osPrefix string, flavorID string, externalIP bool, postCreationScript []byte) (serverID string, serverIP string, adminPass string, err error) { // get available images, pick the one that matches desired OS // *** rackspace API lets you filter on eg. os_distro=ubuntu and os_version=12.04; can we do the same here? pager := images.ListDetail(p.computeClient, images.ListOpts{Status: "ACTIVE"}) var imageID string err = pager.EachPage(func(page pagination.Page) (bool, error) { imageList, err := images.ExtractImages(page) if err != nil { return false, err } for _, i := range imageList { if i.Progress == 100 && strings.HasPrefix(i.Name, osPrefix) { imageID = i.ID return false, nil } } return true, nil }) if err != nil { return } // create the server with a unique name server, err := servers.Create(p.computeClient, keypairs.CreateOptsExt{ CreateOptsBuilder: servers.CreateOpts{ Name: uniqueResourceName(resources.ResourceName), FlavorRef: flavorID, ImageRef: imageID, SecurityGroups: []string{p.securityGroup}, Networks: []servers.Network{{UUID: p.networkUUID}}, UserData: postCreationScript, // Metadata map[string]string }, KeyName: resources.ResourceName, }).Extract() if err != nil { return } // wait for it to come up; servers.WaitForStatus has a timeout, but it // doesn't always work, so we roll our own waitForActive := make(chan error) go func() { timeout := time.After(60 * time.Second) ticker := time.NewTicker(1 * time.Second) for { select { case <-ticker.C: current, err := servers.Get(p.computeClient, server.ID).Extract() if err != nil { ticker.Stop() waitForActive <- err return } if current.Status == "ACTIVE" { ticker.Stop() waitForActive <- nil return } if current.Status == "ERROR" { ticker.Stop() waitForActive <- errors.New("there was an error in bringing up the new server") return } continue case <-timeout: ticker.Stop() waitForActive <- errors.New("timed out waiting for server to become ACTIVE") return } } }() err = <-waitForActive if err != nil { // since we're going to return an error that we failed to spawn, try and // delete the bad server in case it is still there servers.Delete(p.computeClient, server.ID) return } // *** NB. it can still take some number of seconds before I can ssh to it serverID = server.ID adminPass = server.AdminPass // get the servers IP; if we error for any reason we'll delete the server // first, because without an IP it's useless if externalIP { // give it a floating ip var floatingIP string floatingIP, err = p.getAvailableFloatingIP() if err != nil { p.destroyServer(serverID) return } // associate floating ip with server *** we have a race condition // between finding/creating free floating IP above, and using it here err = floatingips.AssociateInstance(p.computeClient, serverID, floatingips.AssociateOpts{ FloatingIP: floatingIP, }).ExtractErr() if err != nil { p.destroyServer(serverID) return } serverIP = floatingIP } else { // find its auto-assigned internal ip *** there must be a better way of // doing this... allNetworkAddressPages, serr := servers.ListAddressesByNetwork(p.computeClient, serverID, p.networkName).AllPages() if serr != nil { p.destroyServer(serverID) err = serr return } allNetworkAddresses, serr := servers.ExtractNetworkAddresses(allNetworkAddressPages) if serr != nil { p.destroyServer(serverID) err = serr return } for _, address := range allNetworkAddresses { if address.Version == 4 && strings.HasPrefix(address.Address, "192.168") { serverIP = address.Address break } } } return }
func TestListImages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() th.Mux.HandleFunc("/images/detail", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") r.ParseForm() marker := r.Form.Get("marker") switch marker { case "": fmt.Fprintf(w, ` { "images": [ { "status": "ACTIVE", "updated": "2014-09-23T12:54:56Z", "id": "f3e4a95d-1f4f-4989-97ce-f3a1fb8c04d7", "OS-EXT-IMG-SIZE:size": 476704768, "name": "F17-x86_64-cfntools", "created": "2014-09-23T12:54:52Z", "minDisk": 0, "progress": 100, "minRam": 0 }, { "status": "ACTIVE", "updated": "2014-09-23T12:51:43Z", "id": "f90f6034-2570-4974-8351-6b49732ef2eb", "OS-EXT-IMG-SIZE:size": 13167616, "name": "cirros-0.3.2-x86_64-disk", "created": "2014-09-23T12:51:42Z", "minDisk": 0, "progress": 100, "minRam": 0 } ] } `) case "2": fmt.Fprintf(w, `{ "images": [] }`) default: t.Fatalf("Unexpected marker: [%s]", marker) } }) pages := 0 options := &images.ListOpts{Limit: 2} err := images.ListDetail(fake.ServiceClient(), options).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := images.ExtractImages(page) if err != nil { return false, err } expected := []images.Image{ { ID: "f3e4a95d-1f4f-4989-97ce-f3a1fb8c04d7", Name: "F17-x86_64-cfntools", Created: "2014-09-23T12:54:52Z", Updated: "2014-09-23T12:54:56Z", MinDisk: 0, MinRAM: 0, Progress: 100, Status: "ACTIVE", }, { ID: "f90f6034-2570-4974-8351-6b49732ef2eb", Name: "cirros-0.3.2-x86_64-disk", Created: "2014-09-23T12:51:42Z", Updated: "2014-09-23T12:51:43Z", MinDisk: 0, MinRAM: 0, Progress: 100, Status: "ACTIVE", }, } if !reflect.DeepEqual(expected, actual) { t.Errorf("Unexpected page contents: expected %#v, got %#v", expected, actual) } return false, nil }) if err != nil { t.Fatalf("EachPage error: %v", err) } if pages != 1 { t.Errorf("Expected one page, got %d", pages) } }