Commit 39f3015b authored by dma's avatar dma

Merge branch 'master' of github.com:GoogleCloudPlatform/kubernetes

parents 98b1c7c7 a99bb482
...@@ -152,8 +152,8 @@ ...@@ -152,8 +152,8 @@
}, },
{ {
"ImportPath": "github.com/rackspace/gophercloud", "ImportPath": "github.com/rackspace/gophercloud",
"Comment": "v0.1.0-31-ge13cda2", "Comment": "v1.0.0",
"Rev": "e13cda260ce48d63ce816f4fa72b6c6cd096596d" "Rev": "da56de6a59e53fdd61be1b5d9b87df34c47ac420"
}, },
{ {
"ImportPath": "github.com/skratchdot/open-golang/open", "ImportPath": "github.com/skratchdot/open-golang/open",
......
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
# All files
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
trim_trailing_whitespace = true
# Golang
[*.go]
indent_style = tab
indent_size = 2
language: go language: go
install: install:
- go get -v . - go get -v -tags 'fixtures acceptance' ./...
go: go:
- 1.1 - 1.1
- 1.2 - 1.2
- tip - tip
script: script/cibuild
after_success: after_success:
- go get code.google.com/p/go.tools/cmd/cover - go get code.google.com/p/go.tools/cmd/cover
- go get github.com/axw/gocov/gocov - go get github.com/axw/gocov/gocov
- go get github.com/mattn/goveralls - go get github.com/mattn/goveralls
- export PATH=$PATH:$HOME/gopath/bin/ - export PATH=$PATH:$HOME/gopath/bin/
- goveralls 2k7PTU3xa474Hymwgdj6XjqenNfGTNkO8 - goveralls 2k7PTU3xa474Hymwgdj6XjqenNfGTNkO8
Contributors Contributors
============ ============
Samuel A. Falvo II <sam.falvo@rackspace.com> | Name | Email |
Glen Campbell <glen.campbell@rackspace.com> | ---- | ----- |
Jesse Noller <jesse.noller@rackspace.com> | Samuel A. Falvo II | <sam.falvo@rackspace.com>
| Glen Campbell | <glen.campbell@rackspace.com>
| Jesse Noller | <jesse.noller@rackspace.com>
| Jon Perritt | <jon.perritt@rackspace.com>
| Ash Wilson | <ash.wilson@rackspace.com>
| Jamie Hannaford | <jamie.hannaford@rackspace.com>
| Don Schenck | don.schenck@rackspace.com>
== Gophercloud -- V0.1.0 image:https://secure.travis-ci.org/rackspace/gophercloud.png?branch=master["build status",link="https://travis-ci.org/rackspace/gophercloud"]
Gophercloud currently lets you authenticate with OpenStack providers to create and manage servers.
We are working on extending the API to further include cloud files, block storage, DNS, databases, security groups, and other features.
WARNING: This library is still in the very early stages of development. Unless you want to contribute, it probably isn't what you want. Yet.
=== Outstanding Features
1. Apache 2.0 License, making Gophercloud friendly to commercial and open-source enterprises alike.
2. Gophercloud is one of the most actively maintained Go SDKs for OpenStack.
3. Gophercloud supports Identity V2 and Nova V2 APIs. More coming soon!
4. The up-coming Gophercloud 0.2.0 release supports API extensions, and makes writing support for new extensions easy.
5. Gophercloud supports automatic reauthentication upon auth token timeout, if enabled by your software.
6. Gophercloud is the only SDK implementation with actual acceptance-level integration tests.
=== What Does it Look Like?
The Gophercloud 0.1.0 and earlier APIs are now deprecated and obsolete.
No new feature development will occur for 0.1.0 or 0.0.0.
However, we will accept and provide bug fixes for these APIs.
Please refer to the acceptance tests in the master brach for code examples using the v0.1.0 API.
The most up to date documentation for version 0.1.x can be found at link:http://godoc.org/github.com/rackspace/gophercloud[our Godoc.org documentation].
We are working on a new API that provides much better support for extensions, pagination, and other features that proved difficult to implement before.
This new API will be substantially more Go-idiomatic as well; one of the complaints received about 0.1.x and earlier is that it didn't "feel" right.
To see what this new API is going to look like, you can look at the code examples up on the link:http://gophercloud.io/docs.html[Gophercloud website].
If you're interested in tracking progress, note that features for version 0.2.0 will appear in the `v0.2.0` branch until merged to master.
=== How can I Contribute?
After using Gophercloud for a while, you might find that it lacks some useful feature, or that existing behavior seems buggy. We welcome contributions
from our users for both missing functionality as well as for bug fixes. We encourage contributors to collaborate with the
link:http://gophercloud.io/community.html[Gophercloud community.]
Finally, Gophercloud maintains its own link:http://gophercloud.io[announcements and updates blog.]
Feel free to check back now and again to see what's new.
== License
Copyright (C) 2013, 2014 Rackspace, Inc.
Licensed under the Apache License, Version 2.0
# Gophercloud: the OpenStack SDK for Go
[![Build Status](https://travis-ci.org/rackspace/gophercloud.svg?branch=master)](https://travis-ci.org/rackspace/gophercloud)
Gophercloud is a flexible SDK that allows you to consume and work with OpenStack
clouds in a simple and idiomatic way using golang. Many services are supported,
including Compute, Block Storage, Object Storage, Networking, and Identity.
Each service API is backed with getting started guides, code samples, reference
documentation, unit tests and acceptance tests.
## Useful links
* [Gophercloud homepage](http://gophercloud.io)
* [Reference documentation](http://godoc.org/github.com/rackspace/gophercloud)
* [Getting started guides](http://gophercloud.io/docs)
* [Effective Go](https://golang.org/doc/effective_go.html)
## How to install
Before installing, you need to ensure that your [GOPATH environment variable](https://golang.org/doc/code.html#GOPATH)
is pointing to an appropriate directory where you want to install Gophercloud:
```bash
mkdir $HOME/go
export GOPATH=$HOME/go
```
To protect yourself against changes in your dependencies, we highly recommend choosing a
[dependency management solution](https://code.google.com/p/go-wiki/wiki/PackageManagementTools) for
your projects, such as [godep](https://github.com/tools/godep). Once this is set up, you can install
Gophercloud as a dependency like so:
```bash
go get github.com/rackspace/gophercloud
# Edit your code to import relevant packages from "github.com/rackspace/gophercloud"
godep save ./...
```
This will install all the source files you need into a `Godeps/_workspace` directory, which is
referenceable from your own source files when you use the `godep go` command.
## Getting started
### Credentials
Because you'll be hitting an API, you will need to retrieve your OpenStack
credentials and either store them as environment variables or in your local Go
files. The first method is recommended because it decouples credential
information from source code, allowing you to push the latter to your version
control system without any security risk.
You will need to retrieve the following:
* username
* password
* tenant name or tenant ID
* a valid Keystone identity URL
For users that have the OpenStack dashboard installed, there's a shortcut. If
you visit the `project/access_and_security` path in Horizon and click on the
"Download OpenStack RC File" button at the top right hand corner, you will
download a bash file that exports all of your access details to environment
variables. To execute the file, run `source admin-openrc.sh` and you will be
prompted for your password.
### Authentication
Once you have access to your credentials, you can begin plugging them into
Gophercloud. The next step is authentication, and this is handled by a base
"Provider" struct. To get one, you can either pass in your credentials
explicitly, or tell Gophercloud to use environment variables:
```go
import (
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack"
"github.com/rackspace/gophercloud/openstack/utils"
)
// Option 1: Pass in the values yourself
opts := gophercloud.AuthOptions{
IdentityEndpoint: "https://my-openstack.com:5000/v2.0",
Username: "{username}",
Password: "{password}",
TenantID: "{tenant_id}",
}
// Option 2: Use a utility function to retrieve all your environment variables
opts, err := openstack.AuthOptionsFromEnv()
```
Once you have the `opts` variable, you can pass it in and get back a
`ProviderClient` struct:
```go
provider, err := openstack.AuthenticatedClient(opts)
```
The `ProviderClient` is the top-level client that all of your OpenStack services
derive from. The provider contains all of the authentication details that allow
your Go code to access the API - such as the base URL and token ID.
### Provision a server
Once we have a base Provider, we inject it as a dependency into each OpenStack
service. In order to work with the Compute API, we need a Compute service
client; which can be created like so:
```go
client, err := openstack.NewComputeV2(provider, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
```
We then use this `client` for any Compute API operation we want. In our case,
we want to provision a new server - so we invoke the `Create` method and pass
in the flavor ID (hardware specification) and image ID (operating system) we're
interested in:
```go
import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
server, err := servers.Create(client, servers.CreateOpts{
Name: "My new server!",
FlavorRef: "flavor_id",
ImageRef: "image_id",
}).Extract()
```
If you are unsure about what images and flavors are, you can read our [Compute
Getting Started guide](http://gophercloud.io/docs/compute). The above code
sample creates a new server with the parameters, and embodies the new resource
in the `server` variable (a
[`servers.Server`](http://godoc.org/github.com/rackspace/gophercloud) struct).
### Next steps
Cool! You've handled authentication, got your `ProviderClient` and provisioned
a new server. You're now ready to use more OpenStack services.
* [Getting started with Compute](http://gophercloud.io/docs/compute)
* [Getting started with Object Storage](http://gophercloud.io/docs/object-storage)
* [Getting started with Networking](http://gophercloud.io/docs/networking)
* [Getting started with Block Storage](http://gophercloud.io/docs/block-storage)
* [Getting started with Identity](http://gophercloud.io/docs/identity)
## Contributing
Engaging the community and lowering barriers for contributors is something we
care a lot about. For this reason, we've taken the time to write a [contributing
guide](./CONTRIBUTING.md) for folks interested in getting involved in our project.
If you're not sure how you can get involved, feel free to submit an issue or
[e-mail us](mailto:sdk-support@rackspace.com) privately. You don't need to be a
Go expert - all members of the community are welcome!
## Help and feedback
If you're struggling with something or have spotted a potential bug, feel free
to submit an issue to our [bug tracker](/issues) or e-mail us directly at
[sdk-support@rackspace.com](mailto:sdk-support@rackspace.com).
# Upgrading to v1.0.0
With the arrival of this new major version increment, the unfortunate news is
that breaking changes have been introduced to existing services. The API
has been completely rewritten from the ground up to make the library more
extensible, maintainable and easy-to-use.
Below we've compiled upgrade instructions for the various services that
existed before. If you have a specific issue that is not addressed below,
please [submit an issue](/issues/new) or
[e-mail our support team](mailto:sdk-support@rackspace.com).
* [Authentication](#authentication)
* [Servers](#servers)
* [List servers](#list-servers)
* [Get server details](#get-server-details)
* [Create server](#create-server)
* [Resize server](#resize-server)
* [Reboot server](#reboot-server)
* [Update server](#update-server)
* [Rebuild server](#rebuild-server)
* [Change admin password](#change-admin-password)
* [Delete server](#delete-server)
* [Rescue server](#rescue-server)
* [Images and flavors](#images-and-flavors)
* [List images](#list-images)
* [List flavors](#list-flavors)
* [Create/delete image](#createdelete-image)
* [Other](#other)
* [List keypairs](#list-keypairs)
* [Create/delete keypair](#createdelete-keypair)
* [List IP addresses](#list-ip-addresses)
# Authentication
One of the major differences that this release introduces is the level of
sub-packaging to differentiate between services and providers. You now have
the option of authenticating with OpenStack and other providers (like Rackspace).
To authenticate with a vanilla OpenStack installation, you can either specify
your credentials like this:
```go
import (
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack"
)
opts := gophercloud.AuthOptions{
IdentityEndpoint: "https://my-openstack.com:5000/v2.0",
Username: "{username}",
Password: "{password}",
TenantID: "{tenant_id}",
}
```
Or have them pulled in through environment variables, like this:
```go
opts, err := openstack.AuthOptionsFromEnv()
```
Once you have your `AuthOptions` struct, you pass it in to get back a `Provider`,
like so:
```go
provider, err := openstack.AuthenticatedClient(opts)
```
This provider is the top-level structure that all services are created from.
# Servers
Before you can interact with the Compute API, you need to retrieve a
`gophercloud.ServiceClient`. To do this:
```go
// Define your region, etc.
opts := gophercloud.EndpointOpts{Region: "RegionOne"}
client, err := openstack.NewComputeV2(provider, opts)
```
## List servers
All operations that involve API collections (servers, flavors, images) now use
the `pagination.Pager` interface. This interface represents paginated entities
that can be iterated over.
Once you have a Pager, you can then pass a callback function into its `EachPage`
method, and this will allow you to traverse over the collection and execute
arbitrary functionality. So, an example with list servers:
```go
import (
"fmt"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
)
// We have the option of filtering the server list. If we want the full
// collection, leave it as an empty struct or nil
opts := servers.ListOpts{Name: "server_1"}
// Retrieve a pager (i.e. a paginated collection)
pager := servers.List(client, opts)
// Define an anonymous function to be executed on each page's iteration
err := pager.EachPage(func(page pagination.Page) (bool, error) {
serverList, err := servers.ExtractServers(page)
// `s' will be a servers.Server struct
for _, s := range serverList {
fmt.Printf("We have a server. ID=%s, Name=%s", s.ID, s.Name)
}
})
```
## Get server details
```go
import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
// Get the HTTP result
response := servers.Get(client, "server_id")
// Extract a Server struct from the response
server, err := response.Extract()
```
## Create server
```go
import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
// Define our options
opts := servers.CreateOpts{
Name: "new_server",
FlavorRef: "flavorID",
ImageRef: "imageID",
}
// Get our response
response := servers.Create(client, opts)
// Extract
server, err := response.Extract()
```
## Change admin password
```go
import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
result := servers.ChangeAdminPassword(client, "server_id", "newPassword_&123")
```
## Resize server
```go
import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
result := servers.Resize(client, "server_id", "new_flavor_id")
```
## Reboot server
```go
import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
// You have a choice of two reboot methods: servers.SoftReboot or servers.HardReboot
result := servers.Reboot(client, "server_id", servers.SoftReboot)
```
## Update server
```go
import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
opts := servers.UpdateOpts{Name: "new_name"}
server, err := servers.Update(client, "server_id", opts).Extract()
```
## Rebuild server
```go
import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
// You have the option of specifying additional options
opts := RebuildOpts{
Name: "new_name",
AdminPass: "admin_password",
ImageID: "image_id",
Metadata: map[string]string{"owner": "me"},
}
result := servers.Rebuild(client, "server_id", opts)
// You can extract a servers.Server struct from the HTTP response
server, err := result.Extract()
```
## Delete server
```go
import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
response := servers.Delete(client, "server_id")
```
## Rescue server
The server rescue extension for Compute is not currently supported.
# Images and flavors
## List images
As with listing servers (see above), you first retrieve a Pager, and then pass
in a callback over each page:
```go
import (
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/openstack/compute/v2/images"
)
// We have the option of filtering the image list. If we want the full
// collection, leave it as an empty struct
opts := images.ListOpts{ChangesSince: "2014-01-01T01:02:03Z", Name: "Ubuntu 12.04"}
// Retrieve a pager (i.e. a paginated collection)
pager := images.List(client, opts)
// Define an anonymous function to be executed on each page's iteration
err := pager.EachPage(func(page pagination.Page) (bool, error) {
imageList, err := images.ExtractImages(page)
for _, i := range imageList {
// "i" will be a images.Image
}
})
```
## List flavors
```go
import (
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/openstack/compute/v2/flavors"
)
// We have the option of filtering the flavor list. If we want the full
// collection, leave it as an empty struct
opts := flavors.ListOpts{ChangesSince: "2014-01-01T01:02:03Z", MinRAM: 4}
// Retrieve a pager (i.e. a paginated collection)
pager := flavors.List(client, opts)
// Define an anonymous function to be executed on each page's iteration
err := pager.EachPage(func(page pagination.Page) (bool, error) {
flavorList, err := networks.ExtractFlavors(page)
for _, f := range flavorList {
// "f" will be a flavors.Flavor
}
})
```
## Create/delete image
Image management has been shifted to Glance, but unfortunately this service is
not supported as of yet. You can, however, list Compute images like so:
```go
import "github.com/rackspace/gophercloud/openstack/compute/v2/images"
// Retrieve a pager (i.e. a paginated collection)
pager := images.List(client, opts)
// Define an anonymous function to be executed on each page's iteration
err := pager.EachPage(func(page pagination.Page) (bool, error) {
imageList, err := images.ExtractImages(page)
for _, i := range imageList {
// "i" will be a images.Image
}
})
```
# Other
## List keypairs
```go
import "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
// Retrieve a pager (i.e. a paginated collection)
pager := keypairs.List(client, opts)
// Define an anonymous function to be executed on each page's iteration
err := pager.EachPage(func(page pagination.Page) (bool, error) {
keyList, err := keypairs.ExtractKeyPairs(page)
for _, k := range keyList {
// "k" will be a keypairs.KeyPair
}
})
```
## Create/delete keypairs
To create a new keypair, you need to specify its name and, optionally, a
pregenerated OpenSSH-formatted public key.
```go
import "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
opts := keypairs.CreateOpts{
Name: "new_key",
PublicKey: "...",
}
response := keypairs.Create(client, opts)
key, err := response.Extract()
```
To delete an existing keypair:
```go
response := keypairs.Delete(client, "keypair_id")
```
## List IP addresses
This operation is not currently supported.
// +build acceptance,old
package main
import (
"fmt"
"github.com/rackspace/gophercloud"
"os"
"strings"
)
func main() {
provider, username, _, apiKey := getCredentials()
if !strings.Contains(provider, "rackspace") {
fmt.Fprintf(os.Stdout, "Skipping test because provider doesn't support API_KEYs\n")
return
}
_, err := gophercloud.Authenticate(
provider,
gophercloud.AuthOptions{
Username: username,
ApiKey: apiKey,
},
)
if err != nil {
panic(err)
}
}
// +build acceptance,old
package main
import (
"github.com/rackspace/gophercloud"
)
func main() {
provider, username, password, _ := getCredentials()
_, err := gophercloud.Authenticate(
provider,
gophercloud.AuthOptions{
Username: username,
Password: password,
},
)
if err != nil {
panic(err)
}
}
// +build acceptance,old
package main
import (
"flag"
"fmt"
"github.com/rackspace/gophercloud"
)
var quiet = flag.Bool("quiet", false, "Quiet mode, for acceptance testing. $? still indicates errors though.")
func main() {
flag.Parse()
withIdentity(false, func(acc gophercloud.AccessProvider) {
withServerApi(acc, func(api gophercloud.CloudServersProvider) {
tryFullDetails(api)
tryLinksOnly(api)
})
})
}
func tryLinksOnly(api gophercloud.CloudServersProvider) {
servers, err := api.ListServersLinksOnly()
if err != nil {
panic(err)
}
if !*quiet {
fmt.Println("Id,Name")
for _, s := range servers {
if s.AccessIPv4 != "" {
panic("IPv4 not expected")
}
if s.Status != "" {
panic("Status not expected")
}
if s.Progress != 0 {
panic("Progress not expected")
}
fmt.Printf("%s,\"%s\"\n", s.Id, s.Name)
}
}
}
func tryFullDetails(api gophercloud.CloudServersProvider) {
servers, err := api.ListServers()
if err != nil {
panic(err)
}
if !*quiet {
fmt.Println("Id,Name,AccessIPv4,Status,Progress")
for _, s := range servers {
fmt.Printf("%s,\"%s\",%s,%s,%d\n", s.Id, s.Name, s.AccessIPv4, s.Status, s.Progress)
}
}
}
// +build acceptance,old
package main
import (
"flag"
"fmt"
"github.com/rackspace/gophercloud"
"os"
"github.com/racker/perigee"
)
var id = flag.String("i", "", "Server ID to get info on. Defaults to first server in your account if unspecified.")
var rgn = flag.String("r", "", "Datacenter region. Leave blank for default region.")
var quiet = flag.Bool("quiet", false, "Run quietly, for acceptance testing. $? non-zero if issue.")
func main() {
flag.Parse()
resultCode := 0
withIdentity(false, func(auth gophercloud.AccessProvider) {
withServerApi(auth, func(servers gophercloud.CloudServersProvider) {
var (
err error
serverId string
deleteAfterwards bool
)
// Figure out which server to provide server details for.
if *id == "" {
deleteAfterwards, serverId, err = locateAServer(servers)
if err != nil {
panic(err)
}
if deleteAfterwards {
defer servers.DeleteServerById(serverId)
}
} else {
serverId = *id
}
// Grab server details by ID, and provide a report.
s, err := servers.ServerById(serverId)
if err != nil {
panic(err)
}
configs := []string{
"Access IPv4: %s\n",
"Access IPv6: %s\n",
" Created: %s\n",
" Flavor: %s\n",
" Host ID: %s\n",
" ID: %s\n",
" Image: %s\n",
" Name: %s\n",
" Progress: %s\n",
" Status: %s\n",
" Tenant ID: %s\n",
" Updated: %s\n",
" User ID: %s\n",
}
values := []string{
s.AccessIPv4,
s.AccessIPv6,
s.Created,
s.Flavor.Id,
s.HostId,
s.Id,
s.Image.Id,
s.Name,
fmt.Sprintf("%d", s.Progress),
s.Status,
s.TenantId,
s.Updated,
s.UserId,
}
if !*quiet {
fmt.Println("Server info:")
for i, _ := range configs {
fmt.Printf(configs[i], values[i])
}
}
})
// Negative test -- We should absolutely never panic for a server that doesn't exist.
withServerApi(auth, func(servers gophercloud.CloudServersProvider) {
_, err := servers.ServerById(randomString("garbage", 32))
if err == nil {
fmt.Printf("Expected a 404 response when looking for a server known not to exist\n")
resultCode = 1
}
perigeeError, ok := err.(*perigee.UnexpectedResponseCodeError)
if !ok {
fmt.Printf("Unexpected error type\n")
resultCode = 1
} else {
if perigeeError.Actual != 404 {
fmt.Printf("Expected a 404 error code\n")
}
}
})
})
os.Exit(resultCode)
}
// locateAServer queries the set of servers owned by the user. If at least one
// exists, the first found is picked, and its ID is returned. Otherwise, a new
// server will be created, and its ID returned.
//
// deleteAfter will be true if the caller should schedule a call to DeleteServerById()
// to clean up.
func locateAServer(servers gophercloud.CloudServersProvider) (deleteAfter bool, id string, err error) {
ss, err := servers.ListServers()
if err != nil {
return false, "", err
}
if len(ss) > 0 {
// We could just cheat and dump the server details from ss[0].
// But, that tests ListServers(), and not ServerById(). So, we
// elect not to cheat.
return false, ss[0].Id, nil
}
serverId, err := createServer(servers, "", "", "", "")
if err != nil {
return false, "", err
}
err = waitForServerState(servers, serverId, "ACTIVE")
return true, serverId, err
}
// +build acceptance,old
package main
import (
"flag"
"fmt"
"github.com/rackspace/gophercloud"
)
var region, serverName, imageRef, flavorRef *string
var adminPass = flag.String("a", "", "Administrator password (auto-assigned if none)")
var quiet = flag.Bool("quiet", false, "Quiet mode for acceptance tests. $? non-zero if error.")
func configure() {
region = flag.String("r", "", "Region in which to create the server. Leave blank for provider-default region.")
serverName = flag.String("n", randomString("ACPTTEST--", 16), "Server name (what you see in the control panel)")
imageRef = flag.String("i", "", "ID of image to deploy onto the server")
flavorRef = flag.String("f", "", "Flavor of server to deploy image upon")
flag.Parse()
}
func main() {
configure()
withIdentity(false, func(auth gophercloud.AccessProvider) {
withServerApi(auth, func(servers gophercloud.CloudServersProvider) {
_, err := createServer(servers, *imageRef, *flavorRef, *serverName, *adminPass)
if err != nil {
panic(err)
}
allServers, err := servers.ListServers()
if err != nil {
panic(err)
}
if !*quiet {
fmt.Printf("ID,Name,Status,Progress\n")
for _, i := range allServers {
fmt.Printf("%s,\"%s\",%s,%d\n", i.Id, i.Name, i.Status, i.Progress)
}
}
})
})
}
// +build acceptance,old
package main
import (
"flag"
"fmt"
"github.com/rackspace/gophercloud"
)
var quiet = flag.Bool("quiet", false, "Quiet mode for acceptance testing. $? non-zero on error though.")
var rgn = flag.String("r", "", "Datacenter region to interrogate. Leave blank for provider-default region.")
func main() {
flag.Parse()
withIdentity(false, func(auth gophercloud.AccessProvider) {
withServerApi(auth, func(servers gophercloud.CloudServersProvider) {
images, err := servers.ListImages()
if err != nil {
panic(err)
}
if !*quiet {
fmt.Println("ID,Name,MinRam,MinDisk")
for _, image := range images {
fmt.Printf("%s,\"%s\",%d,%d\n", image.Id, image.Name, image.MinRam, image.MinDisk)
}
}
})
})
}
// +build acceptance,old
package main
import (
"flag"
"fmt"
"github.com/rackspace/gophercloud"
)
var quiet = flag.Bool("quiet", false, "Quiet mode for acceptance testing. $? non-zero on error though.")
var rgn = flag.String("r", "", "Datacenter region to interrogate. Leave blank for provider-default region.")
func main() {
flag.Parse()
withIdentity(false, func(auth gophercloud.AccessProvider) {
withServerApi(auth, func(servers gophercloud.CloudServersProvider) {
flavors, err := servers.ListFlavors()
if err != nil {
panic(err)
}
if !*quiet {
fmt.Println("ID,Name,MinRam,MinDisk")
for _, f := range flavors {
fmt.Printf("%s,\"%s\",%d,%d\n", f.Id, f.Name, f.Ram, f.Disk)
}
}
})
})
}
// +build acceptance,old
package main
import (
"flag"
"fmt"
"github.com/rackspace/gophercloud"
)
var quiet = flag.Bool("quiet", false, "Quiet mode, for acceptance testing. $? still indicates errors though.")
var serverId = flag.String("i", "", "ID of server whose admin password is to be changed.")
var newPass = flag.String("p", "", "New password for the server.")
func main() {
flag.Parse()
withIdentity(false, func(acc gophercloud.AccessProvider) {
withServerApi(acc, func(api gophercloud.CloudServersProvider) {
// If user doesn't explicitly provide a server ID, create one dynamically.
if *serverId == "" {
var err error
*serverId, err = createServer(api, "", "", "", "")
if err != nil {
panic(err)
}
waitForServerState(api, *serverId, "ACTIVE")
}
// If no password is provided, create one dynamically.
if *newPass == "" {
*newPass = randomString("", 16)
}
// Submit the request for changing the admin password.
// Note that we don't verify this actually completes;
// doing so is beyond the scope of the SDK, and should be
// the responsibility of your specific OpenStack provider.
err := api.SetAdminPassword(*serverId, *newPass)
if err != nil {
panic(err)
}
if !*quiet {
fmt.Println("Password change request submitted.")
}
})
})
}
// +build acceptance,old
package main
import (
"flag"
"fmt"
"github.com/rackspace/gophercloud"
)
var quiet = flag.Bool("quiet", false, "Quiet mode for acceptance testing. $? non-zero on error though.")
var rgn = flag.String("r", "", "Datacenter region to interrogate. Leave blank for provider-default region.")
func main() {
flag.Parse()
// Invoke withIdentity such that re-auth is enabled.
withIdentity(true, func(auth gophercloud.AccessProvider) {
token1 := auth.AuthToken()
withServerApi(auth, func(servers gophercloud.CloudServersProvider) {
// Just to confirm everything works, we should be able to list images without error.
_, err := servers.ListImages()
if err != nil {
panic(err)
}
// Revoke our current authentication token.
auth.Revoke(auth.AuthToken())
// Attempt to list images again. This should _succeed_, because we enabled re-authentication.
_, err = servers.ListImages()
if err != nil {
panic(err)
}
// However, our new authentication token should differ.
token2 := auth.AuthToken()
if !*quiet {
fmt.Println("Old authentication token: ", token1)
fmt.Println("New authentication token: ", token2)
}
if token1 == token2 {
panic("Tokens should differ")
}
})
})
}
// +build acceptance,old
package main
import (
"flag"
"fmt"
"github.com/rackspace/gophercloud"
"time"
)
var quiet = flag.Bool("quiet", false, "Quiet mode, for acceptance testing. $? still indicates errors though.")
func main() {
flag.Parse()
withIdentity(false, func(acc gophercloud.AccessProvider) {
withServerApi(acc, func(api gophercloud.CloudServersProvider) {
// These tests are going to take some time to complete.
// So, we'll do two tests at the same time to help amortize test time.
done := make(chan bool)
go resizeRejectTest(api, done)
go resizeAcceptTest(api, done)
_ = <-done
_ = <-done
if !*quiet {
fmt.Println("Done.")
}
})
})
}
// Perform the resize test, but reject the resize request.
func resizeRejectTest(api gophercloud.CloudServersProvider, done chan bool) {
withServer(api, func(id string) {
newFlavorId := findAlternativeFlavor()
err := api.ResizeServer(id, randomString("ACPTTEST", 24), newFlavorId, "")
if err != nil {
panic(err)
}
waitForServerState(api, id, "VERIFY_RESIZE")
err = api.RevertResize(id)
if err != nil {
panic(err)
}
})
done <- true
}
// Perform the resize test, but accept the resize request.
func resizeAcceptTest(api gophercloud.CloudServersProvider, done chan bool) {
withServer(api, func(id string) {
newFlavorId := findAlternativeFlavor()
err := api.ResizeServer(id, randomString("ACPTTEST", 24), newFlavorId, "")
if err != nil {
panic(err)
}
waitForServerState(api, id, "VERIFY_RESIZE")
err = api.ConfirmResize(id)
if err != nil {
panic(err)
}
})
done <- true
}
func withServer(api gophercloud.CloudServersProvider, f func(string)) {
id, err := createServer(api, "", "", "", "")
if err != nil {
panic(err)
}
for {
s, err := api.ServerById(id)
if err != nil {
panic(err)
}
if s.Status == "ACTIVE" {
break
}
time.Sleep(10 * time.Second)
}
f(id)
// I've learned that resizing an instance can fail if a delete request
// comes in prior to its completion. This ends up leaving the server
// in an error state, and neither the resize NOR the delete complete.
// This is a bug in OpenStack, as far as I'm concerned, but thankfully,
// there's an easy work-around -- just wait for your server to return to
// active state first!
waitForServerState(api, id, "ACTIVE")
err = api.DeleteServerById(id)
if err != nil {
panic(err)
}
}
// +build acceptance,old
package main
import (
"flag"
"fmt"
"github.com/rackspace/gophercloud"
)
var quiet = flag.Bool("quiet", false, "Quiet mode, for acceptance testing. $? still indicates errors though.")
func main() {
flag.Parse()
withIdentity(false, func(acc gophercloud.AccessProvider) {
withServerApi(acc, func(servers gophercloud.CloudServersProvider) {
log("Creating server")
serverId, err := createServer(servers, "", "", "", "")
if err != nil {
panic(err)
}
waitForServerState(servers, serverId, "ACTIVE")
log("Soft-rebooting server")
servers.RebootServer(serverId, false)
waitForServerState(servers, serverId, "REBOOT")
waitForServerState(servers, serverId, "ACTIVE")
log("Hard-rebooting server")
servers.RebootServer(serverId, true)
waitForServerState(servers, serverId, "HARD_REBOOT")
waitForServerState(servers, serverId, "ACTIVE")
log("Done")
servers.DeleteServerById(serverId)
})
})
}
func log(s string) {
if !*quiet {
fmt.Println(s)
}
}
// +build acceptance,old
package main
import (
"flag"
"fmt"
"github.com/rackspace/gophercloud"
)
var quiet = flag.Bool("quiet", false, "Quiet mode, for acceptance testing. $? still indicates errors though.")
func main() {
flag.Parse()
withIdentity(false, func(acc gophercloud.AccessProvider) {
withServerApi(acc, func(servers gophercloud.CloudServersProvider) {
log("Creating server")
id, err := createServer(servers, "", "", "", "")
if err != nil {
panic(err)
}
waitForServerState(servers, id, "ACTIVE")
defer servers.DeleteServerById(id)
log("Rescuing server")
adminPass, err := servers.RescueServer(id)
if err != nil {
panic(err)
}
log(" Admin password = " + adminPass)
if len(adminPass) < 1 {
panic("Empty admin password")
}
waitForServerState(servers, id, "RESCUE")
log("Unrescuing server")
err = servers.UnrescueServer(id)
if err != nil {
panic(err)
}
waitForServerState(servers, id, "ACTIVE")
log("Done")
})
})
}
func log(s string) {
if !*quiet {
fmt.Println(s)
}
}
// +build acceptance,old
package main
import (
"flag"
"fmt"
"github.com/rackspace/gophercloud"
)
var quiet = flag.Bool("quiet", false, "Quiet mode, for acceptance testing. $? still indicates errors though.")
func main() {
flag.Parse()
withIdentity(false, func(acc gophercloud.AccessProvider) {
withServerApi(acc, func(servers gophercloud.CloudServersProvider) {
log("Creating server")
id, err := createServer(servers, "", "", "", "")
if err != nil {
panic(err)
}
waitForServerState(servers, id, "ACTIVE")
defer servers.DeleteServerById(id)
log("Updating name of server")
newName := randomString("ACPTTEST", 32)
newDetails, err := servers.UpdateServer(id, gophercloud.NewServerSettings{
Name: newName,
})
if err != nil {
panic(err)
}
if newDetails.Name != newName {
panic("Name change didn't appear to take")
}
log("Done")
})
})
}
func log(s string) {
if !*quiet {
fmt.Println(s)
}
}
// +build acceptance,old
package main
import (
"flag"
"fmt"
"github.com/rackspace/gophercloud"
)
var quiet = flag.Bool("quiet", false, "Quiet mode, for acceptance testing. $? still indicates errors though.")
func main() {
flag.Parse()
withIdentity(false, func(acc gophercloud.AccessProvider) {
withServerApi(acc, func(servers gophercloud.CloudServersProvider) {
log("Creating server")
id, err := createServer(servers, "", "", "", "")
if err != nil {
panic(err)
}
waitForServerState(servers, id, "ACTIVE")
defer servers.DeleteServerById(id)
log("Rebuilding server")
newDetails, err := servers.RebuildServer(id, gophercloud.NewServer{
Name: randomString("ACPTTEST", 32),
ImageRef: findAlternativeImage(),
FlavorRef: findAlternativeFlavor(),
AdminPass: randomString("", 16),
})
if err != nil {
panic(err)
}
waitForServerState(servers, newDetails.Id, "ACTIVE")
log("Done")
})
})
}
func log(s string) {
if !*quiet {
fmt.Println(s)
}
}
// +build acceptance,old
package main
import (
"flag"
"fmt"
"github.com/rackspace/gophercloud"
)
var quiet = flag.Bool("quiet", false, "Quiet mode, for acceptance testing. $? still indicates errors though.")
func main() {
flag.Parse()
withIdentity(false, func(acc gophercloud.AccessProvider) {
withServerApi(acc, func(api gophercloud.CloudServersProvider) {
log("Creating server")
id, err := createServer(api, "", "", "", "")
if err != nil {
panic(err)
}
waitForServerState(api, id, "ACTIVE")
defer api.DeleteServerById(id)
tryAllAddresses(id, api)
tryAddressesByNetwork("private", id, api)
log("Done")
})
})
}
func tryAllAddresses(id string, api gophercloud.CloudServersProvider) {
log("Getting list of all addresses...")
addresses, err := api.ListAddresses(id)
if (err != nil) && (err != gophercloud.WarnUnauthoritative) {
panic(err)
}
if err == gophercloud.WarnUnauthoritative {
log("Uh oh -- got a response back, but it's not authoritative for some reason.")
}
if !*quiet {
fmt.Println("Addresses:")
fmt.Printf("%+v\n", addresses)
}
}
func tryAddressesByNetwork(networkLabel string, id string, api gophercloud.CloudServersProvider) {
log("Getting list of addresses on", networkLabel, "network...")
network, err := api.ListAddressesByNetwork(id, networkLabel)
if (err != nil) && (err != gophercloud.WarnUnauthoritative) {
panic(err)
}
if err == gophercloud.WarnUnauthoritative {
log("Uh oh -- got a response back, but it's not authoritative for some reason.")
}
for _, addr := range network[networkLabel] {
log("Address:", addr.Addr, " IPv", addr.Version)
}
}
func log(s ...interface{}) {
if !*quiet {
fmt.Println(s...)
}
}
// +build acceptance,old
package main
import (
"flag"
"fmt"
"github.com/rackspace/gophercloud"
)
var quiet = flag.Bool("quiet", false, "Quiet mode for acceptance testing. $? non-zero on error though.")
var rgn = flag.String("r", "", "Datacenter region to interrogate. Leave blank for provider-default region.")
func main() {
flag.Parse()
withIdentity(false, func(auth gophercloud.AccessProvider) {
withServerApi(auth, func(servers gophercloud.CloudServersProvider) {
keypairs, err := servers.ListKeyPairs()
if err != nil {
panic(err)
}
if !*quiet {
fmt.Println("name,fingerprint,publickey")
for _, key := range keypairs {
fmt.Printf("%s,%s,%s\n", key.Name, key.FingerPrint, key.PublicKey)
}
}
})
})
}
// +build acceptance,old
package main
import (
"flag"
"fmt"
"github.com/rackspace/gophercloud"
)
var quiet = flag.Bool("quiet", false, "Quiet mode for acceptance testing. $? non-zero on error though.")
var rgn = flag.String("r", "", "Datacenter region to interrogate. Leave blank for provider-default region.")
func main() {
flag.Parse()
withIdentity(false, func(auth gophercloud.AccessProvider) {
withServerApi(auth, func(servers gophercloud.CloudServersProvider) {
name := randomString("ACPTTEST", 16)
kp := gophercloud.NewKeyPair{
Name: name,
}
keypair, err := servers.CreateKeyPair(kp)
if err != nil {
panic(err)
}
if !*quiet {
fmt.Printf("%s,%s,%s\n", keypair.Name, keypair.FingerPrint, keypair.PublicKey)
}
keypair, err = servers.ShowKeyPair(name)
if err != nil {
panic(err)
}
if !*quiet {
fmt.Printf("%s,%s,%s\n", keypair.Name, keypair.FingerPrint, keypair.PublicKey)
}
err = servers.DeleteKeyPair(name)
if err != nil {
panic(err)
}
})
})
}
// +build acceptance,old
package main
import (
"flag"
"fmt"
"github.com/rackspace/gophercloud"
)
var quiet = flag.Bool("quiet", false, "Quiet mode for acceptance testing. $? non-zero on error though.")
var rgn = flag.String("r", "", "Datacenter region to interrogate. Leave blank for provider-default region.")
func main() {
flag.Parse()
withIdentity(false, func(auth gophercloud.AccessProvider) {
withServerApi(auth, func(servers gophercloud.CloudServersProvider) {
log("Creating server")
serverId, err := createServer(servers, "", "", "", "")
if err != nil {
panic(err)
}
waitForServerState(servers, serverId, "ACTIVE")
log("Creating image")
name := randomString("ACPTTEST", 16)
createImage := gophercloud.CreateImage{
Name: name,
}
imageId, err := servers.CreateImage(serverId, createImage)
if err != nil {
panic(err)
}
waitForImageState(servers, imageId, "ACTIVE")
log("Deleting server")
servers.DeleteServerById(serverId)
log("Deleting image")
servers.DeleteImageById(imageId)
log("Done")
})
})
}
func log(s string) {
if !*quiet {
fmt.Println(s)
}
}
// +build acceptance,old
package main
import (
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/osutil"
)
func main() {
provider, authOptions, err := osutil.AuthOptions()
if err != nil {
panic(err)
}
_, err = gophercloud.Authenticate(provider, authOptions)
if err != nil {
panic(err)
}
}
// +build acceptance,old
package main
import (
"flag"
"fmt"
"github.com/rackspace/gophercloud"
)
var quiet = flag.Bool("quiet", false, "Quiet mode, for acceptance testing. $? still indicates errors though.")
func main() {
flag.Parse()
withIdentity(false, func(acc gophercloud.AccessProvider) {
withServerApi(acc, func(api gophercloud.CloudServersProvider) {
log("Creating server")
id, err := createServer(api, "", "", "", "")
if err != nil {
panic(err)
}
waitForServerState(api, id, "ACTIVE")
defer api.DeleteServerById(id)
tryAllAddresses(id, api)
log("Done")
})
})
}
func tryAllAddresses(id string, api gophercloud.CloudServersProvider) {
log("Getting the server instance")
s, err := api.ServerById(id)
if err != nil {
panic(err)
}
log("Getting the complete set of pools")
ps, err := s.AllAddressPools()
if err != nil {
panic(err)
}
log("Listing IPs for each pool")
for k, v := range ps {
log(fmt.Sprintf(" Pool %s", k))
for _, a := range v {
log(fmt.Sprintf(" IP: %s, Version: %d", a.Addr, a.Version))
}
}
}
func log(s ...interface{}) {
if !*quiet {
fmt.Println(s...)
}
}
// +build acceptance,old
package main
import (
"flag"
"fmt"
"github.com/rackspace/gophercloud"
)
var quiet = flag.Bool("quiet", false, "Quiet operation for acceptance tests. $? non-zero if problem.")
var region = flag.String("r", "", "Datacenter region. Leave blank for provider-default region.")
func main() {
flag.Parse()
withIdentity(false, func(auth gophercloud.AccessProvider) {
withServerApi(auth, func(servers gophercloud.CloudServersProvider) {
// Grab a listing of all servers.
ss, err := servers.ListServers()
if err != nil {
panic(err)
}
// And for each one that starts with the ACPTTEST prefix, delete it.
// These are likely left-overs from previously running acceptance tests.
// Note that 04-create-servers.go is intended to leak servers by intention,
// so as to test this code. :)
n := 0
for _, s := range ss {
if len(s.Name) < 8 {
continue
}
if s.Name[0:8] == "ACPTTEST" {
err := servers.DeleteServerById(s.Id)
if err != nil {
panic(err)
}
n++
}
}
if !*quiet {
fmt.Printf("%d servers removed.\n", n)
}
})
})
}
# Gophercloud Acceptance tests
The purpose of these acceptance tests is to validate that SDK features meet
the requirements of a contract - to consumers, other parts of the library, and
to a remote API.
> **Note:** Because every test will be run against a real API endpoint, you
> may incur bandwidth and service charges for all the resource usage. These
> tests *should* remove their remote products automatically. However, there may
> be certain cases where this does not happen; always double-check to make sure
> you have no stragglers left behind.
### Step 1. Set environment variables
A lot of tests rely on environment variables for configuration - so you will need
to set them before running the suite. If you're testing against pure OpenStack APIs,
you can download a file that contains all of these variables for you: just visit
the `project/access_and_security` page in your control panel and click the "Download
OpenStack RC File" button at the top right. For all other providers, you will need
to set them manually.
#### Authentication
|Name|Description|
|---|---|
|`OS_USERNAME`|Your API username|
|`OS_PASSWORD`|Your API password|
|`OS_AUTH_URL`|The identity URL you need to authenticate|
|`OS_TENANT_NAME`|Your API tenant name|
|`OS_TENANT_ID`|Your API tenant ID|
|`RS_USERNAME`|Your Rackspace username|
|`RS_API_KEY`|Your Rackspace API key|
#### General
|Name|Description|
|---|---|
|`OS_REGION_NAME`|The region you want your resources to reside in|
|`RS_REGION`|Rackspace region you want your resource to reside in|
#### Compute
|Name|Description|
|---|---|
|`OS_IMAGE_ID`|The ID of the image your want your server to be based on|
|`OS_FLAVOR_ID`|The ID of the flavor you want your server to be based on|
|`OS_FLAVOR_ID_RESIZE`|The ID of the flavor you want your server to be resized to|
|`RS_IMAGE_ID`|The ID of the image you want servers to be created with|
|`RS_FLAVOR_ID`|The ID of the flavor you want your server to be created with|
### 2. Run the test suite
From the root directory, run:
```
./script/acceptancetest
```
// +build acceptance,old
package main
import (
"crypto/rand"
"fmt"
"github.com/rackspace/gophercloud"
"os"
"strings"
"time"
)
// getCredentials will verify existence of needed credential information
// provided through environment variables. This function will not return
// if at least one piece of required information is missing.
func getCredentials() (provider, username, password, apiKey string) {
provider = os.Getenv("SDK_PROVIDER")
username = os.Getenv("SDK_USERNAME")
password = os.Getenv("SDK_PASSWORD")
apiKey = os.Getenv("SDK_API_KEY")
var authURL = os.Getenv("OS_AUTH_URL")
if (provider == "") || (username == "") || (password == "") {
fmt.Fprintf(os.Stderr, "One or more of the following environment variables aren't set:\n")
fmt.Fprintf(os.Stderr, " SDK_PROVIDER=\"%s\"\n", provider)
fmt.Fprintf(os.Stderr, " SDK_USERNAME=\"%s\"\n", username)
fmt.Fprintf(os.Stderr, " SDK_PASSWORD=\"%s\"\n", password)
os.Exit(1)
}
if strings.Contains(provider, "rackspace") && (authURL != "") {
provider = authURL + "/v2.0/tokens"
}
return
}
// 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)
}
// aSuitableImage finds a minimal image for use in dynamically creating servers.
// If none can be found, this function will panic.
func aSuitableImage(api gophercloud.CloudServersProvider) string {
images, err := api.ListImages()
if err != nil {
panic(err)
}
// TODO(sfalvo):
// Works for Rackspace, might not work for your provider!
// Need to figure out why ListImages() provides 0 values for
// Ram and Disk fields.
//
// Until then, just return Ubuntu 12.04 LTS.
for i := 0; i < len(images); i++ {
if strings.Contains(images[i].Name, "Ubuntu 12.04 LTS") {
return images[i].Id
}
}
panic("Image for Ubuntu 12.04 LTS not found.")
}
// aSuitableFlavor finds the minimum flavor capable of running the test image
// chosen by aSuitableImage. If none can be found, this function will panic.
func aSuitableFlavor(api gophercloud.CloudServersProvider) string {
flavors, err := api.ListFlavors()
if err != nil {
panic(err)
}
// TODO(sfalvo):
// Works for Rackspace, might not work for your provider!
// Need to figure out why ListFlavors() provides 0 values for
// Ram and Disk fields.
//
// Until then, just return Ubuntu 12.04 LTS.
for i := 0; i < len(flavors); i++ {
if flavors[i].Id == "2" {
return flavors[i].Id
}
}
panic("Flavor 2 (512MB 1-core 20GB machine) not found.")
}
// createServer creates a new server in a manner compatible with acceptance testing.
// In particular, it ensures that the name of the server always starts with "ACPTTEST--",
// which the delete servers acceptance test relies on to identify servers to delete.
// Passing in empty image and flavor references will force the use of reasonable defaults.
// An empty name string will result in a dynamically created name prefixed with "ACPTTEST--".
// A blank admin password will cause a password to be automatically generated; however,
// at present no means of recovering this password exists, as no acceptance tests yet require
// this data.
func createServer(servers gophercloud.CloudServersProvider, imageRef, flavorRef, name, adminPass string) (string, error) {
if imageRef == "" {
imageRef = aSuitableImage(servers)
}
if flavorRef == "" {
flavorRef = aSuitableFlavor(servers)
}
if len(name) < 1 {
name = randomString("ACPTTEST", 16)
}
if (len(name) < 8) || (name[0:8] != "ACPTTEST") {
name = fmt.Sprintf("ACPTTEST--%s", name)
}
newServer, err := servers.CreateServer(gophercloud.NewServer{
Name: name,
ImageRef: imageRef,
FlavorRef: flavorRef,
AdminPass: adminPass,
})
if err != nil {
return "", err
}
return newServer.Id, nil
}
// findAlternativeFlavor locates a flavor to resize a server to. It is guaranteed to be different
// than what aSuitableFlavor() returns. If none could be found, this function will panic.
func findAlternativeFlavor() string {
return "3" // 1GB image, up from 512MB image
}
// findAlternativeImage locates an image to resize or rebuild a server with. It is guaranteed to be
// different than what aSuitableImage() returns. If none could be found, this function will panic.
func findAlternativeImage() string {
return "c6f9c411-e708-4952-91e5-62ded5ea4d3e"
}
// withIdentity authenticates the user against the provider's identity service, and provides an
// accessor for additional services.
func withIdentity(ar bool, f func(gophercloud.AccessProvider)) {
_, _, _, apiKey := getCredentials()
if len(apiKey) == 0 {
withPasswordIdentity(ar, f)
} else {
withAPIKeyIdentity(ar, f)
}
}
func withPasswordIdentity(ar bool, f func(gophercloud.AccessProvider)) {
provider, username, password, _ := getCredentials()
acc, err := gophercloud.Authenticate(
provider,
gophercloud.AuthOptions{
Username: username,
Password: password,
AllowReauth: ar,
},
)
if err != nil {
panic(err)
}
f(acc)
}
func withAPIKeyIdentity(ar bool, f func(gophercloud.AccessProvider)) {
provider, username, _, apiKey := getCredentials()
acc, err := gophercloud.Authenticate(
provider,
gophercloud.AuthOptions{
Username: username,
ApiKey: apiKey,
AllowReauth: ar,
},
)
if err != nil {
panic(err)
}
f(acc)
}
// withServerApi acquires the cloud servers API.
func withServerApi(acc gophercloud.AccessProvider, f func(gophercloud.CloudServersProvider)) {
api, err := gophercloud.ServersApi(acc, gophercloud.ApiCriteria{
Name: "cloudServersOpenStack",
VersionId: "2",
UrlChoice: gophercloud.PublicURL,
})
if err != nil {
panic(err)
}
f(api)
}
// waitForServerState polls, every 10 seconds, for a given server to appear in the indicated state.
// This call will block forever if it never appears in the desired state, so if a timeout is required,
// make sure to call this function in a goroutine.
func waitForServerState(api gophercloud.CloudServersProvider, id, state string) error {
for {
s, err := api.ServerById(id)
if err != nil {
return err
}
if s.Status == state {
return nil
}
time.Sleep(10 * time.Second)
}
panic("Impossible")
}
// waitForImageState polls, every 10 seconds, for a given image to appear in the indicated state.
// This call will block forever if it never appears in the desired state, so if a timeout is required,
// make sure to call this function in a goroutine.
func waitForImageState(api gophercloud.CloudServersProvider, id, state string) error {
for {
s, err := api.ImageById(id)
if err != nil {
return err
}
if s.Status == state {
return nil
}
time.Sleep(10 * time.Second)
}
panic("Impossible")
}
// +build acceptance
package v1
import (
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/blockstorage/v1/snapshots"
"github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestSnapshots(t *testing.T) {
client, err := newClient()
th.AssertNoErr(t, err)
v, err := volumes.Create(client, &volumes.CreateOpts{
Name: "gophercloud-test-volume",
Size: 1,
}).Extract()
th.AssertNoErr(t, err)
err = volumes.WaitForStatus(client, v.ID, "available", 120)
th.AssertNoErr(t, err)
t.Logf("Created volume: %v\n", v)
ss, err := snapshots.Create(client, &snapshots.CreateOpts{
Name: "gophercloud-test-snapshot",
VolumeID: v.ID,
}).Extract()
th.AssertNoErr(t, err)
err = snapshots.WaitForStatus(client, ss.ID, "available", 120)
th.AssertNoErr(t, err)
t.Logf("Created snapshot: %+v\n", ss)
err = snapshots.Delete(client, ss.ID).ExtractErr()
th.AssertNoErr(t, err)
err = gophercloud.WaitFor(120, func() (bool, error) {
_, err := snapshots.Get(client, ss.ID).Extract()
if err != nil {
return true, nil
}
return false, nil
})
th.AssertNoErr(t, err)
t.Log("Deleted snapshot\n")
err = volumes.Delete(client, v.ID).ExtractErr()
th.AssertNoErr(t, err)
err = gophercloud.WaitFor(120, func() (bool, error) {
_, err := volumes.Get(client, v.ID).Extract()
if err != nil {
return true, nil
}
return false, nil
})
th.AssertNoErr(t, err)
t.Log("Deleted volume\n")
}
// +build acceptance blockstorage
package v1
import (
"os"
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack"
"github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func newClient() (*gophercloud.ServiceClient, error) {
ao, err := openstack.AuthOptionsFromEnv()
th.AssertNoErr(t, err)
client, err := openstack.AuthenticatedClient(ao)
th.AssertNoErr(t, err)
return openstack.NewBlockStorageV1(client, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
}
func TestVolumes(t *testing.T) {
client, err := newClient()
th.AssertNoErr(t, err)
cv, err := volumes.Create(client, &volumes.CreateOpts{
Size: 1,
Name: "gophercloud-test-volume",
}).Extract()
th.AssertNoErr(t, err)
defer func() {
err = volumes.WaitForStatus(client, cv.ID, "available", 60)
th.AssertNoErr(t, err)
err = volumes.Delete(client, cv.ID).ExtractErr()
th.AssertNoErr(t, err)
}()
_, err = volumes.Update(client, cv.ID, &volumes.UpdateOpts{
Name: "gophercloud-updated-volume",
}).Extract()
th.AssertNoErr(t, err)
v, err := volumes.Get(client, cv.ID).Extract()
th.AssertNoErr(t, err)
t.Logf("Got volume: %+v\n", v)
if v.Name != "gophercloud-updated-volume" {
t.Errorf("Unable to update volume: Expected name: gophercloud-updated-volume\nActual name: %s", v.Name)
}
err = volumes.List(client, &volumes.ListOpts{Name: "gophercloud-updated-volume"}).EachPage(func(page pagination.Page) (bool, error) {
vols, err := volumes.ExtractVolumes(page)
th.CheckEquals(t, 1, len(vols))
return true, err
})
th.AssertNoErr(t, err)
}
// +build acceptance
package v1
import (
"testing"
"time"
"github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumetypes"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestVolumeTypes(t *testing.T) {
client, err := newClient()
th.AssertNoErr(t, err)
vt, err := volumetypes.Create(client, &volumetypes.CreateOpts{
ExtraSpecs: map[string]interface{}{
"capabilities": "gpu",
"priority": 3,
},
Name: "gophercloud-test-volumeType",
}).Extract()
th.AssertNoErr(t, err)
defer func() {
time.Sleep(10000 * time.Millisecond)
err = volumetypes.Delete(client, vt.ID).ExtractErr()
if err != nil {
t.Error(err)
return
}
}()
t.Logf("Created volume type: %+v\n", vt)
vt, err = volumetypes.Get(client, vt.ID).Extract()
th.AssertNoErr(t, err)
t.Logf("Got volume type: %+v\n", vt)
err = volumetypes.List(client).EachPage(func(page pagination.Page) (bool, error) {
volTypes, err := volumetypes.ExtractVolumeTypes(page)
if len(volTypes) != 1 {
t.Errorf("Expected 1 volume type, got %d", len(volTypes))
}
t.Logf("Listing volume types: %+v\n", volTypes)
return true, err
})
th.AssertNoErr(t, err)
}
// +build acceptance
package openstack
import (
"os"
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack"
)
func TestAuthenticatedClient(t *testing.T) {
// Obtain credentials from the environment.
ao, err := openstack.AuthOptionsFromEnv()
if err != nil {
t.Fatalf("Unable to acquire credentials: %v", err)
}
client, err := openstack.AuthenticatedClient(ao)
if err != nil {
t.Fatalf("Unable to authenticate: %v", err)
}
if client.TokenID == "" {
t.Errorf("No token ID assigned to the client")
}
t.Logf("Client successfully acquired a token: %v", client.TokenID)
// Find the storage service in the service catalog.
storage, err := openstack.NewObjectStorageV1(client, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
if err != nil {
t.Errorf("Unable to locate a storage service: %v", err)
} else {
t.Logf("Located a storage service at endpoint: [%s]", storage.Endpoint)
}
}
// +build acceptance
package v2
import (
"testing"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
th "github.com/rackspace/gophercloud/testhelper"
"github.com/smashwilson/gophercloud/acceptance/tools"
)
func TestBootFromVolume(t *testing.T) {
client, err := newClient()
th.AssertNoErr(t, err)
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
choices, err := ComputeChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
name := tools.RandomString("Gophercloud-", 8)
t.Logf("Creating server [%s].", name)
bd := []bootfromvolume.BlockDevice{
bootfromvolume.BlockDevice{
UUID: choices.ImageID,
SourceType: bootfromvolume.Image,
VolumeSize: 10,
},
}
serverCreateOpts := servers.CreateOpts{
Name: name,
FlavorRef: "3",
}
server, err := bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{
serverCreateOpts,
bd,
}).Extract()
th.AssertNoErr(t, err)
t.Logf("Created server: %+v\n", server)
//defer deleteServer(t, client, server)
t.Logf("Deleting server [%s]...", name)
}
// +build acceptance
package v2
import (
"fmt"
"os"
"strings"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/acceptance/tools"
"github.com/rackspace/gophercloud/openstack"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
)
func newClient() (*gophercloud.ServiceClient, error) {
ao, err := openstack.AuthOptionsFromEnv()
if err != nil {
return nil, err
}
client, err := openstack.AuthenticatedClient(ao)
if err != nil {
return nil, err
}
return openstack.NewComputeV2(client, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
}
func waitForStatus(client *gophercloud.ServiceClient, server *servers.Server, status string) error {
return tools.WaitFor(func() (bool, error) {
latest, err := servers.Get(client, server.ID).Extract()
if err != nil {
return false, err
}
if latest.Status == status {
// Success!
return true, nil
}
return false, nil
})
}
// ComputeChoices contains image and flavor selections for use by the acceptance tests.
type ComputeChoices struct {
// ImageID contains the ID of a valid image.
ImageID string
// FlavorID contains the ID of a valid flavor.
FlavorID string
// FlavorIDResize contains the ID of a different flavor available on the same OpenStack installation, that is distinct
// from FlavorID.
FlavorIDResize string
}
// ComputeChoicesFromEnv populates a ComputeChoices struct from environment variables.
// If any required state is missing, an `error` will be returned that enumerates the missing properties.
func ComputeChoicesFromEnv() (*ComputeChoices, error) {
imageID := os.Getenv("OS_IMAGE_ID")
flavorID := os.Getenv("OS_FLAVOR_ID")
flavorIDResize := os.Getenv("OS_FLAVOR_ID_RESIZE")
missing := make([]string, 0, 3)
if imageID == "" {
missing = append(missing, "OS_IMAGE_ID")
}
if flavorID == "" {
missing = append(missing, "OS_FLAVOR_ID")
}
if flavorIDResize == "" {
missing = append(missing, "OS_FLAVOR_ID_RESIZE")
}
notDistinct := ""
if flavorID == flavorIDResize {
notDistinct = "OS_FLAVOR_ID and OS_FLAVOR_ID_RESIZE must be distinct."
}
if len(missing) > 0 || notDistinct != "" {
text := "You're missing some important setup:\n"
if len(missing) > 0 {
text += " * These environment variables must be provided: " + strings.Join(missing, ", ") + "\n"
}
if notDistinct != "" {
text += " * " + notDistinct + "\n"
}
return nil, fmt.Errorf(text)
}
return &ComputeChoices{ImageID: imageID, FlavorID: flavorID, FlavorIDResize: flavorIDResize}, nil
}
// +build acceptance compute extensionss
package v2
import (
"testing"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestListExtensions(t *testing.T) {
client, err := newClient()
th.AssertNoErr(t, err)
err = extensions.List(client).EachPage(func(page pagination.Page) (bool, error) {
t.Logf("--- Page ---")
exts, err := extensions.ExtractExtensions(page)
th.AssertNoErr(t, err)
for i, ext := range exts {
t.Logf("[%02d] name=[%s]\n", i, ext.Name)
t.Logf(" alias=[%s]\n", ext.Alias)
t.Logf(" description=[%s]\n", ext.Description)
}
return true, nil
})
th.AssertNoErr(t, err)
}
func TestGetExtension(t *testing.T) {
client, err := newClient()
th.AssertNoErr(t, err)
ext, err := extensions.Get(client, "os-admin-actions").Extract()
th.AssertNoErr(t, err)
t.Logf("Extension details:")
t.Logf(" name=[%s]\n", ext.Name)
t.Logf(" namespace=[%s]\n", ext.Namespace)
t.Logf(" alias=[%s]\n", ext.Alias)
t.Logf(" description=[%s]\n", ext.Description)
t.Logf(" updated=[%s]\n", ext.Updated)
}
// +build acceptance compute flavors
package v2
import (
"testing"
"github.com/rackspace/gophercloud/openstack/compute/v2/flavors"
"github.com/rackspace/gophercloud/pagination"
)
func TestListFlavors(t *testing.T) {
client, err := newClient()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
t.Logf("ID\tRegion\tName\tStatus\tCreated")
pager := flavors.ListDetail(client, nil)
count, pages := 0, 0
pager.EachPage(func(page pagination.Page) (bool, error) {
t.Logf("---")
pages++
flavors, err := flavors.ExtractFlavors(page)
if err != nil {
return false, err
}
for _, f := range flavors {
t.Logf("%s\t%s\t%d\t%d\t%d", f.ID, f.Name, f.RAM, f.Disk, f.VCPUs)
}
return true, nil
})
t.Logf("--------\n%d flavors listed on %d pages.", count, pages)
}
func TestGetFlavor(t *testing.T) {
client, err := newClient()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
choices, err := ComputeChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
flavor, err := flavors.Get(client, choices.FlavorID).Extract()
if err != nil {
t.Fatalf("Unable to get flavor information: %v", err)
}
t.Logf("Flavor: %#v", flavor)
}
// +build acceptance compute images
package v2
import (
"testing"
"github.com/rackspace/gophercloud/openstack/compute/v2/images"
"github.com/rackspace/gophercloud/pagination"
)
func TestListImages(t *testing.T) {
client, err := newClient()
if err != nil {
t.Fatalf("Unable to create a compute: client: %v", err)
}
t.Logf("ID\tRegion\tName\tStatus\tCreated")
pager := images.ListDetail(client, nil)
count, pages := 0, 0
pager.EachPage(func(page pagination.Page) (bool, error) {
pages++
images, err := images.ExtractImages(page)
if err != nil {
return false, err
}
for _, i := range images {
t.Logf("%s\t%s\t%s\t%s", i.ID, i.Name, i.Status, i.Created)
}
return true, nil
})
t.Logf("--------\n%d images listed on %d pages.", count, pages)
}
// The v2 package contains acceptance tests for the Openstack Compute V2 service.
package v2
// +build acceptance compute servers
package v2
import (
"os"
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/acceptance/tools"
"github.com/rackspace/gophercloud/openstack"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
"github.com/rackspace/gophercloud/pagination"
)
func TestListServers(t *testing.T) {
client, err := newClient()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
t.Logf("ID\tRegion\tName\tStatus\tIPv4\tIPv6")
pager := servers.List(client, servers.ListOpts{})
count, pages := 0, 0
pager.EachPage(func(page pagination.Page) (bool, error) {
pages++
t.Logf("---")
servers, err := servers.ExtractServers(page)
if err != nil {
return false, err
}
for _, s := range servers {
t.Logf("%s\t%s\t%s\t%s\t%s\t\n", s.ID, s.Name, s.Status, s.AccessIPv4, s.AccessIPv6)
count++
}
return true, nil
})
t.Logf("--------\n%d servers listed on %d pages.\n", count, pages)
}
func networkingClient() (*gophercloud.ServiceClient, error) {
opts, err := openstack.AuthOptionsFromEnv()
if err != nil {
return nil, err
}
provider, err := openstack.AuthenticatedClient(opts)
if err != nil {
return nil, err
}
return openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{
Name: "neutron",
Region: os.Getenv("OS_REGION_NAME"),
})
}
func createServer(t *testing.T, client *gophercloud.ServiceClient, choices *ComputeChoices) (*servers.Server, error) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
var network networks.Network
networkingClient, err := networkingClient()
if err != nil {
t.Fatalf("Unable to create a networking client: %v", err)
}
pager := networks.List(networkingClient, networks.ListOpts{Name: "public", Limit: 1})
pager.EachPage(func(page pagination.Page) (bool, error) {
networks, err := networks.ExtractNetworks(page)
if err != nil {
t.Errorf("Failed to extract networks: %v", err)
return false, err
}
if len(networks) == 0 {
t.Fatalf("No networks to attach to server")
return false, err
}
network = networks[0]
return false, nil
})
name := tools.RandomString("ACPTTEST", 16)
t.Logf("Attempting to create server: %s\n", name)
server, err := servers.Create(client, servers.CreateOpts{
Name: name,
FlavorRef: choices.FlavorID,
ImageRef: choices.ImageID,
Networks: []servers.Network{
servers.Network{UUID: network.ID},
},
}).Extract()
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
return server, err
}
func TestCreateDestroyServer(t *testing.T) {
choices, err := ComputeChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
client, err := newClient()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
server, err := createServer(t, client, choices)
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
defer func() {
servers.Delete(client, server.ID)
t.Logf("Server deleted.")
}()
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
t.Fatalf("Unable to wait for server: %v", err)
}
}
func TestUpdateServer(t *testing.T) {
client, err := newClient()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
choices, err := ComputeChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
server, err := createServer(t, client, choices)
if err != nil {
t.Fatal(err)
}
defer servers.Delete(client, server.ID)
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
t.Fatal(err)
}
alternateName := tools.RandomString("ACPTTEST", 16)
for alternateName == server.Name {
alternateName = tools.RandomString("ACPTTEST", 16)
}
t.Logf("Attempting to rename the server to %s.", alternateName)
updated, err := servers.Update(client, server.ID, servers.UpdateOpts{Name: alternateName}).Extract()
if err != nil {
t.Fatalf("Unable to rename server: %v", err)
}
if updated.ID != server.ID {
t.Errorf("Updated server ID [%s] didn't match original server ID [%s]!", updated.ID, server.ID)
}
err = tools.WaitFor(func() (bool, error) {
latest, err := servers.Get(client, updated.ID).Extract()
if err != nil {
return false, err
}
return latest.Name == alternateName, nil
})
}
func TestActionChangeAdminPassword(t *testing.T) {
t.Parallel()
client, err := newClient()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
choices, err := ComputeChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
server, err := createServer(t, client, choices)
if err != nil {
t.Fatal(err)
}
defer servers.Delete(client, server.ID)
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
t.Fatal(err)
}
randomPassword := tools.MakeNewPassword(server.AdminPass)
res := servers.ChangeAdminPassword(client, server.ID, randomPassword)
if res.Err != nil {
t.Fatal(err)
}
if err = waitForStatus(client, server, "PASSWORD"); err != nil {
t.Fatal(err)
}
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
t.Fatal(err)
}
}
func TestActionReboot(t *testing.T) {
t.Parallel()
client, err := newClient()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
choices, err := ComputeChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
server, err := createServer(t, client, choices)
if err != nil {
t.Fatal(err)
}
defer servers.Delete(client, server.ID)
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
t.Fatal(err)
}
res := servers.Reboot(client, server.ID, "aldhjflaskhjf")
if res.Err == nil {
t.Fatal("Expected the SDK to provide an ArgumentError here")
}
t.Logf("Attempting reboot of server %s", server.ID)
res = servers.Reboot(client, server.ID, servers.OSReboot)
if res.Err != nil {
t.Fatalf("Unable to reboot server: %v", err)
}
if err = waitForStatus(client, server, "REBOOT"); err != nil {
t.Fatal(err)
}
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
t.Fatal(err)
}
}
func TestActionRebuild(t *testing.T) {
t.Parallel()
client, err := newClient()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
choices, err := ComputeChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
server, err := createServer(t, client, choices)
if err != nil {
t.Fatal(err)
}
defer servers.Delete(client, server.ID)
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
t.Fatal(err)
}
t.Logf("Attempting to rebuild server %s", server.ID)
rebuildOpts := servers.RebuildOpts{
Name: tools.RandomString("ACPTTEST", 16),
AdminPass: tools.MakeNewPassword(server.AdminPass),
ImageID: choices.ImageID,
}
rebuilt, err := servers.Rebuild(client, server.ID, rebuildOpts).Extract()
if err != nil {
t.Fatal(err)
}
if rebuilt.ID != server.ID {
t.Errorf("Expected rebuilt server ID of [%s]; got [%s]", server.ID, rebuilt.ID)
}
if err = waitForStatus(client, rebuilt, "REBUILD"); err != nil {
t.Fatal(err)
}
if err = waitForStatus(client, rebuilt, "ACTIVE"); err != nil {
t.Fatal(err)
}
}
func resizeServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server, choices *ComputeChoices) {
if err := waitForStatus(client, server, "ACTIVE"); err != nil {
t.Fatal(err)
}
t.Logf("Attempting to resize server [%s]", server.ID)
opts := &servers.ResizeOpts{
FlavorRef: choices.FlavorIDResize,
}
if res := servers.Resize(client, server.ID, opts); res.Err != nil {
t.Fatal(res.Err)
}
if err := waitForStatus(client, server, "VERIFY_RESIZE"); err != nil {
t.Fatal(err)
}
}
func TestActionResizeConfirm(t *testing.T) {
t.Parallel()
choices, err := ComputeChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
client, err := newClient()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
server, err := createServer(t, client, choices)
if err != nil {
t.Fatal(err)
}
defer servers.Delete(client, server.ID)
resizeServer(t, client, server, choices)
t.Logf("Attempting to confirm resize for server %s", server.ID)
if res := servers.ConfirmResize(client, server.ID); res.Err != nil {
t.Fatal(err)
}
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
t.Fatal(err)
}
}
func TestActionResizeRevert(t *testing.T) {
t.Parallel()
choices, err := ComputeChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
client, err := newClient()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
server, err := createServer(t, client, choices)
if err != nil {
t.Fatal(err)
}
defer servers.Delete(client, server.ID)
resizeServer(t, client, server, choices)
t.Logf("Attempting to revert resize for server %s", server.ID)
if res := servers.RevertResize(client, server.ID); res.Err != nil {
t.Fatal(err)
}
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
t.Fatal(err)
}
}
// +build acceptance
package v2
import (
"testing"
extensions2 "github.com/rackspace/gophercloud/openstack/identity/v2/extensions"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestEnumerateExtensions(t *testing.T) {
service := authenticatedClient(t)
t.Logf("Extensions available on this identity endpoint:")
count := 0
err := extensions2.List(service).EachPage(func(page pagination.Page) (bool, error) {
t.Logf("--- Page %02d ---", count)
extensions, err := extensions2.ExtractExtensions(page)
th.AssertNoErr(t, err)
for i, ext := range extensions {
t.Logf("[%02d] name=[%s] namespace=[%s]", i, ext.Name, ext.Namespace)
t.Logf(" alias=[%s] updated=[%s]", ext.Alias, ext.Updated)
t.Logf(" description=[%s]", ext.Description)
}
count++
return true, nil
})
th.AssertNoErr(t, err)
}
func TestGetExtension(t *testing.T) {
service := authenticatedClient(t)
ext, err := extensions2.Get(service, "OS-KSCRUD").Extract()
th.AssertNoErr(t, err)
th.CheckEquals(t, "OpenStack Keystone User CRUD", ext.Name)
th.CheckEquals(t, "http://docs.openstack.org/identity/api/ext/OS-KSCRUD/v1.0", ext.Namespace)
th.CheckEquals(t, "OS-KSCRUD", ext.Alias)
th.CheckEquals(t, "OpenStack extensions to Keystone v2.0 API enabling User Operations.", ext.Description)
}
// +build acceptance
package v2
import (
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack"
th "github.com/rackspace/gophercloud/testhelper"
)
func v2AuthOptions(t *testing.T) gophercloud.AuthOptions {
// Obtain credentials from the environment.
ao, err := openstack.AuthOptionsFromEnv()
th.AssertNoErr(t, err)
// Trim out unused fields. Prefer authentication by API key to password.
ao.UserID, ao.DomainID, ao.DomainName = "", "", ""
if ao.APIKey != "" {
ao.Password = ""
}
return ao
}
func createClient(t *testing.T, auth bool) *gophercloud.ServiceClient {
ao := v2AuthOptions(t)
provider, err := openstack.NewClient(ao.IdentityEndpoint)
th.AssertNoErr(t, err)
if auth {
err = openstack.AuthenticateV2(provider, ao)
th.AssertNoErr(t, err)
}
return openstack.NewIdentityV2(provider)
}
func unauthenticatedClient(t *testing.T) *gophercloud.ServiceClient {
return createClient(t, false)
}
func authenticatedClient(t *testing.T) *gophercloud.ServiceClient {
return createClient(t, true)
}
// +build acceptance
package v2
import (
"testing"
tenants2 "github.com/rackspace/gophercloud/openstack/identity/v2/tenants"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestEnumerateTenants(t *testing.T) {
service := authenticatedClient(t)
t.Logf("Tenants to which your current token grants access:")
count := 0
err := tenants2.List(service, nil).EachPage(func(page pagination.Page) (bool, error) {
t.Logf("--- Page %02d ---", count)
tenants, err := tenants2.ExtractTenants(page)
th.AssertNoErr(t, err)
for i, tenant := range tenants {
t.Logf("[%02d] name=[%s] id=[%s] description=[%s] enabled=[%v]",
i, tenant.Name, tenant.ID, tenant.Description, tenant.Enabled)
}
count++
return true, nil
})
th.AssertNoErr(t, err)
}
// +build acceptance
package v2
import (
"testing"
tokens2 "github.com/rackspace/gophercloud/openstack/identity/v2/tokens"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestAuthenticate(t *testing.T) {
ao := v2AuthOptions(t)
service := unauthenticatedClient(t)
// Authenticated!
result := tokens2.Create(service, tokens2.WrapOptions(ao))
// Extract and print the token.
token, err := result.ExtractToken()
th.AssertNoErr(t, err)
t.Logf("Acquired token: [%s]", token.ID)
t.Logf("The token will expire at: [%s]", token.ExpiresAt.String())
t.Logf("The token is valid for tenant: [%#v]", token.Tenant)
// Extract and print the service catalog.
catalog, err := result.ExtractServiceCatalog()
th.AssertNoErr(t, err)
t.Logf("Acquired service catalog listing [%d] services", len(catalog.Entries))
for i, entry := range catalog.Entries {
t.Logf("[%02d]: name=[%s], type=[%s]", i, entry.Name, entry.Type)
for _, endpoint := range entry.Endpoints {
t.Logf(" - region=[%s] publicURL=[%s]", endpoint.Region, endpoint.PublicURL)
}
}
}
// +build acceptance
package v3
import (
"testing"
"github.com/rackspace/gophercloud"
endpoints3 "github.com/rackspace/gophercloud/openstack/identity/v3/endpoints"
services3 "github.com/rackspace/gophercloud/openstack/identity/v3/services"
"github.com/rackspace/gophercloud/pagination"
)
func TestListEndpoints(t *testing.T) {
// Create a service client.
serviceClient := createAuthenticatedClient(t)
if serviceClient == nil {
return
}
// Use the service to list all available endpoints.
pager := endpoints3.List(serviceClient, endpoints3.ListOpts{})
err := pager.EachPage(func(page pagination.Page) (bool, error) {
t.Logf("--- Page ---")
endpoints, err := endpoints3.ExtractEndpoints(page)
if err != nil {
t.Fatalf("Error extracting endpoings: %v", err)
}
for _, endpoint := range endpoints {
t.Logf("Endpoint: %8s %10s %9s %s",
endpoint.ID,
endpoint.Availability,
endpoint.Name,
endpoint.URL)
}
return true, nil
})
if err != nil {
t.Errorf("Unexpected error while iterating endpoint pages: %v", err)
}
}
func TestNavigateCatalog(t *testing.T) {
// Create a service client.
client := createAuthenticatedClient(t)
if client == nil {
return
}
var compute *services3.Service
var endpoint *endpoints3.Endpoint
// Discover the service we're interested in.
servicePager := services3.List(client, services3.ListOpts{ServiceType: "compute"})
err := servicePager.EachPage(func(page pagination.Page) (bool, error) {
part, err := services3.ExtractServices(page)
if err != nil {
return false, err
}
if compute != nil {
t.Fatalf("Expected one service, got more than one page")
return false, nil
}
if len(part) != 1 {
t.Fatalf("Expected one service, got %d", len(part))
return false, nil
}
compute = &part[0]
return true, nil
})
if err != nil {
t.Fatalf("Unexpected error iterating pages: %v", err)
}
if compute == nil {
t.Fatalf("No compute service found.")
}
// Enumerate the endpoints available for this service.
computePager := endpoints3.List(client, endpoints3.ListOpts{
Availability: gophercloud.AvailabilityPublic,
ServiceID: compute.ID,
})
err = computePager.EachPage(func(page pagination.Page) (bool, error) {
part, err := endpoints3.ExtractEndpoints(page)
if err != nil {
return false, err
}
if endpoint != nil {
t.Fatalf("Expected one endpoint, got more than one page")
return false, nil
}
if len(part) != 1 {
t.Fatalf("Expected one endpoint, got %d", len(part))
return false, nil
}
endpoint = &part[0]
return true, nil
})
if endpoint == nil {
t.Fatalf("No endpoint found.")
}
t.Logf("Success. The compute endpoint is at %s.", endpoint.URL)
}
// +build acceptance
package v3
import (
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack"
th "github.com/rackspace/gophercloud/testhelper"
)
func createAuthenticatedClient(t *testing.T) *gophercloud.ServiceClient {
// Obtain credentials from the environment.
ao, err := openstack.AuthOptionsFromEnv()
th.AssertNoErr(t, err)
// Trim out unused fields.
ao.Username, ao.TenantID, ao.TenantName = "", "", ""
if ao.UserID == "" {
t.Logf("Skipping identity v3 tests because no OS_USERID is present.")
return nil
}
// Create a client and manually authenticate against v3.
providerClient, err := openstack.NewClient(ao.IdentityEndpoint)
if err != nil {
t.Fatalf("Unable to instantiate client: %v", err)
}
err = openstack.AuthenticateV3(providerClient, ao)
if err != nil {
t.Fatalf("Unable to authenticate against identity v3: %v", err)
}
// Create a service client.
return openstack.NewIdentityV3(providerClient)
}
// +build acceptance
package v3
import (
"testing"
services3 "github.com/rackspace/gophercloud/openstack/identity/v3/services"
"github.com/rackspace/gophercloud/pagination"
)
func TestListServices(t *testing.T) {
// Create a service client.
serviceClient := createAuthenticatedClient(t)
if serviceClient == nil {
return
}
// Use the client to list all available services.
pager := services3.List(serviceClient, services3.ListOpts{})
err := pager.EachPage(func(page pagination.Page) (bool, error) {
parts, err := services3.ExtractServices(page)
if err != nil {
return false, err
}
t.Logf("--- Page ---")
for _, service := range parts {
t.Logf("Service: %32s %15s %10s %s", service.ID, service.Type, service.Name, *service.Description)
}
return true, nil
})
if err != nil {
t.Errorf("Unexpected error traversing pages: %v", err)
}
}
// +build acceptance
package v3
import (
"testing"
"github.com/rackspace/gophercloud/openstack"
tokens3 "github.com/rackspace/gophercloud/openstack/identity/v3/tokens"
)
func TestGetToken(t *testing.T) {
// Obtain credentials from the environment.
ao, err := openstack.AuthOptionsFromEnv()
if err != nil {
t.Fatalf("Unable to acquire credentials: %v", err)
}
// Trim out unused fields. Skip if we don't have a UserID.
ao.Username, ao.TenantID, ao.TenantName = "", "", ""
if ao.UserID == "" {
t.Logf("Skipping identity v3 tests because no OS_USERID is present.")
return
}
// Create an unauthenticated client.
provider, err := openstack.NewClient(ao.IdentityEndpoint)
if err != nil {
t.Fatalf("Unable to instantiate client: %v", err)
}
// Create a service client.
service := openstack.NewIdentityV3(provider)
// Use the service to create a token.
token, err := tokens3.Create(service, ao, nil).Extract()
if err != nil {
t.Fatalf("Unable to get token: %v", err)
}
t.Logf("Acquired token: %s", token.ID)
}
// +build acceptance networking
package v2
import (
"testing"
"github.com/rackspace/gophercloud/openstack/networking/v2/apiversions"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestListAPIVersions(t *testing.T) {
Setup(t)
defer Teardown()
pager := apiversions.ListVersions(Client)
err := pager.EachPage(func(page pagination.Page) (bool, error) {
t.Logf("--- Page ---")
versions, err := apiversions.ExtractAPIVersions(page)
th.AssertNoErr(t, err)
for _, v := range versions {
t.Logf("API Version: ID [%s] Status [%s]", v.ID, v.Status)
}
return true, nil
})
th.CheckNoErr(t, err)
}
func TestListAPIResources(t *testing.T) {
Setup(t)
defer Teardown()
pager := apiversions.ListVersionResources(Client, "v2.0")
err := pager.EachPage(func(page pagination.Page) (bool, error) {
t.Logf("--- Page ---")
vrs, err := apiversions.ExtractVersionResources(page)
th.AssertNoErr(t, err)
for _, vr := range vrs {
t.Logf("Network: Name [%s] Collection [%s]", vr.Name, vr.Collection)
}
return true, nil
})
th.CheckNoErr(t, err)
}
package v2
import (
"os"
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack"
th "github.com/rackspace/gophercloud/testhelper"
)
var Client *gophercloud.ServiceClient
func NewClient() (*gophercloud.ServiceClient, error) {
opts, err := openstack.AuthOptionsFromEnv()
if err != nil {
return nil, err
}
provider, err := openstack.AuthenticatedClient(opts)
if err != nil {
return nil, err
}
return openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{
Name: "neutron",
Region: os.Getenv("OS_REGION_NAME"),
})
}
func Setup(t *testing.T) {
client, err := NewClient()
th.AssertNoErr(t, err)
Client = client
}
func Teardown() {
Client = nil
}
// +build acceptance networking
package v2
import (
"testing"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestListExts(t *testing.T) {
Setup(t)
defer Teardown()
pager := extensions.List(Client)
err := pager.EachPage(func(page pagination.Page) (bool, error) {
t.Logf("--- Page ---")
exts, err := extensions.ExtractExtensions(page)
th.AssertNoErr(t, err)
for _, ext := range exts {
t.Logf("Extension: Name [%s] Description [%s]", ext.Name, ext.Description)
}
return true, nil
})
th.CheckNoErr(t, err)
}
func TestGetExt(t *testing.T) {
Setup(t)
defer Teardown()
ext, err := extensions.Get(Client, "service-type").Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, ext.Updated, "2013-01-20T00:00:00-00:00")
th.AssertEquals(t, ext.Name, "Neutron Service Type Management")
th.AssertEquals(t, ext.Namespace, "http://docs.openstack.org/ext/neutron/service-type/api/v1.0")
th.AssertEquals(t, ext.Alias, "service-type")
th.AssertEquals(t, ext.Description, "API for retrieving service providers for Neutron advanced services")
}
// +build acceptance networking layer3ext
package extensions
import (
"testing"
base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/external"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers"
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
"github.com/rackspace/gophercloud/openstack/networking/v2/ports"
"github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
const (
cidr1 = "10.0.0.1/24"
cidr2 = "20.0.0.1/24"
)
func TestAll(t *testing.T) {
base.Setup(t)
defer base.Teardown()
testRouter(t)
testFloatingIP(t)
}
func testRouter(t *testing.T) {
// Setup: Create network
networkID := createNetwork(t)
// Create router
routerID := createRouter(t, networkID)
// Lists routers
listRouters(t)
// Update router
updateRouter(t, routerID)
// Get router
getRouter(t, routerID)
// Create new subnet. Note: this subnet will be deleted when networkID is deleted
subnetID := createSubnet(t, networkID, cidr2)
// Add interface
addInterface(t, routerID, subnetID)
// Remove interface
removeInterface(t, routerID, subnetID)
// Delete router
deleteRouter(t, routerID)
// Cleanup
deleteNetwork(t, networkID)
}
func testFloatingIP(t *testing.T) {
// Setup external network
extNetworkID := createNetwork(t)
// Setup internal network, subnet and port
intNetworkID, subnetID, portID := createInternalTopology(t)
// Now the important part: we need to allow the external network to talk to
// the internal subnet. For this we need a router that has an interface to
// the internal subnet.
routerID := bridgeIntSubnetWithExtNetwork(t, extNetworkID, subnetID)
// Create floating IP
ipID := createFloatingIP(t, extNetworkID, portID)
// Get floating IP
getFloatingIP(t, ipID)
// Update floating IP
updateFloatingIP(t, ipID, portID)
// Delete floating IP
deleteFloatingIP(t, ipID)
// Remove the internal subnet interface
removeInterface(t, routerID, subnetID)
// Delete router and external network
deleteRouter(t, routerID)
deleteNetwork(t, extNetworkID)
// Delete internal port and network
deletePort(t, portID)
deleteNetwork(t, intNetworkID)
}
func createNetwork(t *testing.T) string {
t.Logf("Creating a network")
asu := true
opts := external.CreateOpts{
Parent: networks.CreateOpts{Name: "sample_network", AdminStateUp: &asu},
External: true,
}
n, err := networks.Create(base.Client, opts).Extract()
th.AssertNoErr(t, err)
if n.ID == "" {
t.Fatalf("No ID returned when creating a network")
}
createSubnet(t, n.ID, cidr1)
t.Logf("Network created: ID [%s]", n.ID)
return n.ID
}
func deleteNetwork(t *testing.T, networkID string) {
t.Logf("Deleting network %s", networkID)
networks.Delete(base.Client, networkID)
}
func deletePort(t *testing.T, portID string) {
t.Logf("Deleting port %s", portID)
ports.Delete(base.Client, portID)
}
func createInternalTopology(t *testing.T) (string, string, string) {
t.Logf("Creating an internal network (for port)")
opts := networks.CreateOpts{Name: "internal_network"}
n, err := networks.Create(base.Client, opts).Extract()
th.AssertNoErr(t, err)
// A subnet is also needed
subnetID := createSubnet(t, n.ID, cidr2)
t.Logf("Creating an internal port on network %s", n.ID)
p, err := ports.Create(base.Client, ports.CreateOpts{
NetworkID: n.ID,
Name: "fixed_internal_port",
}).Extract()
th.AssertNoErr(t, err)
return n.ID, subnetID, p.ID
}
func bridgeIntSubnetWithExtNetwork(t *testing.T, networkID, subnetID string) string {
// Create router with external gateway info
routerID := createRouter(t, networkID)
// Add interface for internal subnet
addInterface(t, routerID, subnetID)
return routerID
}
func createSubnet(t *testing.T, networkID, cidr string) string {
t.Logf("Creating a subnet for network %s", networkID)
iFalse := false
s, err := subnets.Create(base.Client, subnets.CreateOpts{
NetworkID: networkID,
CIDR: cidr,
IPVersion: subnets.IPv4,
Name: "my_subnet",
EnableDHCP: &iFalse,
}).Extract()
th.AssertNoErr(t, err)
t.Logf("Subnet created: ID [%s]", s.ID)
return s.ID
}
func createRouter(t *testing.T, networkID string) string {
t.Logf("Creating a router for network %s", networkID)
asu := false
gwi := routers.GatewayInfo{NetworkID: networkID}
r, err := routers.Create(base.Client, routers.CreateOpts{
Name: "foo_router",
AdminStateUp: &asu,
GatewayInfo: &gwi,
}).Extract()
th.AssertNoErr(t, err)
if r.ID == "" {
t.Fatalf("No ID returned when creating a router")
}
t.Logf("Router created: ID [%s]", r.ID)
return r.ID
}
func listRouters(t *testing.T) {
pager := routers.List(base.Client, routers.ListOpts{})
err := pager.EachPage(func(page pagination.Page) (bool, error) {
routerList, err := routers.ExtractRouters(page)
th.AssertNoErr(t, err)
for _, r := range routerList {
t.Logf("Listing router: ID [%s] Name [%s] Status [%s] GatewayInfo [%#v]",
r.ID, r.Name, r.Status, r.GatewayInfo)
}
return true, nil
})
th.AssertNoErr(t, err)
}
func updateRouter(t *testing.T, routerID string) {
_, err := routers.Update(base.Client, routerID, routers.UpdateOpts{
Name: "another_name",
}).Extract()
th.AssertNoErr(t, err)
}
func getRouter(t *testing.T, routerID string) {
r, err := routers.Get(base.Client, routerID).Extract()
th.AssertNoErr(t, err)
t.Logf("Getting router: ID [%s] Name [%s] Status [%s]", r.ID, r.Name, r.Status)
}
func addInterface(t *testing.T, routerID, subnetID string) {
ir, err := routers.AddInterface(base.Client, routerID, routers.InterfaceOpts{SubnetID: subnetID}).Extract()
th.AssertNoErr(t, err)
t.Logf("Interface added to router %s: SubnetID [%s] PortID [%s]", routerID, ir.SubnetID, ir.PortID)
}
func removeInterface(t *testing.T, routerID, subnetID string) {
ir, err := routers.RemoveInterface(base.Client, routerID, routers.InterfaceOpts{SubnetID: subnetID}).Extract()
th.AssertNoErr(t, err)
t.Logf("Interface %s removed from %s", ir.ID, routerID)
}
func deleteRouter(t *testing.T, routerID string) {
t.Logf("Deleting router %s", routerID)
res := routers.Delete(base.Client, routerID)
th.AssertNoErr(t, res.Err)
}
func createFloatingIP(t *testing.T, networkID, portID string) string {
t.Logf("Creating floating IP on network [%s] with port [%s]", networkID, portID)
opts := floatingips.CreateOpts{
FloatingNetworkID: networkID,
PortID: portID,
}
ip, err := floatingips.Create(base.Client, opts).Extract()
th.AssertNoErr(t, err)
t.Logf("Floating IP created: ID [%s] Status [%s] Fixed (internal) IP: [%s] Floating (external) IP: [%s]",
ip.ID, ip.Status, ip.FixedIP, ip.FloatingIP)
return ip.ID
}
func getFloatingIP(t *testing.T, ipID string) {
ip, err := floatingips.Get(base.Client, ipID).Extract()
th.AssertNoErr(t, err)
t.Logf("Getting floating IP: ID [%s] Status [%s]", ip.ID, ip.Status)
}
func updateFloatingIP(t *testing.T, ipID, portID string) {
t.Logf("Disassociate all ports from IP %s", ipID)
_, err := floatingips.Update(base.Client, ipID, floatingips.UpdateOpts{PortID: ""}).Extract()
th.AssertNoErr(t, err)
t.Logf("Re-associate the port %s", portID)
_, err = floatingips.Update(base.Client, ipID, floatingips.UpdateOpts{PortID: portID}).Extract()
th.AssertNoErr(t, err)
}
func deleteFloatingIP(t *testing.T, ipID string) {
t.Logf("Deleting IP %s", ipID)
res := floatingips.Delete(base.Client, ipID)
th.AssertNoErr(t, res.Err)
}
package lbaas
import (
"testing"
base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools"
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
"github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
th "github.com/rackspace/gophercloud/testhelper"
)
func SetupTopology(t *testing.T) (string, string) {
// create network
n, err := networks.Create(base.Client, networks.CreateOpts{Name: "tmp_network"}).Extract()
th.AssertNoErr(t, err)
t.Logf("Created network %s", n.ID)
// create subnet
s, err := subnets.Create(base.Client, subnets.CreateOpts{
NetworkID: n.ID,
CIDR: "192.168.199.0/24",
IPVersion: subnets.IPv4,
Name: "tmp_subnet",
}).Extract()
th.AssertNoErr(t, err)
t.Logf("Created subnet %s", s.ID)
return n.ID, s.ID
}
func DeleteTopology(t *testing.T, networkID string) {
res := networks.Delete(base.Client, networkID)
th.AssertNoErr(t, res.Err)
t.Logf("Deleted network %s", networkID)
}
func CreatePool(t *testing.T, subnetID string) string {
p, err := pools.Create(base.Client, pools.CreateOpts{
LBMethod: pools.LBMethodRoundRobin,
Protocol: "HTTP",
Name: "tmp_pool",
SubnetID: subnetID,
}).Extract()
th.AssertNoErr(t, err)
t.Logf("Created pool %s", p.ID)
return p.ID
}
func DeletePool(t *testing.T, poolID string) {
res := pools.Delete(base.Client, poolID)
th.AssertNoErr(t, res.Err)
t.Logf("Deleted pool %s", poolID)
}
func CreateMonitor(t *testing.T) string {
m, err := monitors.Create(base.Client, monitors.CreateOpts{
Delay: 10,
Timeout: 10,
MaxRetries: 3,
Type: monitors.TypeHTTP,
ExpectedCodes: "200",
URLPath: "/login",
HTTPMethod: "GET",
}).Extract()
th.AssertNoErr(t, err)
t.Logf("Created monitor ID [%s]", m.ID)
return m.ID
}
// +build acceptance networking lbaas lbaasmember
package lbaas
import (
"testing"
base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestMembers(t *testing.T) {
base.Setup(t)
defer base.Teardown()
// setup
networkID, subnetID := SetupTopology(t)
poolID := CreatePool(t, subnetID)
// create member
memberID := createMember(t, poolID)
// list members
listMembers(t)
// update member
updateMember(t, memberID)
// get member
getMember(t, memberID)
// delete member
deleteMember(t, memberID)
// teardown
DeletePool(t, poolID)
DeleteTopology(t, networkID)
}
func createMember(t *testing.T, poolID string) string {
m, err := members.Create(base.Client, members.CreateOpts{
Address: "192.168.199.1",
ProtocolPort: 8080,
PoolID: poolID,
}).Extract()
th.AssertNoErr(t, err)
t.Logf("Created member: ID [%s] Status [%s] Weight [%d] Address [%s] Port [%d]",
m.ID, m.Status, m.Weight, m.Address, m.ProtocolPort)
return m.ID
}
func listMembers(t *testing.T) {
err := members.List(base.Client, members.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
memberList, err := members.ExtractMembers(page)
if err != nil {
t.Errorf("Failed to extract members: %v", err)
return false, err
}
for _, m := range memberList {
t.Logf("Listing member: ID [%s] Status [%s]", m.ID, m.Status)
}
return true, nil
})
th.AssertNoErr(t, err)
}
func updateMember(t *testing.T, memberID string) {
m, err := members.Update(base.Client, memberID, members.UpdateOpts{AdminStateUp: true}).Extract()
th.AssertNoErr(t, err)
t.Logf("Updated member ID [%s]", m.ID)
}
func getMember(t *testing.T, memberID string) {
m, err := members.Get(base.Client, memberID).Extract()
th.AssertNoErr(t, err)
t.Logf("Getting member ID [%s]", m.ID)
}
func deleteMember(t *testing.T, memberID string) {
res := members.Delete(base.Client, memberID)
th.AssertNoErr(t, res.Err)
t.Logf("Deleted member %s", memberID)
}
// +build acceptance networking lbaas lbaasmonitor
package lbaas
import (
"testing"
base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestMonitors(t *testing.T) {
base.Setup(t)
defer base.Teardown()
// create monitor
monitorID := CreateMonitor(t)
// list monitors
listMonitors(t)
// update monitor
updateMonitor(t, monitorID)
// get monitor
getMonitor(t, monitorID)
// delete monitor
deleteMonitor(t, monitorID)
}
func listMonitors(t *testing.T) {
err := monitors.List(base.Client, monitors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
monitorList, err := monitors.ExtractMonitors(page)
if err != nil {
t.Errorf("Failed to extract monitors: %v", err)
return false, err
}
for _, m := range monitorList {
t.Logf("Listing monitor: ID [%s] Type [%s] Delay [%ds] Timeout [%d] Retries [%d] Status [%s]",
m.ID, m.Type, m.Delay, m.Timeout, m.MaxRetries, m.Status)
}
return true, nil
})
th.AssertNoErr(t, err)
}
func updateMonitor(t *testing.T, monitorID string) {
opts := monitors.UpdateOpts{Delay: 10, Timeout: 10, MaxRetries: 3}
m, err := monitors.Update(base.Client, monitorID, opts).Extract()
th.AssertNoErr(t, err)
t.Logf("Updated monitor ID [%s]", m.ID)
}
func getMonitor(t *testing.T, monitorID string) {
m, err := monitors.Get(base.Client, monitorID).Extract()
th.AssertNoErr(t, err)
t.Logf("Getting monitor ID [%s]: URL path [%s] HTTP Method [%s] Accepted codes [%s]",
m.ID, m.URLPath, m.HTTPMethod, m.ExpectedCodes)
}
func deleteMonitor(t *testing.T, monitorID string) {
res := monitors.Delete(base.Client, monitorID)
th.AssertNoErr(t, res.Err)
t.Logf("Deleted monitor %s", monitorID)
}
// +build acceptance networking lbaas lbaaspool
package lbaas
import (
"testing"
base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestPools(t *testing.T) {
base.Setup(t)
defer base.Teardown()
// setup
networkID, subnetID := SetupTopology(t)
// create pool
poolID := CreatePool(t, subnetID)
// list pools
listPools(t)
// update pool
updatePool(t, poolID)
// get pool
getPool(t, poolID)
// create monitor
monitorID := CreateMonitor(t)
// associate health monitor
associateMonitor(t, poolID, monitorID)
// disassociate health monitor
disassociateMonitor(t, poolID, monitorID)
// delete pool
DeletePool(t, poolID)
// teardown
DeleteTopology(t, networkID)
}
func listPools(t *testing.T) {
err := pools.List(base.Client, pools.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
poolList, err := pools.ExtractPools(page)
if err != nil {
t.Errorf("Failed to extract pools: %v", err)
return false, err
}
for _, p := range poolList {
t.Logf("Listing pool: ID [%s] Name [%s] Status [%s] LB algorithm [%s]", p.ID, p.Name, p.Status, p.LBMethod)
}
return true, nil
})
th.AssertNoErr(t, err)
}
func updatePool(t *testing.T, poolID string) {
opts := pools.UpdateOpts{Name: "SuperPool", LBMethod: pools.LBMethodLeastConnections}
p, err := pools.Update(base.Client, poolID, opts).Extract()
th.AssertNoErr(t, err)
t.Logf("Updated pool ID [%s]", p.ID)
}
func getPool(t *testing.T, poolID string) {
p, err := pools.Get(base.Client, poolID).Extract()
th.AssertNoErr(t, err)
t.Logf("Getting pool ID [%s]", p.ID)
}
func associateMonitor(t *testing.T, poolID, monitorID string) {
res := pools.AssociateMonitor(base.Client, poolID, monitorID)
th.AssertNoErr(t, res.Err)
t.Logf("Associated pool %s with monitor %s", poolID, monitorID)
}
func disassociateMonitor(t *testing.T, poolID, monitorID string) {
res := pools.DisassociateMonitor(base.Client, poolID, monitorID)
th.AssertNoErr(t, res.Err)
t.Logf("Disassociated pool %s with monitor %s", poolID, monitorID)
}
// +build acceptance networking lbaas lbaasvip
package lbaas
import (
"testing"
base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestVIPs(t *testing.T) {
base.Setup(t)
defer base.Teardown()
// setup
networkID, subnetID := SetupTopology(t)
poolID := CreatePool(t, subnetID)
// create VIP
VIPID := createVIP(t, subnetID, poolID)
// list VIPs
listVIPs(t)
// update VIP
updateVIP(t, VIPID)
// get VIP
getVIP(t, VIPID)
// delete VIP
deleteVIP(t, VIPID)
// teardown
DeletePool(t, poolID)
DeleteTopology(t, networkID)
}
func createVIP(t *testing.T, subnetID, poolID string) string {
p, err := vips.Create(base.Client, vips.CreateOpts{
Protocol: "HTTP",
Name: "New_VIP",
AdminStateUp: vips.Up,
SubnetID: subnetID,
PoolID: poolID,
ProtocolPort: 80,
}).Extract()
th.AssertNoErr(t, err)
t.Logf("Created pool %s", p.ID)
return p.ID
}
func listVIPs(t *testing.T) {
err := vips.List(base.Client, vips.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
vipList, err := vips.ExtractVIPs(page)
if err != nil {
t.Errorf("Failed to extract VIPs: %v", err)
return false, err
}
for _, vip := range vipList {
t.Logf("Listing VIP: ID [%s] Name [%s] Address [%s] Port [%s] Connection Limit [%d]",
vip.ID, vip.Name, vip.Address, vip.ProtocolPort, vip.ConnLimit)
}
return true, nil
})
th.AssertNoErr(t, err)
}
func updateVIP(t *testing.T, VIPID string) {
i1000 := 1000
_, err := vips.Update(base.Client, VIPID, vips.UpdateOpts{ConnLimit: &i1000}).Extract()
th.AssertNoErr(t, err)
t.Logf("Updated VIP ID [%s]", VIPID)
}
func getVIP(t *testing.T, VIPID string) {
vip, err := vips.Get(base.Client, VIPID).Extract()
th.AssertNoErr(t, err)
t.Logf("Getting VIP ID [%s]: Status [%s]", vip.ID, vip.Status)
}
func deleteVIP(t *testing.T, VIPID string) {
res := vips.Delete(base.Client, VIPID)
th.AssertNoErr(t, res.Err)
t.Logf("Deleted VIP %s", VIPID)
}
// +build acceptance networking
package extensions
import (
"strconv"
"testing"
base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestNetworkCRUDOperations(t *testing.T) {
base.Setup(t)
defer base.Teardown()
// Create a network
n, err := networks.Create(base.Client, networks.CreateOpts{Name: "sample_network", AdminStateUp: networks.Up}).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, n.Name, "sample_network")
th.AssertEquals(t, n.AdminStateUp, true)
networkID := n.ID
// List networks
pager := networks.List(base.Client, networks.ListOpts{Limit: 2})
err = pager.EachPage(func(page pagination.Page) (bool, error) {
t.Logf("--- Page ---")
networkList, err := networks.ExtractNetworks(page)
th.AssertNoErr(t, err)
for _, n := range networkList {
t.Logf("Network: ID [%s] Name [%s] Status [%s] Is shared? [%s]",
n.ID, n.Name, n.Status, strconv.FormatBool(n.Shared))
}
return true, nil
})
th.CheckNoErr(t, err)
// Get a network
if networkID == "" {
t.Fatalf("In order to retrieve a network, the NetworkID must be set")
}
n, err = networks.Get(base.Client, networkID).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, n.Status, "ACTIVE")
th.AssertDeepEquals(t, n.Subnets, []string{})
th.AssertEquals(t, n.Name, "sample_network")
th.AssertEquals(t, n.AdminStateUp, true)
th.AssertEquals(t, n.Shared, false)
th.AssertEquals(t, n.ID, networkID)
// Update network
n, err = networks.Update(base.Client, networkID, networks.UpdateOpts{Name: "new_network_name"}).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, n.Name, "new_network_name")
// Delete network
res := networks.Delete(base.Client, networkID)
th.AssertNoErr(t, res.Err)
}
func TestCreateMultipleNetworks(t *testing.T) {
//networks.CreateMany()
}
// +build acceptance networking security
package extensions
import (
"testing"
base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules"
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
"github.com/rackspace/gophercloud/openstack/networking/v2/ports"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestSecurityGroups(t *testing.T) {
base.Setup(t)
defer base.Teardown()
// create security group
groupID := createSecGroup(t)
// delete security group
defer deleteSecGroup(t, groupID)
// list security group
listSecGroups(t)
// get security group
getSecGroup(t, groupID)
// create port with security group
networkID, portID := createPort(t, groupID)
// teardown
defer deleteNetwork(t, networkID)
// delete port
defer deletePort(t, portID)
}
func TestSecurityGroupRules(t *testing.T) {
base.Setup(t)
defer base.Teardown()
// create security group
groupID := createSecGroup(t)
defer deleteSecGroup(t, groupID)
// create security group rule
ruleID := createSecRule(t, groupID)
// delete security group rule
defer deleteSecRule(t, ruleID)
// list security group rule
listSecRules(t)
// get security group rule
getSecRule(t, ruleID)
}
func createSecGroup(t *testing.T) string {
sg, err := groups.Create(base.Client, groups.CreateOpts{
Name: "new-webservers",
Description: "security group for webservers",
}).Extract()
th.AssertNoErr(t, err)
t.Logf("Created security group %s", sg.ID)
return sg.ID
}
func listSecGroups(t *testing.T) {
err := groups.List(base.Client, groups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
list, err := groups.ExtractGroups(page)
if err != nil {
t.Errorf("Failed to extract secgroups: %v", err)
return false, err
}
for _, sg := range list {
t.Logf("Listing security group: ID [%s] Name [%s]", sg.ID, sg.Name)
}
return true, nil
})
th.AssertNoErr(t, err)
}
func getSecGroup(t *testing.T, id string) {
sg, err := groups.Get(base.Client, id).Extract()
th.AssertNoErr(t, err)
t.Logf("Getting security group: ID [%s] Name [%s] Description [%s]", sg.ID, sg.Name, sg.Description)
}
func createPort(t *testing.T, groupID string) (string, string) {
n, err := networks.Create(base.Client, networks.CreateOpts{Name: "tmp_network"}).Extract()
th.AssertNoErr(t, err)
t.Logf("Created network %s", n.ID)
opts := ports.CreateOpts{
NetworkID: n.ID,
Name: "my_port",
SecurityGroups: []string{groupID},
}
p, err := ports.Create(base.Client, opts).Extract()
th.AssertNoErr(t, err)
t.Logf("Created port %s with security group %s", p.ID, groupID)
return n.ID, p.ID
}
func deleteSecGroup(t *testing.T, groupID string) {
res := groups.Delete(base.Client, groupID)
th.AssertNoErr(t, res.Err)
t.Logf("Deleted security group %s", groupID)
}
func createSecRule(t *testing.T, groupID string) string {
r, err := rules.Create(base.Client, rules.CreateOpts{
Direction: "ingress",
PortRangeMin: 80,
EtherType: "IPv4",
PortRangeMax: 80,
Protocol: "tcp",
SecGroupID: groupID,
}).Extract()
th.AssertNoErr(t, err)
t.Logf("Created security group rule %s", r.ID)
return r.ID
}
func listSecRules(t *testing.T) {
err := rules.List(base.Client, rules.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
list, err := rules.ExtractRules(page)
if err != nil {
t.Errorf("Failed to extract sec rules: %v", err)
return false, err
}
for _, r := range list {
t.Logf("Listing security rule: ID [%s]", r.ID)
}
return true, nil
})
th.AssertNoErr(t, err)
}
func getSecRule(t *testing.T, id string) {
r, err := rules.Get(base.Client, id).Extract()
th.AssertNoErr(t, err)
t.Logf("Getting security rule: ID [%s] Direction [%s] EtherType [%s] Protocol [%s]",
r.ID, r.Direction, r.EtherType, r.Protocol)
}
func deleteSecRule(t *testing.T, id string) {
res := rules.Delete(base.Client, id)
th.AssertNoErr(t, res.Err)
t.Logf("Deleted security rule %s", id)
}
// +build acceptance networking
package v2
import (
"strconv"
"testing"
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestNetworkCRUDOperations(t *testing.T) {
Setup(t)
defer Teardown()
// Create a network
n, err := networks.Create(Client, networks.CreateOpts{Name: "sample_network", AdminStateUp: networks.Up}).Extract()
th.AssertNoErr(t, err)
defer networks.Delete(Client, n.ID)
th.AssertEquals(t, n.Name, "sample_network")
th.AssertEquals(t, n.AdminStateUp, true)
networkID := n.ID
// List networks
pager := networks.List(Client, networks.ListOpts{Limit: 2})
err = pager.EachPage(func(page pagination.Page) (bool, error) {
t.Logf("--- Page ---")
networkList, err := networks.ExtractNetworks(page)
th.AssertNoErr(t, err)
for _, n := range networkList {
t.Logf("Network: ID [%s] Name [%s] Status [%s] Is shared? [%s]",
n.ID, n.Name, n.Status, strconv.FormatBool(n.Shared))
}
return true, nil
})
th.CheckNoErr(t, err)
// Get a network
if networkID == "" {
t.Fatalf("In order to retrieve a network, the NetworkID must be set")
}
n, err = networks.Get(Client, networkID).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, n.Status, "ACTIVE")
th.AssertDeepEquals(t, n.Subnets, []string{})
th.AssertEquals(t, n.Name, "sample_network")
th.AssertEquals(t, n.AdminStateUp, true)
th.AssertEquals(t, n.Shared, false)
th.AssertEquals(t, n.ID, networkID)
// Update network
n, err = networks.Update(Client, networkID, networks.UpdateOpts{Name: "new_network_name"}).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, n.Name, "new_network_name")
// Delete network
res := networks.Delete(Client, networkID)
th.AssertNoErr(t, res.Err)
}
func TestCreateMultipleNetworks(t *testing.T) {
//networks.CreateMany()
}
// +build acceptance networking
package v2
import (
"testing"
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
"github.com/rackspace/gophercloud/openstack/networking/v2/ports"
"github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestPortCRUD(t *testing.T) {
Setup(t)
defer Teardown()
// Setup network
t.Log("Setting up network")
networkID, err := createNetwork()
th.AssertNoErr(t, err)
defer networks.Delete(Client, networkID)
// Setup subnet
t.Logf("Setting up subnet on network %s", networkID)
subnetID, err := createSubnet(networkID)
th.AssertNoErr(t, err)
defer subnets.Delete(Client, subnetID)
// Create port
t.Logf("Create port based on subnet %s", subnetID)
portID := createPort(t, networkID, subnetID)
// List ports
t.Logf("Listing all ports")
listPorts(t)
// Get port
if portID == "" {
t.Fatalf("In order to retrieve a port, the portID must be set")
}
p, err := ports.Get(Client, portID).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, p.ID, portID)
// Update port
p, err = ports.Update(Client, portID, ports.UpdateOpts{Name: "new_port_name"}).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, p.Name, "new_port_name")
// Delete port
res := ports.Delete(Client, portID)
th.AssertNoErr(t, res.Err)
}
func createPort(t *testing.T, networkID, subnetID string) string {
enable := false
opts := ports.CreateOpts{
NetworkID: networkID,
Name: "my_port",
AdminStateUp: &enable,
FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}},
}
p, err := ports.Create(Client, opts).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, p.NetworkID, networkID)
th.AssertEquals(t, p.Name, "my_port")
th.AssertEquals(t, p.AdminStateUp, false)
return p.ID
}
func listPorts(t *testing.T) {
count := 0
pager := ports.List(Client, ports.ListOpts{})
err := pager.EachPage(func(page pagination.Page) (bool, error) {
count++
t.Logf("--- Page ---")
portList, err := ports.ExtractPorts(page)
th.AssertNoErr(t, err)
for _, p := range portList {
t.Logf("Port: ID [%s] Name [%s] Status [%d] MAC addr [%s] Fixed IPs [%#v] Security groups [%#v]",
p.ID, p.Name, p.Status, p.MACAddress, p.FixedIPs, p.SecurityGroups)
}
return true, nil
})
th.CheckNoErr(t, err)
if count == 0 {
t.Logf("No pages were iterated over when listing ports")
}
}
func createNetwork() (string, error) {
res, err := networks.Create(Client, networks.CreateOpts{Name: "tmp_network", AdminStateUp: networks.Up}).Extract()
return res.ID, err
}
func createSubnet(networkID string) (string, error) {
s, err := subnets.Create(Client, subnets.CreateOpts{
NetworkID: networkID,
CIDR: "192.168.199.0/24",
IPVersion: subnets.IPv4,
Name: "my_subnet",
EnableDHCP: subnets.Down,
}).Extract()
return s.ID, err
}
func TestPortBatchCreate(t *testing.T) {
// todo
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment