|  | // +build acceptance | 
|  |  | 
|  | package tools | 
|  |  | 
|  | import ( | 
|  | "crypto/rand" | 
|  | "fmt" | 
|  | "github.com/rackspace/gophercloud/openstack/compute/servers" | 
|  | identity "github.com/rackspace/gophercloud/openstack/identity/v2" | 
|  | "github.com/rackspace/gophercloud/openstack/utils" | 
|  | "os" | 
|  | "text/tabwriter" | 
|  | "time" | 
|  | ) | 
|  |  | 
|  | var errTimeout = fmt.Errorf("Timeout.") | 
|  |  | 
|  | type testState struct { | 
|  | O              identity.AuthOptions | 
|  | A              identity.AuthResults | 
|  | SC             *identity.ServiceCatalog | 
|  | EPs            []identity.Endpoint | 
|  | W              *tabwriter.Writer | 
|  | ImageId        string | 
|  | FlavorId       string | 
|  | Region         string | 
|  | EP             string | 
|  | Client         *servers.Client | 
|  | CreatedServer  *servers.Server | 
|  | GottenServer   *servers.Server | 
|  | UpdatedServer  *servers.Server | 
|  | ServerName     string | 
|  | AlternateName  string | 
|  | FlavorIdResize string | 
|  | } | 
|  |  | 
|  | func SetupForList(service string) (*testState, error) { | 
|  | var err error | 
|  |  | 
|  | ts := new(testState) | 
|  |  | 
|  | ts.O, err = utils.AuthOptions() | 
|  | if err != nil { | 
|  | return ts, err | 
|  | } | 
|  |  | 
|  | ts.A, err = identity.Authenticate(ts.O) | 
|  | if err != nil { | 
|  | return ts, err | 
|  | } | 
|  |  | 
|  | ts.SC, err = identity.GetServiceCatalog(ts.A) | 
|  | if err != nil { | 
|  | return ts, err | 
|  | } | 
|  |  | 
|  | ts.EPs, err = FindAllEndpoints(ts.SC, service) | 
|  | if err != nil { | 
|  | return ts, err | 
|  | } | 
|  |  | 
|  | ts.W = new(tabwriter.Writer) | 
|  | ts.W.Init(os.Stdout, 2, 8, 2, ' ', 0) | 
|  |  | 
|  | return ts, nil | 
|  | } | 
|  |  | 
|  | func SetupForCRUD() (*testState, error) { | 
|  | ts, err := SetupForList("compute") | 
|  | if err != nil { | 
|  | return ts, err | 
|  | } | 
|  |  | 
|  | ts.ImageId = os.Getenv("OS_IMAGE_ID") | 
|  | if ts.ImageId == "" { | 
|  | return ts, fmt.Errorf("Expected OS_IMAGE_ID environment variable to be set") | 
|  | } | 
|  |  | 
|  | ts.FlavorId = os.Getenv("OS_FLAVOR_ID") | 
|  | if ts.FlavorId == "" { | 
|  | return ts, fmt.Errorf("Expected OS_FLAVOR_ID environment variable to be set") | 
|  | } | 
|  |  | 
|  | ts.FlavorIdResize = os.Getenv("OS_FLAVOR_ID_RESIZE") | 
|  | if ts.FlavorIdResize == "" { | 
|  | return ts, fmt.Errorf("Expected OS_FLAVOR_ID_RESIZE environment variable to be set") | 
|  | } | 
|  |  | 
|  | if ts.FlavorIdResize == ts.FlavorId { | 
|  | return ts, fmt.Errorf("OS_FLAVOR_ID and OS_FLAVOR_ID_RESIZE cannot be the same") | 
|  | } | 
|  |  | 
|  | ts.Region = os.Getenv("OS_REGION_NAME") | 
|  | if ts.Region == "" { | 
|  | ts.Region = ts.EPs[0].Region | 
|  | } | 
|  |  | 
|  | ts.EP, err = FindEndpointForRegion(ts.EPs, ts.Region) | 
|  | if err != nil { | 
|  | return ts, err | 
|  | } | 
|  |  | 
|  | return ts, err | 
|  | } | 
|  |  | 
|  | func FindAllEndpoints(sc *identity.ServiceCatalog, service string) ([]identity.Endpoint, error) { | 
|  | ces, err := sc.CatalogEntries() | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  |  | 
|  | for _, ce := range ces { | 
|  | if ce.Type == service { | 
|  | return ce.Endpoints, nil | 
|  | } | 
|  | } | 
|  |  | 
|  | return nil, fmt.Errorf(service + " endpoint not found.") | 
|  | } | 
|  |  | 
|  | func FindEndpointForRegion(eps []identity.Endpoint, r string) (string, error) { | 
|  | for _, ep := range eps { | 
|  | if ep.Region == r { | 
|  | return ep.PublicURL, nil | 
|  | } | 
|  | } | 
|  | return "", fmt.Errorf("Unknown region %s", r) | 
|  | } | 
|  |  | 
|  | func CountDown(ts *testState, timeout int) (bool, int, error) { | 
|  | if timeout < 1 { | 
|  | return false, 0, errTimeout | 
|  | } | 
|  | time.Sleep(1 * time.Second) | 
|  | timeout-- | 
|  |  | 
|  | gr, err := servers.GetDetail(ts.Client, ts.CreatedServer.Id) | 
|  | if err != nil { | 
|  | return false, timeout, err | 
|  | } | 
|  |  | 
|  | ts.GottenServer, err = servers.GetServer(gr) | 
|  | if err != nil { | 
|  | return false, timeout, err | 
|  | } | 
|  |  | 
|  | return true, timeout, nil | 
|  | } | 
|  |  | 
|  | func CreateServer(ts *testState) error { | 
|  | ts.ServerName = RandomString("ACPTTEST", 16) | 
|  | fmt.Printf("Attempting to create server: %s\n", ts.ServerName) | 
|  |  | 
|  | ts.Client = servers.NewClient(ts.EP, ts.A, ts.O) | 
|  |  | 
|  | cr, err := servers.Create(ts.Client, map[string]interface{}{ | 
|  | "flavorRef": ts.FlavorId, | 
|  | "imageRef":  ts.ImageId, | 
|  | "name":      ts.ServerName, | 
|  | }) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | ts.CreatedServer, err = servers.GetServer(cr) | 
|  | return err | 
|  | } | 
|  |  | 
|  | func WaitForStatus(ts *testState, s string) error { | 
|  | var ( | 
|  | inProgress bool | 
|  | timeout    int | 
|  | err        error | 
|  | ) | 
|  |  | 
|  | for inProgress, timeout, err = CountDown(ts, 300); inProgress; inProgress, timeout, err = CountDown(ts, timeout) { | 
|  | if ts.GottenServer.Id != ts.CreatedServer.Id { | 
|  | return fmt.Errorf("created server id (%s) != gotten server id (%s)", ts.CreatedServer.Id, ts.GottenServer.Id) | 
|  | } | 
|  |  | 
|  | if ts.GottenServer.Status == s { | 
|  | fmt.Printf("Server reached state %s after %d seconds (approximately)\n", s, 300-timeout) | 
|  | break | 
|  | } | 
|  | } | 
|  |  | 
|  | if err == errTimeout { | 
|  | fmt.Printf("Time out -- I'm not waiting around.\n") | 
|  | err = nil | 
|  | } | 
|  |  | 
|  | return err | 
|  | } | 
|  |  | 
|  | func ChangeServerName(ts *testState) error { | 
|  | var ( | 
|  | inProgress bool | 
|  | timeout    int | 
|  | ) | 
|  |  | 
|  | ts.AlternateName = RandomString("ACPTTEST", 16) | 
|  | for ts.AlternateName == ts.ServerName { | 
|  | ts.AlternateName = RandomString("ACPTTEST", 16) | 
|  | } | 
|  | fmt.Println("Attempting to change server name") | 
|  |  | 
|  | ur, err := servers.Update(ts.Client, ts.CreatedServer.Id, map[string]interface{}{ | 
|  | "name": ts.AlternateName, | 
|  | }) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | ts.UpdatedServer, err = servers.GetServer(ur) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | if ts.UpdatedServer.Id != ts.CreatedServer.Id { | 
|  | return fmt.Errorf("Expected updated and created server to share the same ID") | 
|  | } | 
|  |  | 
|  | for inProgress, timeout, err = CountDown(ts, 300); inProgress; inProgress, timeout, err = CountDown(ts, timeout) { | 
|  | if ts.GottenServer.Id != ts.UpdatedServer.Id { | 
|  | return fmt.Errorf("Updated server ID (%s) != gotten server ID (%s)", ts.UpdatedServer.Id, ts.GottenServer.Id) | 
|  | } | 
|  |  | 
|  | if ts.GottenServer.Name == ts.AlternateName { | 
|  | fmt.Printf("Server updated after %d seconds (approximately)\n", 300-timeout) | 
|  | break | 
|  | } | 
|  | } | 
|  |  | 
|  | if err == errTimeout { | 
|  | fmt.Printf("I'm not waiting around.\n") | 
|  | err = nil | 
|  | } | 
|  |  | 
|  | return err | 
|  | } | 
|  |  | 
|  | func MakeNewPassword(oldPass string) string { | 
|  | fmt.Println("Current password: " + oldPass) | 
|  | randomPassword := RandomString("", 16) | 
|  | for randomPassword == oldPass { | 
|  | randomPassword = RandomString("", 16) | 
|  | } | 
|  | fmt.Println("    New password: " + randomPassword) | 
|  | return randomPassword | 
|  | } | 
|  |  | 
|  | func ChangeAdminPassword(ts *testState) error { | 
|  | randomPassword := MakeNewPassword(ts.CreatedServer.AdminPass) | 
|  |  | 
|  | err := servers.ChangeAdminPassword(ts.Client, ts.CreatedServer.Id, randomPassword) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | err = WaitForStatus(ts, "PASSWORD") | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | return WaitForStatus(ts, "ACTIVE") | 
|  | } | 
|  |  | 
|  | func RebootServer(ts *testState) error { | 
|  | fmt.Println("Attempting reboot of server " + ts.CreatedServer.Id) | 
|  | err := servers.Reboot(ts.Client, ts.CreatedServer.Id, servers.OSReboot) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | err = WaitForStatus(ts, "REBOOT") | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | return WaitForStatus(ts, "ACTIVE") | 
|  | } | 
|  |  | 
|  | func RebuildServer(ts *testState) error { | 
|  | fmt.Println("Attempting to rebuild server " + ts.CreatedServer.Id) | 
|  |  | 
|  | newPassword := MakeNewPassword(ts.CreatedServer.AdminPass) | 
|  | newName := RandomString("ACPTTEST", 16) | 
|  | sr, err := servers.Rebuild(ts.Client, ts.CreatedServer.Id, newName, newPassword, ts.ImageId, nil) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | s, err := servers.GetServer(sr) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | if s.Id != ts.CreatedServer.Id { | 
|  | return fmt.Errorf("Expected rebuilt server ID of %s; got %s", ts.CreatedServer.Id, s.Id) | 
|  | } | 
|  |  | 
|  | err = WaitForStatus(ts, "REBUILD") | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | return WaitForStatus(ts, "ACTIVE") | 
|  | } | 
|  |  | 
|  | func ResizeServer(ts *testState) error { | 
|  | fmt.Println("Attempting to resize server " + ts.CreatedServer.Id) | 
|  |  | 
|  | err := servers.Resize(ts.Client, ts.CreatedServer.Id, ts.FlavorIdResize) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | err = WaitForStatus(ts, "RESIZE") | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | return WaitForStatus(ts, "VERIFY_RESIZE") | 
|  | } | 
|  |  | 
|  | func ConfirmResize(ts *testState) error { | 
|  | fmt.Println("Attempting to confirm resize for server " + ts.CreatedServer.Id) | 
|  |  | 
|  | err := servers.ConfirmResize(ts.Client, ts.CreatedServer.Id) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | return WaitForStatus(ts, "ACTIVE") | 
|  | } | 
|  |  | 
|  | func RevertResize(ts *testState) error { | 
|  | fmt.Println("Attempting to revert resize for server " + ts.CreatedServer.Id) | 
|  |  | 
|  | err := servers.RevertResize(ts.Client, ts.CreatedServer.Id) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | err = WaitForStatus(ts, "REVERT_RESIZE") | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | return WaitForStatus(ts, "ACTIVE") | 
|  | } | 
|  |  | 
|  | // randomString generates a string of given length, but random content. | 
|  | // All content will be within the ASCII graphic character set. | 
|  | // (Implementation from Even Shaw's contribution on | 
|  | // http://stackoverflow.com/questions/12771930/what-is-the-fastest-way-to-generate-a-long-random-string-in-go). | 
|  | func RandomString(prefix string, n int) string { | 
|  | const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" | 
|  | var bytes = make([]byte, n) | 
|  | rand.Read(bytes) | 
|  | for i, b := range bytes { | 
|  | bytes[i] = alphanum[b%byte(len(alphanum))] | 
|  | } | 
|  | return prefix + string(bytes) | 
|  | } |