Merge pull request #2051 from anguslees/openstack-provider

Openstack provider
This commit is contained in:
Brendan Burns 2014-12-23 09:33:19 -08:00
commit c7d3b12d43
459 changed files with 32174 additions and 4452 deletions

4
Godeps/Godeps.json generated
View File

@ -152,8 +152,8 @@
},
{
"ImportPath": "github.com/rackspace/gophercloud",
"Comment": "v0.1.0-31-ge13cda2",
"Rev": "e13cda260ce48d63ce816f4fa72b6c6cd096596d"
"Comment": "v1.0.0",
"Rev": "da56de6a59e53fdd61be1b5d9b87df34c47ac420"
},
{
"ImportPath": "github.com/skratchdot/open-golang/open",

View File

@ -1,16 +0,0 @@
# 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

View File

@ -1,14 +1,14 @@
language: go
install:
- go get -v .
- go get -v -tags 'fixtures acceptance' ./...
go:
- 1.1
- 1.2
- tip
script: script/cibuild
after_success:
- go get code.google.com/p/go.tools/cmd/cover
- go get github.com/axw/gocov/gocov
- go get github.com/mattn/goveralls
- export PATH=$PATH:$HOME/gopath/bin/
- goveralls 2k7PTU3xa474Hymwgdj6XjqenNfGTNkO8

View File

@ -0,0 +1,275 @@
# Contributing to gophercloud
- [Getting started](#getting-started)
- [Tests](#tests)
- [Style guide](#basic-style-guide)
- [5 ways to get involved](#5-ways-to-get-involved)
## Setting up your git workspace
As a contributor you will need to setup your workspace in a slightly different
way than just downloading it. Here are the basic installation instructions:
1. Configure your `$GOPATH` and run `go get` as described in the main
[README](/#how-to-install).
2. Move into the directory that houses your local repository:
```bash
cd ${GOPATH}/src/github.com/rackspace/gophercloud
```
3. Fork the `rackspace/gophercloud` repository and update your remote refs. You
will need to rename the `origin` remote branch to `upstream`, and add your
fork as `origin` instead:
```bash
git remote rename origin upstream
git remote add origin git@github.com/<my_username>/gophercloud
```
4. Checkout the latest development branch ([click here](/branches) to see all
the branches):
```bash
git checkout release/v1.0.1
```
5. If you're working on something (discussed more in detail below), you will
need to checkout a new feature branch:
```bash
git checkout -b my-new-feature
```
Another thing to bear in mind is that you will need to add a few extra
environment variables for acceptance tests - this is documented in our
[acceptance tests readme](/acceptance).
## Tests
When working on a new or existing feature, testing will be the backbone of your
work since it helps uncover and prevent regressions in the codebase. There are
two types of test we use in gophercloud: unit tests and acceptance tests, which
are both described below.
### Unit tests
Unit tests are the fine-grained tests that establish and ensure the behaviour
of individual units of functionality. We usually test on an
operation-by-operation basis (an operation typically being an API action) with
the use of mocking to set up explicit expectations. Each operation will set up
its HTTP response expectation, and then test how the system responds when fed
this controlled, pre-determined input.
To make life easier, we've introduced a bunch of test helpers to simplify the
process of testing expectations with assertions:
```go
import (
"testing"
"github.com/rackspace/gophercloud/testhelper"
)
func TestSomething(t *testing.T) {
result, err := Operation()
testhelper.AssertEquals(t, "foo", result.Bar)
testhelper.AssertNoErr(t, err)
}
func TestSomethingElse(t *testing.T) {
testhelper.CheckEquals(t, "expected", "actual")
}
```
`AssertEquals` and `AssertNoErr` will throw a fatal error if a value does not
match an expected value or if an error has been declared, respectively. You can
also use `CheckEquals` and `CheckNoErr` for the same purpose; the only difference
being that `t.Errorf` is raised rather than `t.Fatalf`.
Here is a truncated example of mocked HTTP responses:
```go
import (
"testing"
th "github.com/rackspace/gophercloud/testhelper"
fake "github.com/rackspace/gophercloud/testhelper/client"
)
func TestGet(t *testing.T) {
// Setup the HTTP request multiplexer and server
th.SetupHTTP()
defer th.TeardownHTTP()
th.Mux.HandleFunc("/networks/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
// Test we're using the correct HTTP method
th.TestMethod(t, r, "GET")
// Test we're setting the auth token
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
// Set the appropriate headers for our mocked response
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
// Set the HTTP body
fmt.Fprintf(w, `
{
"network": {
"status": "ACTIVE",
"name": "private-network",
"admin_state_up": true,
"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
"shared": true,
"id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
}
}
`)
})
// Call our API operation
network, err := Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
// Assert no errors and equality
th.AssertNoErr(t, err)
th.AssertEquals(t, n.Status, "ACTIVE")
}
```
### Acceptance tests
As we've already mentioned, unit tests have a very narrow and confined focus -
they test small units of behaviour. Acceptance tests on the other hand have a
far larger scope: they are fully functional tests that test the entire API of a
service in one fell swoop. They don't care about unit isolation or mocking
expectations, they instead do a full run-through and consequently test how the
entire system _integrates_ together. When an API satisfies expectations, it
proves by default that the requirements for a contract have been met.
Please be aware that acceptance tests will hit a live API - and may incur
service charges from your provider. Although most tests handle their own
teardown procedures, it is always worth manually checking that resources are
deleted after the test suite finishes.
### Running tests
To run all tests:
```bash
go test ./...
```
To run all tests with verbose output:
```bash
go test -v ./...
```
To run tests that match certain [build tags]():
```bash
go test -tags "foo bar" ./...
```
To run tests for a particular sub-package:
```bash
cd ./path/to/package && go test .
```
## Basic style guide
We follow the standard formatting recommendations and language idioms set out
in the [Effective Go](https://golang.org/doc/effective_go.html) guide. It's
definitely worth reading - but the relevant sections are
[formatting](https://golang.org/doc/effective_go.html#formatting)
and [names](https://golang.org/doc/effective_go.html#names).
## 5 ways to get involved
There are five main ways you can get involved in our open-source project, and
each is described briefly below. Once you've made up your mind and decided on
your fix, you will need to follow the same basic steps that all submissions are
required to adhere to:
1. [fork](https://help.github.com/articles/fork-a-repo/) the `rackspace/gophercloud` repository
2. checkout a [new branch](https://github.com/Kunena/Kunena-Forum/wiki/Create-a-new-branch-with-git-and-manage-branches)
3. submit your branch as a [pull request](https://help.github.com/articles/creating-a-pull-request/)
### 1. Providing feedback
On of the easiest ways to get readily involved in our project is to let us know
about your experiences using our SDK. Feedback like this is incredibly useful
to us, because it allows us to refine and change features based on what our
users want and expect of us. There are a bunch of ways to get in contact! You
can [ping us](mailto:sdk-support@rackspace.com) via e-mail, talk to us on irc
(#rackspace-dev on freenode), [tweet us](https://twitter.com/rackspace), or
submit an issue on our [bug tracker](/issues). Things you might like to tell us
are:
* how easy was it to start using our SDK?
* did it meet your expectations? If not, why not?
* did our documentation help or hinder you?
* what could we improve in general?
### 2. Fixing bugs
If you want to start fixing open bugs, we'd really appreciate that! Bug fixing
is central to any project. The best way to get started is by heading to our
[bug tracker](https://github.com/rackspace/gophercloud/issues) and finding open
bugs that you think nobody is working on. It might be useful to comment on the
thread to see the current state of the issue and if anybody has made any
breakthroughs on it so far.
### 3. Improving documentation
We have three forms of documentation:
* short README documents that briefly introduce a topic
* reference documentation on [godoc.org](http://godoc.org) that is automatically
generated from source code comments
* user documentation on our [homepage](http://gophercloud.io) that includes
getting started guides, installation guides and code samples
If you feel that a certain section could be improved - whether it's to clarify
ambiguity, correct a technical mistake, or to fix a grammatical error - please
feel entitled to do so! We welcome doc pull requests with the same childlike
enthusiasm as any other contribution!
### 4. Optimizing existing features
If you would like to improve or optimize an existing feature, please be aware
that we adhere to [semantic versioning](http://semver.org) - which means that
we cannot introduce breaking changes to the API without a major version change
(v1.x -> v2.x). Making that leap is a big step, so we encourage contributors to
refactor rather than rewrite. Running tests will prevent regression and avoid
the possibility of breaking somebody's current implementation.
Another tip is to keep the focus of your work as small as possible - try not to
introduce a change that affects lots and lots of files because it introduces
added risk and increases the cognitive load on the reviewers checking your
work. Change-sets which are easily understood and will not negatively impact
users are more likely to be integrated quickly.
Lastly, if you're seeking to optimize a particular operation, you should try to
demonstrate a negative performance impact - perhaps using go's inbuilt
[benchmark capabilities](http://dave.cheney.net/2013/06/30/how-to-write-benchmarks-in-go).
### 5. Working on a new feature
If you've found something we've left out, definitely feel free to start work on
introducing that feature. It's always useful to open an issue or submit a pull
request early on to indicate your intent to a core contributor - this enables
quick/early feedback and can help steer you in the right direction by avoiding
known issues. It might also help you avoid losing time implementing something
that might not ever work. One tip is to prefix your Pull Request issue title
with [wip] - then people know it's a work in progress.
You must ensure that all of your work is well tested - both in terms of unit
and acceptance tests. Untested code will not be merged because it introduces
too much of a risk to end-users.
Happy hacking!

View File

@ -1,6 +1,12 @@
Contributors
============
Samuel A. Falvo II <sam.falvo@rackspace.com>
Glen Campbell <glen.campbell@rackspace.com>
Jesse Noller <jesse.noller@rackspace.com>
| Name | Email |
| ---- | ----- |
| 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>

View File

@ -1,44 +0,0 @@
== 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

View File

@ -0,0 +1,161 @@
# 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).

View File

@ -0,0 +1,338 @@
# 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.

View File

@ -1,30 +0,0 @@
// +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)
}
}

View File

@ -1,22 +0,0 @@
// +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)
}
}

View File

@ -1,62 +0,0 @@
// +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)
}
}
}

View File

@ -1,134 +0,0 @@
// +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
}

View File

@ -1,47 +0,0 @@
// +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)
}
}
})
})
}

View File

@ -1,32 +0,0 @@
// +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)
}
}
})
})
}

View File

@ -1,32 +0,0 @@
// +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)
}
}
})
})
}

View File

@ -1,49 +0,0 @@
// +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.")
}
})
})
}

View File

@ -1,50 +0,0 @@
// +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")
}
})
})
}

View File

@ -1,102 +0,0 @@
// +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)
}
}

View File

@ -1,45 +0,0 @@
// +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)
}
}

View File

@ -1,52 +0,0 @@
// +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)
}
}

View File

@ -1,46 +0,0 @@
// +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)
}
}

View File

@ -1,46 +0,0 @@
// +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)
}
}

View File

@ -1,66 +0,0 @@
// +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...)
}
}

View File

@ -1,32 +0,0 @@
// +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)
}
}
})
})
}

View File

@ -1,45 +0,0 @@
// +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)
}
})
})
}

View File

@ -1,52 +0,0 @@
// +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)
}
}

View File

@ -1,19 +0,0 @@
// +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)
}
}

View File

@ -1,58 +0,0 @@
// +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...)
}
}

View File

@ -1,48 +0,0 @@
// +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)
}
})
})
}

View File

@ -0,0 +1,57 @@
# 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
```

View File

@ -1,239 +0,0 @@
// +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")
}

View File

@ -0,0 +1,70 @@
// +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")
}

View File

@ -0,0 +1,63 @@
// +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)
}

View File

@ -0,0 +1,49 @@
// +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)
}

View File

@ -0,0 +1,40 @@
// +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)
}
}

View File

@ -0,0 +1,50 @@
// +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)
}

View File

@ -0,0 +1,97 @@
// +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
}

View File

@ -0,0 +1,47 @@
// +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)
}

View File

@ -0,0 +1,57 @@
// +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)
}

View File

@ -0,0 +1,37 @@
// +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)
}

View File

@ -0,0 +1,3 @@
// The v2 package contains acceptance tests for the Openstack Compute V2 service.
package v2

View File

@ -0,0 +1,393 @@
// +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)
}
}

View File

@ -0,0 +1,46 @@
// +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)
}

View File

@ -0,0 +1,47 @@
// +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)
}

View File

@ -0,0 +1 @@
package v2

View File

@ -0,0 +1,32 @@
// +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)
}

View File

@ -0,0 +1,38 @@
// +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)
}
}
}

View File

@ -0,0 +1,111 @@
// +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)
}

View File

@ -0,0 +1,39 @@
// +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)
}

View File

@ -0,0 +1 @@
package v3

View File

@ -0,0 +1,36 @@
// +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)
}
}

View File

@ -0,0 +1,42 @@
// +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)
}

View File

@ -0,0 +1,51 @@
// +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)
}

View File

@ -0,0 +1,39 @@
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
}

View File

@ -0,0 +1,45 @@
// +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")
}

View File

@ -0,0 +1,300 @@
// +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)
}

View File

@ -0,0 +1,78 @@
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
}

View File

@ -0,0 +1,95 @@
// +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)
}

View File

@ -0,0 +1,77 @@
// +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)
}

View File

@ -0,0 +1,98 @@
// +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)
}

View File

@ -0,0 +1,101 @@
// +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)
}

View File

@ -0,0 +1 @@
package extensions

View File

@ -0,0 +1,68 @@
// +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()
}

View File

@ -0,0 +1,171 @@
// +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)
}

View File

@ -0,0 +1,68 @@
// +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()
}

View File

@ -0,0 +1 @@
package v2

View File

@ -0,0 +1,117 @@
// +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
}

View File

@ -0,0 +1,86 @@
// +build acceptance networking
package v2
import (
"testing"
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
"github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestList(t *testing.T) {
Setup(t)
defer Teardown()
pager := subnets.List(Client, subnets.ListOpts{Limit: 2})
err := pager.EachPage(func(page pagination.Page) (bool, error) {
t.Logf("--- Page ---")
subnetList, err := subnets.ExtractSubnets(page)
th.AssertNoErr(t, err)
for _, s := range subnetList {
t.Logf("Subnet: ID [%s] Name [%s] IP Version [%d] CIDR [%s] GatewayIP [%s]",
s.ID, s.Name, s.IPVersion, s.CIDR, s.GatewayIP)
}
return true, nil
})
th.CheckNoErr(t, err)
}
func TestCRUD(t *testing.T) {
Setup(t)
defer Teardown()
// Setup network
t.Log("Setting up network")
n, err := networks.Create(Client, networks.CreateOpts{Name: "tmp_network", AdminStateUp: networks.Up}).Extract()
th.AssertNoErr(t, err)
networkID := n.ID
defer networks.Delete(Client, networkID)
// Create subnet
t.Log("Create subnet")
enable := false
opts := subnets.CreateOpts{
NetworkID: networkID,
CIDR: "192.168.199.0/24",
IPVersion: subnets.IPv4,
Name: "my_subnet",
EnableDHCP: &enable,
}
s, err := subnets.Create(Client, opts).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, s.NetworkID, networkID)
th.AssertEquals(t, s.CIDR, "192.168.199.0/24")
th.AssertEquals(t, s.IPVersion, 4)
th.AssertEquals(t, s.Name, "my_subnet")
th.AssertEquals(t, s.EnableDHCP, false)
subnetID := s.ID
// Get subnet
t.Log("Getting subnet")
s, err = subnets.Get(Client, subnetID).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, s.ID, subnetID)
// Update subnet
t.Log("Update subnet")
s, err = subnets.Update(Client, subnetID, subnets.UpdateOpts{Name: "new_subnet_name"}).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, s.Name, "new_subnet_name")
// Delete subnet
t.Log("Delete subnet")
res := subnets.Delete(Client, subnetID)
th.AssertNoErr(t, res.Err)
}
func TestBatchCreate(t *testing.T) {
// todo
}

View File

@ -0,0 +1,44 @@
// +build acceptance
package v1
import (
"strings"
"testing"
"github.com/rackspace/gophercloud/openstack/objectstorage/v1/accounts"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestAccounts(t *testing.T) {
// Create a provider client for making the HTTP requests.
// See common.go in this directory for more information.
client := newClient(t)
// Update an account's metadata.
updateres := accounts.Update(client, accounts.UpdateOpts{Metadata: metadata})
th.AssertNoErr(t, updateres.Err)
// Defer the deletion of the metadata set above.
defer func() {
tempMap := make(map[string]string)
for k := range metadata {
tempMap[k] = ""
}
updateres = accounts.Update(client, accounts.UpdateOpts{Metadata: tempMap})
th.AssertNoErr(t, updateres.Err)
}()
// Retrieve account metadata.
getres := accounts.Get(client, nil)
th.AssertNoErr(t, getres.Err)
// Extract the custom metadata from the 'Get' response.
am, err := getres.ExtractMetadata()
th.AssertNoErr(t, err)
for k := range metadata {
if am[k] != metadata[strings.Title(k)] {
t.Errorf("Expected custom metadata with key: %s", k)
return
}
}
}

View File

@ -0,0 +1,28 @@
// +build acceptance
package v1
import (
"os"
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack"
th "github.com/rackspace/gophercloud/testhelper"
)
var metadata = map[string]string{"gopher": "cloud"}
func newClient(t *testing.T) *gophercloud.ServiceClient {
ao, err := openstack.AuthOptionsFromEnv()
th.AssertNoErr(t, err)
client, err := openstack.AuthenticatedClient(ao)
th.AssertNoErr(t, err)
c, err := openstack.NewObjectStorageV1(client, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
th.AssertNoErr(t, err)
return c
}

View File

@ -0,0 +1,89 @@
// +build acceptance
package v1
import (
"strings"
"testing"
"github.com/rackspace/gophercloud/acceptance/tools"
"github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
// numContainers is the number of containers to create for testing.
var numContainers = 2
func TestContainers(t *testing.T) {
// Create a new client to execute the HTTP requests. See common.go for newClient body.
client := newClient(t)
// Create a slice of random container names.
cNames := make([]string, numContainers)
for i := 0; i < numContainers; i++ {
cNames[i] = tools.RandomString("gophercloud-test-container-", 8)
}
// Create numContainers containers.
for i := 0; i < len(cNames); i++ {
res := containers.Create(client, cNames[i], nil)
th.AssertNoErr(t, res.Err)
}
// Delete the numContainers containers after function completion.
defer func() {
for i := 0; i < len(cNames); i++ {
res := containers.Delete(client, cNames[i])
th.AssertNoErr(t, res.Err)
}
}()
// List the numContainer names that were just created. To just list those,
// the 'prefix' parameter is used.
err := containers.List(client, &containers.ListOpts{Full: true, Prefix: "gophercloud-test-container-"}).EachPage(func(page pagination.Page) (bool, error) {
containerList, err := containers.ExtractInfo(page)
th.AssertNoErr(t, err)
for _, n := range containerList {
t.Logf("Container: Name [%s] Count [%d] Bytes [%d]",
n.Name, n.Count, n.Bytes)
}
return true, nil
})
th.AssertNoErr(t, err)
// List the info for the numContainer containers that were created.
err = containers.List(client, &containers.ListOpts{Full: false, Prefix: "gophercloud-test-container-"}).EachPage(func(page pagination.Page) (bool, error) {
containerList, err := containers.ExtractNames(page)
th.AssertNoErr(t, err)
for _, n := range containerList {
t.Logf("Container: Name [%s]", n)
}
return true, nil
})
th.AssertNoErr(t, err)
// Update one of the numContainer container metadata.
updateres := containers.Update(client, cNames[0], &containers.UpdateOpts{Metadata: metadata})
th.AssertNoErr(t, updateres.Err)
// After the tests are done, delete the metadata that was set.
defer func() {
tempMap := make(map[string]string)
for k := range metadata {
tempMap[k] = ""
}
res := containers.Update(client, cNames[0], &containers.UpdateOpts{Metadata: tempMap})
th.AssertNoErr(t, res.Err)
}()
// Retrieve a container's metadata.
cm, err := containers.Get(client, cNames[0]).ExtractMetadata()
th.AssertNoErr(t, err)
for k := range metadata {
if cm[k] != metadata[strings.Title(k)] {
t.Errorf("Expected custom metadata with key: %s", k)
}
}
}

View File

@ -0,0 +1,119 @@
// +build acceptance
package v1
import (
"bytes"
"strings"
"testing"
"github.com/rackspace/gophercloud/acceptance/tools"
"github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers"
"github.com/rackspace/gophercloud/openstack/objectstorage/v1/objects"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
// numObjects is the number of objects to create for testing.
var numObjects = 2
func TestObjects(t *testing.T) {
// Create a provider client for executing the HTTP request.
// See common.go for more information.
client := newClient(t)
// Make a slice of length numObjects to hold the random object names.
oNames := make([]string, numObjects)
for i := 0; i < len(oNames); i++ {
oNames[i] = tools.RandomString("test-object-", 8)
}
// Create a container to hold the test objects.
cName := tools.RandomString("test-container-", 8)
header, err := containers.Create(client, cName, nil).ExtractHeader()
th.AssertNoErr(t, err)
t.Logf("Create object headers: %+v\n", header)
// Defer deletion of the container until after testing.
defer func() {
res := containers.Delete(client, cName)
th.AssertNoErr(t, res.Err)
}()
// Create a slice of buffers to hold the test object content.
oContents := make([]*bytes.Buffer, numObjects)
for i := 0; i < numObjects; i++ {
oContents[i] = bytes.NewBuffer([]byte(tools.RandomString("", 10)))
res := objects.Create(client, cName, oNames[i], oContents[i], nil)
th.AssertNoErr(t, res.Err)
}
// Delete the objects after testing.
defer func() {
for i := 0; i < numObjects; i++ {
res := objects.Delete(client, cName, oNames[i], nil)
th.AssertNoErr(t, res.Err)
}
}()
ons := make([]string, 0, len(oNames))
err = objects.List(client, cName, &objects.ListOpts{Full: false, Prefix: "test-object-"}).EachPage(func(page pagination.Page) (bool, error) {
names, err := objects.ExtractNames(page)
th.AssertNoErr(t, err)
ons = append(ons, names...)
return true, nil
})
th.AssertNoErr(t, err)
th.AssertEquals(t, len(ons), len(oNames))
ois := make([]objects.Object, 0, len(oNames))
err = objects.List(client, cName, &objects.ListOpts{Full: true, Prefix: "test-object-"}).EachPage(func(page pagination.Page) (bool, error) {
info, err := objects.ExtractInfo(page)
th.AssertNoErr(t, err)
ois = append(ois, info...)
return true, nil
})
th.AssertNoErr(t, err)
th.AssertEquals(t, len(ois), len(oNames))
// Copy the contents of one object to another.
copyres := objects.Copy(client, cName, oNames[0], &objects.CopyOpts{Destination: cName + "/" + oNames[1]})
th.AssertNoErr(t, copyres.Err)
// Download one of the objects that was created above.
o1Content, err := objects.Download(client, cName, oNames[0], nil).ExtractContent()
th.AssertNoErr(t, err)
// Download the another object that was create above.
o2Content, err := objects.Download(client, cName, oNames[1], nil).ExtractContent()
th.AssertNoErr(t, err)
// Compare the two object's contents to test that the copy worked.
th.AssertEquals(t, string(o2Content), string(o1Content))
// Update an object's metadata.
updateres := objects.Update(client, cName, oNames[0], &objects.UpdateOpts{Metadata: metadata})
th.AssertNoErr(t, updateres.Err)
// Delete the object's metadata after testing.
defer func() {
tempMap := make(map[string]string)
for k := range metadata {
tempMap[k] = ""
}
res := objects.Update(client, cName, oNames[0], &objects.UpdateOpts{Metadata: tempMap})
th.AssertNoErr(t, res.Err)
}()
// Retrieve an object's metadata.
om, err := objects.Get(client, cName, oNames[0], nil).ExtractMetadata()
th.AssertNoErr(t, err)
for k := range metadata {
if om[k] != metadata[strings.Title(k)] {
t.Errorf("Expected custom metadata with key: %s", k)
return
}
}
}

View File

@ -0,0 +1,4 @@
// +build acceptance
package openstack

View File

@ -0,0 +1,38 @@
// +build acceptance
package v1
import (
"os"
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/acceptance/tools"
"github.com/rackspace/gophercloud/rackspace"
th "github.com/rackspace/gophercloud/testhelper"
)
func newClient() (*gophercloud.ServiceClient, error) {
opts, err := rackspace.AuthOptionsFromEnv()
if err != nil {
return nil, err
}
opts = tools.OnlyRS(opts)
region := os.Getenv("RS_REGION")
provider, err := rackspace.AuthenticatedClient(opts)
if err != nil {
return nil, err
}
return rackspace.NewBlockStorageV1(provider, gophercloud.EndpointOpts{
Region: region,
})
}
func setup(t *testing.T) *gophercloud.ServiceClient {
client, err := newClient()
th.AssertNoErr(t, err)
return client
}

View File

@ -0,0 +1,82 @@
// +build acceptance blockstorage snapshots
package v1
import (
"testing"
"time"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/rackspace/blockstorage/v1/snapshots"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestSnapshots(t *testing.T) {
client := setup(t)
volID := testVolumeCreate(t, client)
t.Log("Creating snapshots")
s := testSnapshotCreate(t, client, volID)
id := s.ID
t.Log("Listing snapshots")
testSnapshotList(t, client)
t.Logf("Getting snapshot %s", id)
testSnapshotGet(t, client, id)
t.Logf("Updating snapshot %s", id)
testSnapshotUpdate(t, client, id)
t.Logf("Deleting snapshot %s", id)
testSnapshotDelete(t, client, id)
s.WaitUntilDeleted(client, -1)
t.Logf("Deleting volume %s", volID)
testVolumeDelete(t, client, volID)
}
func testSnapshotCreate(t *testing.T, client *gophercloud.ServiceClient, volID string) *snapshots.Snapshot {
opts := snapshots.CreateOpts{VolumeID: volID, Name: "snapshot-001"}
s, err := snapshots.Create(client, opts).Extract()
th.AssertNoErr(t, err)
t.Logf("Created snapshot %s", s.ID)
t.Logf("Waiting for new snapshot to become available...")
start := time.Now().Second()
s.WaitUntilComplete(client, -1)
t.Logf("Snapshot completed after %ds", time.Now().Second()-start)
return s
}
func testSnapshotList(t *testing.T, client *gophercloud.ServiceClient) {
snapshots.List(client).EachPage(func(page pagination.Page) (bool, error) {
sList, err := snapshots.ExtractSnapshots(page)
th.AssertNoErr(t, err)
for _, s := range sList {
t.Logf("Snapshot: ID [%s] Name [%s] Volume ID [%s] Progress [%s] Created [%s]",
s.ID, s.Name, s.VolumeID, s.Progress, s.CreatedAt)
}
return true, nil
})
}
func testSnapshotGet(t *testing.T, client *gophercloud.ServiceClient, id string) {
_, err := snapshots.Get(client, id).Extract()
th.AssertNoErr(t, err)
}
func testSnapshotUpdate(t *testing.T, client *gophercloud.ServiceClient, id string) {
_, err := snapshots.Update(client, id, snapshots.UpdateOpts{Name: "new_name"}).Extract()
th.AssertNoErr(t, err)
}
func testSnapshotDelete(t *testing.T, client *gophercloud.ServiceClient, id string) {
res := snapshots.Delete(client, id)
th.AssertNoErr(t, res.Err)
t.Logf("Deleted snapshot %s", id)
}

View File

@ -0,0 +1,71 @@
// +build acceptance blockstorage volumes
package v1
import (
"testing"
"github.com/rackspace/gophercloud"
os "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/rackspace/blockstorage/v1/volumes"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestVolumes(t *testing.T) {
client := setup(t)
t.Logf("Listing volumes")
testVolumeList(t, client)
t.Logf("Creating volume")
volumeID := testVolumeCreate(t, client)
t.Logf("Getting volume %s", volumeID)
testVolumeGet(t, client, volumeID)
t.Logf("Updating volume %s", volumeID)
testVolumeUpdate(t, client, volumeID)
t.Logf("Deleting volume %s", volumeID)
testVolumeDelete(t, client, volumeID)
}
func testVolumeList(t *testing.T, client *gophercloud.ServiceClient) {
volumes.List(client).EachPage(func(page pagination.Page) (bool, error) {
vList, err := volumes.ExtractVolumes(page)
th.AssertNoErr(t, err)
for _, v := range vList {
t.Logf("Volume: ID [%s] Name [%s] Type [%s] Created [%s]", v.ID, v.Name,
v.VolumeType, v.CreatedAt)
}
return true, nil
})
}
func testVolumeCreate(t *testing.T, client *gophercloud.ServiceClient) string {
vol, err := volumes.Create(client, os.CreateOpts{Size: 75}).Extract()
th.AssertNoErr(t, err)
t.Logf("Created volume: ID [%s] Size [%s]", vol.ID, vol.Size)
return vol.ID
}
func testVolumeGet(t *testing.T, client *gophercloud.ServiceClient, id string) {
vol, err := volumes.Get(client, id).Extract()
th.AssertNoErr(t, err)
t.Logf("Created volume: ID [%s] Size [%s]", vol.ID, vol.Size)
}
func testVolumeUpdate(t *testing.T, client *gophercloud.ServiceClient, id string) {
vol, err := volumes.Update(client, id, volumes.UpdateOpts{Name: "new_name"}).Extract()
th.AssertNoErr(t, err)
t.Logf("Created volume: ID [%s] Name [%s]", vol.ID, vol.Name)
}
func testVolumeDelete(t *testing.T, client *gophercloud.ServiceClient, id string) {
res := volumes.Delete(client, id)
th.AssertNoErr(t, res.Err)
t.Logf("Deleted volume %s", id)
}

View File

@ -0,0 +1,46 @@
// +build acceptance blockstorage volumetypes
package v1
import (
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/rackspace/blockstorage/v1/volumetypes"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestAll(t *testing.T) {
client := setup(t)
t.Logf("Listing volume types")
id := testList(t, client)
t.Logf("Getting volume type %s", id)
testGet(t, client, id)
}
func testList(t *testing.T, client *gophercloud.ServiceClient) string {
var lastID string
volumetypes.List(client).EachPage(func(page pagination.Page) (bool, error) {
typeList, err := volumetypes.ExtractVolumeTypes(page)
th.AssertNoErr(t, err)
for _, vt := range typeList {
t.Logf("Volume type: ID [%s] Name [%s]", vt.ID, vt.Name)
lastID = vt.ID
}
return true, nil
})
return lastID
}
func testGet(t *testing.T, client *gophercloud.ServiceClient, id string) {
vt, err := volumetypes.Get(client, id).Extract()
th.AssertNoErr(t, err)
t.Logf("Volume: ID [%s] Name [%s]", vt.ID, vt.Name)
}

View File

@ -0,0 +1,28 @@
// +build acceptance
package rackspace
import (
"testing"
"github.com/rackspace/gophercloud/acceptance/tools"
"github.com/rackspace/gophercloud/rackspace"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestAuthenticatedClient(t *testing.T) {
// Obtain credentials from the environment.
ao, err := rackspace.AuthOptionsFromEnv()
th.AssertNoErr(t, err)
client, err := rackspace.AuthenticatedClient(tools.OnlyRS(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)
}

View File

@ -0,0 +1,46 @@
// +build acceptance
package v2
import (
"testing"
osBFV "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
"github.com/rackspace/gophercloud/rackspace/compute/v2/bootfromvolume"
"github.com/rackspace/gophercloud/rackspace/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.")
}
options, err := optionsFromEnv()
th.AssertNoErr(t, err)
name := tools.RandomString("Gophercloud-", 8)
t.Logf("Creating server [%s].", name)
bd := []osBFV.BlockDevice{
osBFV.BlockDevice{
UUID: options.imageID,
SourceType: osBFV.Image,
VolumeSize: 10,
},
}
server, err := bootfromvolume.Create(client, servers.CreateOpts{
Name: name,
FlavorRef: "performance1-1",
BlockDevice: bd,
}).Extract()
th.AssertNoErr(t, err)
t.Logf("Created server: %+v\n", server)
//defer deleteServer(t, client, server)
t.Logf("Deleting server [%s]...", name)
}

View File

@ -0,0 +1,60 @@
// +build acceptance
package v2
import (
"errors"
"os"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/acceptance/tools"
"github.com/rackspace/gophercloud/rackspace"
)
func newClient() (*gophercloud.ServiceClient, error) {
// Obtain credentials from the environment.
options, err := rackspace.AuthOptionsFromEnv()
if err != nil {
return nil, err
}
options = tools.OnlyRS(options)
region := os.Getenv("RS_REGION")
if options.Username == "" {
return nil, errors.New("Please provide a Rackspace username as RS_USERNAME.")
}
if options.APIKey == "" {
return nil, errors.New("Please provide a Rackspace API key as RS_API_KEY.")
}
if region == "" {
return nil, errors.New("Please provide a Rackspace region as RS_REGION.")
}
client, err := rackspace.AuthenticatedClient(options)
if err != nil {
return nil, err
}
return rackspace.NewComputeV2(client, gophercloud.EndpointOpts{
Region: region,
})
}
type serverOpts struct {
imageID string
flavorID string
}
func optionsFromEnv() (*serverOpts, error) {
options := &serverOpts{
imageID: os.Getenv("RS_IMAGE_ID"),
flavorID: os.Getenv("RS_FLAVOR_ID"),
}
if options.imageID == "" {
return nil, errors.New("Please provide a valid Rackspace image ID as RS_IMAGE_ID")
}
if options.flavorID == "" {
return nil, errors.New("Please provide a valid Rackspace flavor ID as RS_FLAVOR_ID")
}
return options, nil
}

View File

@ -0,0 +1,61 @@
// +build acceptance
package v2
import (
"testing"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/rackspace/compute/v2/flavors"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestListFlavors(t *testing.T) {
client, err := newClient()
th.AssertNoErr(t, err)
count := 0
err = flavors.ListDetail(client, nil).EachPage(func(page pagination.Page) (bool, error) {
count++
t.Logf("-- Page %0d --", count)
fs, err := flavors.ExtractFlavors(page)
th.AssertNoErr(t, err)
for i, flavor := range fs {
t.Logf("[%02d] id=[%s]", i, flavor.ID)
t.Logf(" name=[%s]", flavor.Name)
t.Logf(" disk=[%d]", flavor.Disk)
t.Logf(" RAM=[%d]", flavor.RAM)
t.Logf(" rxtx_factor=[%f]", flavor.RxTxFactor)
t.Logf(" swap=[%d]", flavor.Swap)
t.Logf(" VCPUs=[%d]", flavor.VCPUs)
}
return true, nil
})
th.AssertNoErr(t, err)
if count == 0 {
t.Errorf("No flavors listed!")
}
}
func TestGetFlavor(t *testing.T) {
client, err := newClient()
th.AssertNoErr(t, err)
options, err := optionsFromEnv()
th.AssertNoErr(t, err)
flavor, err := flavors.Get(client, options.flavorID).Extract()
th.AssertNoErr(t, err)
t.Logf("Requested flavor:")
t.Logf(" id=[%s]", flavor.ID)
t.Logf(" name=[%s]", flavor.Name)
t.Logf(" disk=[%d]", flavor.Disk)
t.Logf(" RAM=[%d]", flavor.RAM)
t.Logf(" rxtx_factor=[%f]", flavor.RxTxFactor)
t.Logf(" swap=[%d]", flavor.Swap)
t.Logf(" VCPUs=[%d]", flavor.VCPUs)
}

View File

@ -0,0 +1,63 @@
// +build acceptance
package v2
import (
"testing"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/rackspace/compute/v2/images"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestListImages(t *testing.T) {
client, err := newClient()
th.AssertNoErr(t, err)
count := 0
err = images.ListDetail(client, nil).EachPage(func(page pagination.Page) (bool, error) {
count++
t.Logf("-- Page %02d --", count)
is, err := images.ExtractImages(page)
th.AssertNoErr(t, err)
for i, image := range is {
t.Logf("[%02d] id=[%s]", i, image.ID)
t.Logf(" name=[%s]", image.Name)
t.Logf(" created=[%s]", image.Created)
t.Logf(" updated=[%s]", image.Updated)
t.Logf(" min disk=[%d]", image.MinDisk)
t.Logf(" min RAM=[%d]", image.MinRAM)
t.Logf(" progress=[%d]", image.Progress)
t.Logf(" status=[%s]", image.Status)
}
return true, nil
})
th.AssertNoErr(t, err)
if count < 1 {
t.Errorf("Expected at least one page of images.")
}
}
func TestGetImage(t *testing.T) {
client, err := newClient()
th.AssertNoErr(t, err)
options, err := optionsFromEnv()
th.AssertNoErr(t, err)
image, err := images.Get(client, options.imageID).Extract()
th.AssertNoErr(t, err)
t.Logf("Requested image:")
t.Logf(" id=[%s]", image.ID)
t.Logf(" name=[%s]", image.Name)
t.Logf(" created=[%s]", image.Created)
t.Logf(" updated=[%s]", image.Updated)
t.Logf(" min disk=[%d]", image.MinDisk)
t.Logf(" min RAM=[%d]", image.MinRAM)
t.Logf(" progress=[%d]", image.Progress)
t.Logf(" status=[%s]", image.Status)
}

View File

@ -0,0 +1,87 @@
// +build acceptance rackspace
package v2
import (
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/acceptance/tools"
os "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/rackspace/compute/v2/keypairs"
th "github.com/rackspace/gophercloud/testhelper"
)
func deleteKeyPair(t *testing.T, client *gophercloud.ServiceClient, name string) {
err := keypairs.Delete(client, name).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Successfully deleted key [%s].", name)
}
func TestCreateKeyPair(t *testing.T) {
client, err := newClient()
th.AssertNoErr(t, err)
name := tools.RandomString("createdkey-", 8)
k, err := keypairs.Create(client, os.CreateOpts{Name: name}).Extract()
th.AssertNoErr(t, err)
defer deleteKeyPair(t, client, name)
t.Logf("Created a new keypair:")
t.Logf(" name=[%s]", k.Name)
t.Logf(" fingerprint=[%s]", k.Fingerprint)
t.Logf(" publickey=[%s]", tools.Elide(k.PublicKey))
t.Logf(" privatekey=[%s]", tools.Elide(k.PrivateKey))
t.Logf(" userid=[%s]", k.UserID)
}
func TestImportKeyPair(t *testing.T) {
client, err := newClient()
th.AssertNoErr(t, err)
name := tools.RandomString("importedkey-", 8)
pubkey := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDlIQ3r+zd97kb9Hzmujd3V6pbO53eb3Go4q2E8iqVGWQfZTrFdL9KACJnqJIm9HmncfRkUTxE37hqeGCCv8uD+ZPmPiZG2E60OX1mGDjbbzAyReRwYWXgXHopggZTLak5k4mwZYaxwaufbVBDRn847e01lZnaXaszEToLM37NLw+uz29sl3TwYy2R0RGHPwPc160aWmdLjSyd1Nd4c9pvvOP/EoEuBjIC6NJJwg2Rvg9sjjx9jYj0QUgc8CqKLN25oMZ69kNJzlFylKRUoeeVr89txlR59yehJWk6Uw6lYFTdJmcmQOFVAJ12RMmS1hLWCM8UzAgtw+EDa0eqBxBDl smash@winter"
k, err := keypairs.Create(client, os.CreateOpts{
Name: name,
PublicKey: pubkey,
}).Extract()
th.AssertNoErr(t, err)
defer deleteKeyPair(t, client, name)
th.CheckEquals(t, pubkey, k.PublicKey)
th.CheckEquals(t, "", k.PrivateKey)
t.Logf("Imported an existing keypair:")
t.Logf(" name=[%s]", k.Name)
t.Logf(" fingerprint=[%s]", k.Fingerprint)
t.Logf(" publickey=[%s]", tools.Elide(k.PublicKey))
t.Logf(" privatekey=[%s]", tools.Elide(k.PrivateKey))
t.Logf(" userid=[%s]", k.UserID)
}
func TestListKeyPairs(t *testing.T) {
client, err := newClient()
th.AssertNoErr(t, err)
count := 0
err = keypairs.List(client).EachPage(func(page pagination.Page) (bool, error) {
count++
t.Logf("--- %02d ---", count)
ks, err := keypairs.ExtractKeyPairs(page)
th.AssertNoErr(t, err)
for i, keypair := range ks {
t.Logf("[%02d] name=[%s]", i, keypair.Name)
t.Logf(" fingerprint=[%s]", keypair.Fingerprint)
t.Logf(" publickey=[%s]", tools.Elide(keypair.PublicKey))
t.Logf(" privatekey=[%s]", tools.Elide(keypair.PrivateKey))
t.Logf(" userid=[%s]", keypair.UserID)
}
return true, nil
})
th.AssertNoErr(t, err)
}

View File

@ -0,0 +1,53 @@
// +build acceptance rackspace
package v2
import (
"testing"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/rackspace/compute/v2/networks"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestNetworks(t *testing.T) {
client, err := newClient()
th.AssertNoErr(t, err)
// Create a network
n, err := networks.Create(client, networks.CreateOpts{Label: "sample_network", CIDR: "172.20.0.0/24"}).Extract()
th.AssertNoErr(t, err)
t.Logf("Created network: %+v\n", n)
defer networks.Delete(client, n.ID)
th.AssertEquals(t, n.Label, "sample_network")
th.AssertEquals(t, n.CIDR, "172.20.0.0/24")
networkID := n.ID
// List networks
pager := networks.List(client)
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] Label [%s] CIDR [%s]",
n.ID, n.Label, n.CIDR)
}
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()
t.Logf("Retrieved Network: %+v\n", n)
th.AssertNoErr(t, err)
th.AssertEquals(t, n.CIDR, "172.20.0.0/24")
th.AssertEquals(t, n.Label, "sample_network")
th.AssertEquals(t, n.ID, networkID)
}

View File

@ -0,0 +1 @@
package v2

View File

@ -0,0 +1,199 @@
// +build acceptance
package v2
import (
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/acceptance/tools"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/diskconfig"
oskey "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
os "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/rackspace/compute/v2/keypairs"
"github.com/rackspace/gophercloud/rackspace/compute/v2/servers"
th "github.com/rackspace/gophercloud/testhelper"
)
func createServerKeyPair(t *testing.T, client *gophercloud.ServiceClient) *oskey.KeyPair {
name := tools.RandomString("importedkey-", 8)
pubkey := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDlIQ3r+zd97kb9Hzmujd3V6pbO53eb3Go4q2E8iqVGWQfZTrFdL9KACJnqJIm9HmncfRkUTxE37hqeGCCv8uD+ZPmPiZG2E60OX1mGDjbbzAyReRwYWXgXHopggZTLak5k4mwZYaxwaufbVBDRn847e01lZnaXaszEToLM37NLw+uz29sl3TwYy2R0RGHPwPc160aWmdLjSyd1Nd4c9pvvOP/EoEuBjIC6NJJwg2Rvg9sjjx9jYj0QUgc8CqKLN25oMZ69kNJzlFylKRUoeeVr89txlR59yehJWk6Uw6lYFTdJmcmQOFVAJ12RMmS1hLWCM8UzAgtw+EDa0eqBxBDl smash@winter"
k, err := keypairs.Create(client, oskey.CreateOpts{
Name: name,
PublicKey: pubkey,
}).Extract()
th.AssertNoErr(t, err)
return k
}
func createServer(t *testing.T, client *gophercloud.ServiceClient, keyName string) *os.Server {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
options, err := optionsFromEnv()
th.AssertNoErr(t, err)
name := tools.RandomString("Gophercloud-", 8)
opts := &servers.CreateOpts{
Name: name,
ImageRef: options.imageID,
FlavorRef: options.flavorID,
DiskConfig: diskconfig.Manual,
}
if keyName != "" {
opts.KeyPair = keyName
}
t.Logf("Creating server [%s].", name)
s, err := servers.Create(client, opts).Extract()
th.AssertNoErr(t, err)
t.Logf("Creating server.")
err = servers.WaitForStatus(client, s.ID, "ACTIVE", 300)
th.AssertNoErr(t, err)
t.Logf("Server created successfully.")
return s
}
func logServer(t *testing.T, server *os.Server, index int) {
if index == -1 {
t.Logf(" id=[%s]", server.ID)
} else {
t.Logf("[%02d] id=[%s]", index, server.ID)
}
t.Logf(" name=[%s]", server.Name)
t.Logf(" tenant ID=[%s]", server.TenantID)
t.Logf(" user ID=[%s]", server.UserID)
t.Logf(" updated=[%s]", server.Updated)
t.Logf(" created=[%s]", server.Created)
t.Logf(" host ID=[%s]", server.HostID)
t.Logf(" access IPv4=[%s]", server.AccessIPv4)
t.Logf(" access IPv6=[%s]", server.AccessIPv6)
t.Logf(" image=[%v]", server.Image)
t.Logf(" flavor=[%v]", server.Flavor)
t.Logf(" addresses=[%v]", server.Addresses)
t.Logf(" metadata=[%v]", server.Metadata)
t.Logf(" links=[%v]", server.Links)
t.Logf(" keyname=[%s]", server.KeyName)
t.Logf(" admin password=[%s]", server.AdminPass)
t.Logf(" status=[%s]", server.Status)
t.Logf(" progress=[%d]", server.Progress)
}
func getServer(t *testing.T, client *gophercloud.ServiceClient, server *os.Server) {
t.Logf("> servers.Get")
details, err := servers.Get(client, server.ID).Extract()
th.AssertNoErr(t, err)
logServer(t, details, -1)
}
func listServers(t *testing.T, client *gophercloud.ServiceClient) {
t.Logf("> servers.List")
count := 0
err := servers.List(client, nil).EachPage(func(page pagination.Page) (bool, error) {
count++
t.Logf("--- Page %02d ---", count)
s, err := servers.ExtractServers(page)
th.AssertNoErr(t, err)
for index, server := range s {
logServer(t, &server, index)
}
return true, nil
})
th.AssertNoErr(t, err)
}
func changeAdminPassword(t *testing.T, client *gophercloud.ServiceClient, server *os.Server) {
t.Logf("> servers.ChangeAdminPassword")
original := server.AdminPass
t.Logf("Changing server password.")
err := servers.ChangeAdminPassword(client, server.ID, tools.MakeNewPassword(original)).Extract()
th.AssertNoErr(t, err)
err = servers.WaitForStatus(client, server.ID, "ACTIVE", 300)
th.AssertNoErr(t, err)
t.Logf("Password changed successfully.")
}
func rebootServer(t *testing.T, client *gophercloud.ServiceClient, server *os.Server) {
t.Logf("> servers.Reboot")
err := servers.Reboot(client, server.ID, os.HardReboot).Extract()
th.AssertNoErr(t, err)
err = servers.WaitForStatus(client, server.ID, "ACTIVE", 300)
th.AssertNoErr(t, err)
t.Logf("Server successfully rebooted.")
}
func rebuildServer(t *testing.T, client *gophercloud.ServiceClient, server *os.Server) {
t.Logf("> servers.Rebuild")
options, err := optionsFromEnv()
th.AssertNoErr(t, err)
opts := servers.RebuildOpts{
Name: tools.RandomString("RenamedGopher", 16),
AdminPass: tools.MakeNewPassword(server.AdminPass),
ImageID: options.imageID,
DiskConfig: diskconfig.Manual,
}
after, err := servers.Rebuild(client, server.ID, opts).Extract()
th.AssertNoErr(t, err)
th.CheckEquals(t, after.ID, server.ID)
err = servers.WaitForStatus(client, after.ID, "ACTIVE", 300)
th.AssertNoErr(t, err)
t.Logf("Server successfully rebuilt.")
logServer(t, after, -1)
}
func deleteServer(t *testing.T, client *gophercloud.ServiceClient, server *os.Server) {
t.Logf("> servers.Delete")
res := servers.Delete(client, server.ID)
th.AssertNoErr(t, res.Err)
t.Logf("Server deleted successfully.")
}
func deleteServerKeyPair(t *testing.T, client *gophercloud.ServiceClient, k *oskey.KeyPair) {
t.Logf("> keypairs.Delete")
err := keypairs.Delete(client, k.Name).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Keypair deleted successfully.")
}
func TestServerOperations(t *testing.T) {
client, err := newClient()
th.AssertNoErr(t, err)
kp := createServerKeyPair(t, client)
defer deleteServerKeyPair(t, client, kp)
server := createServer(t, client, kp.Name)
defer deleteServer(t, client, server)
getServer(t, client, server)
listServers(t, client)
changeAdminPassword(t, client, server)
rebootServer(t, client, server)
rebuildServer(t, client, server)
}

View File

@ -0,0 +1,53 @@
// +build acceptance rackspace
package v2
import (
"testing"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/rackspace/compute/v2/networks"
"github.com/rackspace/gophercloud/rackspace/compute/v2/virtualinterfaces"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestVirtualInterfaces(t *testing.T) {
client, err := newClient()
th.AssertNoErr(t, err)
// Create a server
server := createServer(t, client, "")
t.Logf("Created Server: %v\n", server)
defer deleteServer(t, client, server)
serverID := server.ID
// Create a network
n, err := networks.Create(client, networks.CreateOpts{Label: "sample_network", CIDR: "172.20.0.0/24"}).Extract()
th.AssertNoErr(t, err)
t.Logf("Created Network: %v\n", n)
defer networks.Delete(client, n.ID)
networkID := n.ID
// Create a virtual interface
vi, err := virtualinterfaces.Create(client, serverID, networkID).Extract()
th.AssertNoErr(t, err)
t.Logf("Created virtual interface: %+v\n", vi)
defer virtualinterfaces.Delete(client, serverID, vi.ID)
// List virtual interfaces
pager := virtualinterfaces.List(client, serverID)
err = pager.EachPage(func(page pagination.Page) (bool, error) {
t.Logf("--- Page ---")
virtualinterfacesList, err := virtualinterfaces.ExtractVirtualInterfaces(page)
th.AssertNoErr(t, err)
for _, vi := range virtualinterfacesList {
t.Logf("Virtual Interface: ID [%s] MAC Address [%s] IP Addresses [%v]",
vi.ID, vi.MACAddress, vi.IPAddresses)
}
return true, nil
})
th.CheckNoErr(t, err)
}

View File

@ -0,0 +1,54 @@
// +build acceptance
package v2
import (
"testing"
"github.com/rackspace/gophercloud/pagination"
extensions2 "github.com/rackspace/gophercloud/rackspace/identity/v2/extensions"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestExtensions(t *testing.T) {
service := authenticatedClient(t)
t.Logf("Extensions available on this identity endpoint:")
count := 0
var chosen string
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 {
if chosen == "" {
chosen = ext.Alias
}
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)
if chosen == "" {
t.Logf("No extensions found.")
return
}
ext, err := extensions2.Get(service, chosen).Extract()
th.AssertNoErr(t, err)
t.Logf("Detail for extension [%s]:", chosen)
t.Logf(" name=[%s]", ext.Name)
t.Logf(" namespace=[%s]", ext.Namespace)
t.Logf(" alias=[%s]", ext.Alias)
t.Logf(" updated=[%s]", ext.Updated)
t.Logf(" description=[%s]", ext.Description)
}

View File

@ -0,0 +1,50 @@
// +build acceptance
package v2
import (
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/acceptance/tools"
"github.com/rackspace/gophercloud/rackspace"
th "github.com/rackspace/gophercloud/testhelper"
)
func rackspaceAuthOptions(t *testing.T) gophercloud.AuthOptions {
// Obtain credentials from the environment.
options, err := rackspace.AuthOptionsFromEnv()
th.AssertNoErr(t, err)
options = tools.OnlyRS(options)
if options.Username == "" {
t.Fatal("Please provide a Rackspace username as RS_USERNAME.")
}
if options.APIKey == "" {
t.Fatal("Please provide a Rackspace API key as RS_API_KEY.")
}
return options
}
func createClient(t *testing.T, auth bool) *gophercloud.ServiceClient {
ao := rackspaceAuthOptions(t)
provider, err := rackspace.NewClient(ao.IdentityEndpoint)
th.AssertNoErr(t, err)
if auth {
err = rackspace.Authenticate(provider, ao)
th.AssertNoErr(t, err)
}
return rackspace.NewIdentityV2(provider)
}
func unauthenticatedClient(t *testing.T) *gophercloud.ServiceClient {
return createClient(t, false)
}
func authenticatedClient(t *testing.T) *gophercloud.ServiceClient {
return createClient(t, true)
}

View File

@ -0,0 +1,37 @@
// +build acceptance
package v2
import (
"testing"
"github.com/rackspace/gophercloud/pagination"
rstenants "github.com/rackspace/gophercloud/rackspace/identity/v2/tenants"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestTenants(t *testing.T) {
service := authenticatedClient(t)
t.Logf("Tenants available to the currently issued token:")
count := 0
err := rstenants.List(service, nil).EachPage(func(page pagination.Page) (bool, error) {
t.Logf("--- Page %02d ---", count)
tenants, err := rstenants.ExtractTenants(page)
th.AssertNoErr(t, err)
for i, tenant := range tenants {
t.Logf("[%02d] id=[%s]", i, tenant.ID)
t.Logf(" name=[%s] enabled=[%v]", i, tenant.Name, tenant.Enabled)
t.Logf(" description=[%s]", tenant.Description)
}
count++
return true, nil
})
th.AssertNoErr(t, err)
if count == 0 {
t.Errorf("No tenants listed for your current token.")
}
}

View File

@ -0,0 +1,33 @@
// +build acceptance rackspace
package v1
import (
"testing"
raxAccounts "github.com/rackspace/gophercloud/rackspace/objectstorage/v1/accounts"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestAccounts(t *testing.T) {
c, err := createClient(t, false)
th.AssertNoErr(t, err)
updateres := raxAccounts.Update(c, raxAccounts.UpdateOpts{Metadata: map[string]string{"white": "mountains"}})
th.AssertNoErr(t, updateres.Err)
t.Logf("Headers from Update Account request: %+v\n", updateres.Header)
defer func() {
updateres = raxAccounts.Update(c, raxAccounts.UpdateOpts{Metadata: map[string]string{"white": ""}})
th.AssertNoErr(t, updateres.Err)
metadata, err := raxAccounts.Get(c).ExtractMetadata()
th.AssertNoErr(t, err)
t.Logf("Metadata from Get Account request (after update reverted): %+v\n", metadata)
th.CheckEquals(t, metadata["White"], "")
}()
metadata, err := raxAccounts.Get(c).ExtractMetadata()
th.AssertNoErr(t, err)
t.Logf("Metadata from Get Account request (after update): %+v\n", metadata)
th.CheckEquals(t, metadata["White"], "mountains")
}

View File

@ -0,0 +1,23 @@
// +build acceptance rackspace objectstorage v1
package v1
import (
"testing"
"github.com/rackspace/gophercloud/rackspace/objectstorage/v1/bulk"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestBulk(t *testing.T) {
c, err := createClient(t, false)
th.AssertNoErr(t, err)
var options bulk.DeleteOpts
options = append(options, "container/object1")
res := bulk.Delete(c, options)
th.AssertNoErr(t, res.Err)
body, err := res.ExtractBody()
th.AssertNoErr(t, err)
t.Logf("Response body from Bulk Delete Request: %+v\n", body)
}

View File

@ -0,0 +1,61 @@
// +build acceptance rackspace objectstorage v1
package v1
import (
"testing"
osContainers "github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers"
"github.com/rackspace/gophercloud/pagination"
raxCDNContainers "github.com/rackspace/gophercloud/rackspace/objectstorage/v1/cdncontainers"
raxContainers "github.com/rackspace/gophercloud/rackspace/objectstorage/v1/containers"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestCDNContainers(t *testing.T) {
raxClient, err := createClient(t, false)
th.AssertNoErr(t, err)
createres := raxContainers.Create(raxClient, "gophercloud-test", nil)
th.AssertNoErr(t, createres.Err)
t.Logf("Headers from Create Container request: %+v\n", createres.Header)
defer func() {
res := raxContainers.Delete(raxClient, "gophercloud-test")
th.AssertNoErr(t, res.Err)
}()
raxCDNClient, err := createClient(t, true)
th.AssertNoErr(t, err)
r := raxCDNContainers.Enable(raxCDNClient, "gophercloud-test", raxCDNContainers.EnableOpts{CDNEnabled: true, TTL: 900})
th.AssertNoErr(t, r.Err)
t.Logf("Headers from Enable CDN Container request: %+v\n", r.Header)
t.Logf("Container Names available to the currently issued token:")
count := 0
err = raxCDNContainers.List(raxCDNClient, &osContainers.ListOpts{Full: false}).EachPage(func(page pagination.Page) (bool, error) {
t.Logf("--- Page %02d ---", count)
names, err := raxCDNContainers.ExtractNames(page)
th.AssertNoErr(t, err)
for i, name := range names {
t.Logf("[%02d] %s", i, name)
}
count++
return true, nil
})
th.AssertNoErr(t, err)
if count == 0 {
t.Errorf("No CDN containers listed for your current token.")
}
updateres := raxCDNContainers.Update(raxCDNClient, "gophercloud-test", raxCDNContainers.UpdateOpts{CDNEnabled: false})
th.AssertNoErr(t, updateres.Err)
t.Logf("Headers from Update CDN Container request: %+v\n", updateres.Header)
metadata, err := raxCDNContainers.Get(raxCDNClient, "gophercloud-test").ExtractMetadata()
th.AssertNoErr(t, err)
t.Logf("Headers from Get CDN Container request (after update): %+v\n", metadata)
}

View File

@ -0,0 +1,46 @@
// +build acceptance rackspace objectstorage v1
package v1
import (
"bytes"
"testing"
raxCDNContainers "github.com/rackspace/gophercloud/rackspace/objectstorage/v1/cdncontainers"
raxCDNObjects "github.com/rackspace/gophercloud/rackspace/objectstorage/v1/cdnobjects"
raxContainers "github.com/rackspace/gophercloud/rackspace/objectstorage/v1/containers"
raxObjects "github.com/rackspace/gophercloud/rackspace/objectstorage/v1/objects"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestCDNObjects(t *testing.T) {
raxClient, err := createClient(t, false)
th.AssertNoErr(t, err)
createContResult := raxContainers.Create(raxClient, "gophercloud-test", nil)
th.AssertNoErr(t, createContResult.Err)
t.Logf("Headers from Create Container request: %+v\n", createContResult.Header)
defer func() {
deleteResult := raxContainers.Delete(raxClient, "gophercloud-test")
th.AssertNoErr(t, deleteResult.Err)
}()
header, err := raxObjects.Create(raxClient, "gophercloud-test", "test-object", bytes.NewBufferString("gophercloud cdn test"), nil).ExtractHeader()
th.AssertNoErr(t, err)
t.Logf("Headers from Create Object request: %+v\n", header)
defer func() {
deleteResult := raxObjects.Delete(raxClient, "gophercloud-test", "test-object", nil)
th.AssertNoErr(t, deleteResult.Err)
}()
raxCDNClient, err := createClient(t, true)
th.AssertNoErr(t, err)
enableResult := raxCDNContainers.Enable(raxCDNClient, "gophercloud-test", raxCDNContainers.EnableOpts{CDNEnabled: true, TTL: 900})
th.AssertNoErr(t, enableResult.Err)
t.Logf("Headers from Enable CDN Container request: %+v\n", enableResult.Header)
deleteResult := raxCDNObjects.Delete(raxCDNClient, "gophercloud-test", "test-object", nil)
th.AssertNoErr(t, deleteResult.Err)
t.Logf("Headers from Delete CDN Object request: %+v\n", deleteResult.Err)
}

View File

@ -0,0 +1,54 @@
// +build acceptance rackspace objectstorage v1
package v1
import (
"os"
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/acceptance/tools"
"github.com/rackspace/gophercloud/rackspace"
th "github.com/rackspace/gophercloud/testhelper"
)
func rackspaceAuthOptions(t *testing.T) gophercloud.AuthOptions {
// Obtain credentials from the environment.
options, err := rackspace.AuthOptionsFromEnv()
th.AssertNoErr(t, err)
options = tools.OnlyRS(options)
if options.Username == "" {
t.Fatal("Please provide a Rackspace username as RS_USERNAME.")
}
if options.APIKey == "" {
t.Fatal("Please provide a Rackspace API key as RS_API_KEY.")
}
return options
}
func createClient(t *testing.T, cdn bool) (*gophercloud.ServiceClient, error) {
region := os.Getenv("RS_REGION")
if region == "" {
t.Fatal("Please provide a Rackspace region as RS_REGION")
}
ao := rackspaceAuthOptions(t)
provider, err := rackspace.NewClient(ao.IdentityEndpoint)
th.AssertNoErr(t, err)
err = rackspace.Authenticate(provider, ao)
th.AssertNoErr(t, err)
if cdn {
return rackspace.NewObjectCDNV1(provider, gophercloud.EndpointOpts{
Region: region,
})
}
return rackspace.NewObjectStorageV1(provider, gophercloud.EndpointOpts{
Region: region,
})
}

View File

@ -0,0 +1,85 @@
// +build acceptance rackspace objectstorage v1
package v1
import (
"testing"
osContainers "github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers"
"github.com/rackspace/gophercloud/pagination"
raxContainers "github.com/rackspace/gophercloud/rackspace/objectstorage/v1/containers"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestContainers(t *testing.T) {
c, err := createClient(t, false)
th.AssertNoErr(t, err)
t.Logf("Containers Info available to the currently issued token:")
count := 0
err = raxContainers.List(c, &osContainers.ListOpts{Full: true}).EachPage(func(page pagination.Page) (bool, error) {
t.Logf("--- Page %02d ---", count)
containers, err := raxContainers.ExtractInfo(page)
th.AssertNoErr(t, err)
for i, container := range containers {
t.Logf("[%02d] name=[%s]", i, container.Name)
t.Logf(" count=[%d]", container.Count)
t.Logf(" bytes=[%d]", container.Bytes)
}
count++
return true, nil
})
th.AssertNoErr(t, err)
if count == 0 {
t.Errorf("No containers listed for your current token.")
}
t.Logf("Container Names available to the currently issued token:")
count = 0
err = raxContainers.List(c, &osContainers.ListOpts{Full: false}).EachPage(func(page pagination.Page) (bool, error) {
t.Logf("--- Page %02d ---", count)
names, err := raxContainers.ExtractNames(page)
th.AssertNoErr(t, err)
for i, name := range names {
t.Logf("[%02d] %s", i, name)
}
count++
return true, nil
})
th.AssertNoErr(t, err)
if count == 0 {
t.Errorf("No containers listed for your current token.")
}
createres := raxContainers.Create(c, "gophercloud-test", nil)
th.AssertNoErr(t, createres.Err)
defer func() {
res := raxContainers.Delete(c, "gophercloud-test")
th.AssertNoErr(t, res.Err)
}()
updateres := raxContainers.Update(c, "gophercloud-test", raxContainers.UpdateOpts{Metadata: map[string]string{"white": "mountains"}})
th.AssertNoErr(t, updateres.Err)
t.Logf("Headers from Update Account request: %+v\n", updateres.Header)
defer func() {
res := raxContainers.Update(c, "gophercloud-test", raxContainers.UpdateOpts{Metadata: map[string]string{"white": ""}})
th.AssertNoErr(t, res.Err)
metadata, err := raxContainers.Get(c, "gophercloud-test").ExtractMetadata()
th.AssertNoErr(t, err)
t.Logf("Metadata from Get Account request (after update reverted): %+v\n", metadata)
th.CheckEquals(t, metadata["White"], "")
}()
getres := raxContainers.Get(c, "gophercloud-test")
t.Logf("Headers from Get Account request (after update): %+v\n", getres.Header)
metadata, err := getres.ExtractMetadata()
th.AssertNoErr(t, err)
t.Logf("Metadata from Get Account request (after update): %+v\n", metadata)
th.CheckEquals(t, metadata["White"], "mountains")
}

View File

@ -0,0 +1,112 @@
// +build acceptance rackspace objectstorage v1
package v1
import (
"bytes"
"testing"
osObjects "github.com/rackspace/gophercloud/openstack/objectstorage/v1/objects"
"github.com/rackspace/gophercloud/pagination"
raxContainers "github.com/rackspace/gophercloud/rackspace/objectstorage/v1/containers"
raxObjects "github.com/rackspace/gophercloud/rackspace/objectstorage/v1/objects"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestObjects(t *testing.T) {
c, err := createClient(t, false)
th.AssertNoErr(t, err)
res := raxContainers.Create(c, "gophercloud-test", nil)
th.AssertNoErr(t, res.Err)
defer func() {
res := raxContainers.Delete(c, "gophercloud-test")
th.AssertNoErr(t, res.Err)
}()
content := bytes.NewBufferString("Lewis Carroll")
options := &osObjects.CreateOpts{ContentType: "text/plain"}
createres := raxObjects.Create(c, "gophercloud-test", "o1", content, options)
th.AssertNoErr(t, createres.Err)
defer func() {
res := raxObjects.Delete(c, "gophercloud-test", "o1", nil)
th.AssertNoErr(t, res.Err)
}()
t.Logf("Objects Info available to the currently issued token:")
count := 0
err = raxObjects.List(c, "gophercloud-test", &osObjects.ListOpts{Full: true}).EachPage(func(page pagination.Page) (bool, error) {
t.Logf("--- Page %02d ---", count)
objects, err := raxObjects.ExtractInfo(page)
th.AssertNoErr(t, err)
for i, object := range objects {
t.Logf("[%02d] name=[%s]", i, object.Name)
t.Logf(" content-type=[%s]", object.ContentType)
t.Logf(" bytes=[%d]", object.Bytes)
t.Logf(" last-modified=[%s]", object.LastModified)
t.Logf(" hash=[%s]", object.Hash)
}
count++
return true, nil
})
th.AssertNoErr(t, err)
if count == 0 {
t.Errorf("No objects listed for your current token.")
}
t.Logf("Container Names available to the currently issued token:")
count = 0
err = raxObjects.List(c, "gophercloud-test", &osObjects.ListOpts{Full: false}).EachPage(func(page pagination.Page) (bool, error) {
t.Logf("--- Page %02d ---", count)
names, err := raxObjects.ExtractNames(page)
th.AssertNoErr(t, err)
for i, name := range names {
t.Logf("[%02d] %s", i, name)
}
count++
return true, nil
})
th.AssertNoErr(t, err)
if count == 0 {
t.Errorf("No objects listed for your current token.")
}
copyres := raxObjects.Copy(c, "gophercloud-test", "o1", &raxObjects.CopyOpts{Destination: "gophercloud-test/o2"})
th.AssertNoErr(t, copyres.Err)
defer func() {
res := raxObjects.Delete(c, "gophercloud-test", "o2", nil)
th.AssertNoErr(t, res.Err)
}()
o1Content, err := raxObjects.Download(c, "gophercloud-test", "o1", nil).ExtractContent()
th.AssertNoErr(t, err)
o2Content, err := raxObjects.Download(c, "gophercloud-test", "o2", nil).ExtractContent()
th.AssertNoErr(t, err)
th.AssertEquals(t, string(o2Content), string(o1Content))
updateres := raxObjects.Update(c, "gophercloud-test", "o2", osObjects.UpdateOpts{Metadata: map[string]string{"white": "mountains"}})
th.AssertNoErr(t, updateres.Err)
t.Logf("Headers from Update Account request: %+v\n", updateres.Header)
defer func() {
res := raxObjects.Update(c, "gophercloud-test", "o2", osObjects.UpdateOpts{Metadata: map[string]string{"white": ""}})
th.AssertNoErr(t, res.Err)
metadata, err := raxObjects.Get(c, "gophercloud-test", "o2", nil).ExtractMetadata()
th.AssertNoErr(t, err)
t.Logf("Metadata from Get Account request (after update reverted): %+v\n", metadata)
th.CheckEquals(t, metadata["White"], "")
}()
getres := raxObjects.Get(c, "gophercloud-test", "o2", nil)
th.AssertNoErr(t, getres.Err)
t.Logf("Headers from Get Account request (after update): %+v\n", getres.Header)
metadata, err := getres.ExtractMetadata()
th.AssertNoErr(t, err)
t.Logf("Metadata from Get Account request (after update): %+v\n", metadata)
th.CheckEquals(t, metadata["White"], "mountains")
}

View File

@ -0,0 +1 @@
package rackspace

View File

@ -0,0 +1 @@
package tools

Some files were not shown because too many files have changed in this diff Show More