blob: 5891c7f3a22213cda9af5d3835b1f4c82d047b39 [file] [log] [blame]
// +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)
}