1
0
mirror of https://github.com/kubernetes-sigs/descheduler.git synced 2026-01-27 05:46:13 +01:00

Running glide update -v to pulldown all the required depedencies

This commit is contained in:
Hugues Alary
2018-05-22 19:14:00 -07:00
parent 450a5c290b
commit fad9e8dc39
1481 changed files with 199396 additions and 6 deletions

View File

@@ -0,0 +1,235 @@
# Contributing to Gophercloud
- [Getting started](#getting-started)
- [Tests](#tests)
- [Style guide](#basic-style-guide)
- [3 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](/README.md#how-to-install) but add `-tags "fixtures acceptance"` to
get dependencies for unit and acceptance tests.
```bash
go get -tags "fixtures acceptance" github.com/gophercloud/gophercloud
```
2. Move into the directory that houses your local repository:
```bash
cd ${GOPATH}/src/github.com/gophercloud/gophercloud
```
3. Fork the `gophercloud/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.git
```
4. Checkout the latest development branch:
```bash
git checkout master
```
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 behavior
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/gophercloud/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/gophercloud/gophercloud/testhelper"
fake "github.com/gophercloud/gophercloud/testhelper/client"
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
)
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 := networks.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 behavior. 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 -tags fixtures ./...
```
To run all tests with verbose output:
```bash
go test -v -tags fixtures ./...
```
To run tests that match certain [build tags]():
```bash
go test -tags "fixtures foo bar" ./...
```
To run tests for a particular sub-package:
```bash
cd ./path/to/package && go test -tags fixtures .
```
## Style guide
See [here](/STYLEGUIDE.md)
## 3 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 `gophercloud/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. 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/gophercloud/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.
### 2. Improving documentation
The best source of documentation is on [godoc.org](http://godoc.org). It is
automatically generated from the source code.
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!
### 3. 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

@@ -0,0 +1 @@
Before starting a PR, please read the [style guide](https://github.com/gophercloud/gophercloud/blob/master/STYLEGUIDE.md).

View File

@@ -0,0 +1,9 @@
Prior to a PR being reviewed, there needs to be a Github issue that the PR
addresses. Replace the brackets and text below with that issue number.
For #[PUT ISSUE NUMBER HERE]
Links to the line numbers/files in the OpenStack source code that support the
code in this PR:
[PUT URLS HERE]

2
vendor/github.com/gophercloud/gophercloud/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,2 @@
**/*.swp
.idea

21
vendor/github.com/gophercloud/gophercloud/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,21 @@
language: go
sudo: false
install:
- go get golang.org/x/crypto/ssh
- go get -v -tags 'fixtures acceptance' ./...
- go get github.com/wadey/gocovmerge
- go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/goimports
go:
- 1.8
- tip
env:
global:
- secure: "xSQsAG5wlL9emjbCdxzz/hYQsSpJ/bABO1kkbwMSISVcJ3Nk0u4ywF+LS4bgeOnwPfmFvNTOqVDu3RwEvMeWXSI76t1piCPcObutb2faKLVD/hLoAS76gYX+Z8yGWGHrSB7Do5vTPj1ERe2UljdrnsSeOXzoDwFxYRaZLX4bBOB4AyoGvRniil5QXPATiA1tsWX1VMicj8a4F8X+xeESzjt1Q5Iy31e7vkptu71bhvXCaoo5QhYwT+pLR9dN0S1b7Ro0KVvkRefmr1lUOSYd2e74h6Lc34tC1h3uYZCS4h47t7v5cOXvMNxinEj2C51RvbjvZI1RLVdkuAEJD1Iz4+Ote46nXbZ//6XRZMZz/YxQ13l7ux1PFjgEB6HAapmF5Xd8PRsgeTU9LRJxpiTJ3P5QJ3leS1va8qnziM5kYipj/Rn+V8g2ad/rgkRox9LSiR9VYZD2Pe45YCb1mTKSl2aIJnV7nkOqsShY5LNB4JZSg7xIffA+9YVDktw8dJlATjZqt7WvJJ49g6A61mIUV4C15q2JPGKTkZzDiG81NtmS7hFa7k0yaE2ELgYocbcuyUcAahhxntYTC0i23nJmEHVNiZmBO3u7EgpWe4KGVfumU+lt12tIn5b3dZRBBUk3QakKKozSK1QPHGpk/AZGrhu7H6l8to6IICKWtDcyMPQ="
before_script:
- go vet ./...
script:
- ./script/coverage
- ./script/format
after_success:
- $HOME/gopath/bin/goveralls -service=travis-ci -coverprofile=cover.out

12
vendor/github.com/gophercloud/gophercloud/.zuul.yaml generated vendored Normal file
View File

@@ -0,0 +1,12 @@
- project:
name: gophercloud/gophercloud
check:
jobs:
- gophercloud-unittest
- gophercloud-acceptance-test
recheck-mitaka:
jobs:
- gophercloud-acceptance-test-mitaka
recheck-pike:
jobs:
- gophercloud-acceptance-test-pike

View File

148
vendor/github.com/gophercloud/gophercloud/FAQ.md generated vendored Normal file
View File

@@ -0,0 +1,148 @@
# Tips
## Implementing default logging and re-authentication attempts
You can implement custom logging and/or limit re-auth attempts by creating a custom HTTP client
like the following and setting it as the provider client's HTTP Client (via the
`gophercloud.ProviderClient.HTTPClient` field):
```go
//...
// LogRoundTripper satisfies the http.RoundTripper interface and is used to
// customize the default Gophercloud RoundTripper to allow for logging.
type LogRoundTripper struct {
rt http.RoundTripper
numReauthAttempts int
}
// newHTTPClient return a custom HTTP client that allows for logging relevant
// information before and after the HTTP request.
func newHTTPClient() http.Client {
return http.Client{
Transport: &LogRoundTripper{
rt: http.DefaultTransport,
},
}
}
// RoundTrip performs a round-trip HTTP request and logs relevant information about it.
func (lrt *LogRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {
glog.Infof("Request URL: %s\n", request.URL)
response, err := lrt.rt.RoundTrip(request)
if response == nil {
return nil, err
}
if response.StatusCode == http.StatusUnauthorized {
if lrt.numReauthAttempts == 3 {
return response, fmt.Errorf("Tried to re-authenticate 3 times with no success.")
}
lrt.numReauthAttempts++
}
glog.Debugf("Response Status: %s\n", response.Status)
return response, nil
}
endpoint := "https://127.0.0.1/auth"
pc := openstack.NewClient(endpoint)
pc.HTTPClient = newHTTPClient()
//...
```
## Implementing custom objects
OpenStack request/response objects may differ among variable names or types.
### Custom request objects
To pass custom options to a request, implement the desired `<ACTION>OptsBuilder` interface. For
example, to pass in
```go
type MyCreateServerOpts struct {
Name string
Size int
}
```
to `servers.Create`, simply implement the `servers.CreateOptsBuilder` interface:
```go
func (o MyCreateServeropts) ToServerCreateMap() (map[string]interface{}, error) {
return map[string]interface{}{
"name": o.Name,
"size": o.Size,
}, nil
}
```
create an instance of your custom options object, and pass it to `servers.Create`:
```go
// ...
myOpts := MyCreateServerOpts{
Name: "s1",
Size: "100",
}
server, err := servers.Create(computeClient, myOpts).Extract()
// ...
```
### Custom response objects
Some OpenStack services have extensions. Extensions that are supported in Gophercloud can be
combined to create a custom object:
```go
// ...
type MyVolume struct {
volumes.Volume
tenantattr.VolumeExt
}
var v struct {
MyVolume `json:"volume"`
}
err := volumes.Get(client, volID).ExtractInto(&v)
// ...
```
## Overriding default `UnmarshalJSON` method
For some response objects, a field may be a custom type or may be allowed to take on
different types. In these cases, overriding the default `UnmarshalJSON` method may be
necessary. To do this, declare the JSON `struct` field tag as "-" and create an `UnmarshalJSON`
method on the type:
```go
// ...
type MyVolume struct {
ID string `json: "id"`
TimeCreated time.Time `json: "-"`
}
func (r *MyVolume) UnmarshalJSON(b []byte) error {
type tmp MyVolume
var s struct {
tmp
TimeCreated gophercloud.JSONRFC3339MilliNoZ `json:"created_at"`
}
err := json.Unmarshal(b, &s)
if err != nil {
return err
}
*r = Volume(s.tmp)
r.TimeCreated = time.Time(s.CreatedAt)
return err
}
// ...
```

191
vendor/github.com/gophercloud/gophercloud/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,191 @@
Copyright 2012-2013 Rackspace, Inc.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
------
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

32
vendor/github.com/gophercloud/gophercloud/MIGRATING.md generated vendored Normal file
View File

@@ -0,0 +1,32 @@
# Compute
## Floating IPs
* `github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingip` is now `github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips`
* `floatingips.Associate` and `floatingips.Disassociate` have been removed.
* `floatingips.DisassociateOpts` is now required to disassociate a Floating IP.
## Security Groups
* `secgroups.AddServerToGroup` is now `secgroups.AddServer`.
* `secgroups.RemoveServerFromGroup` is now `secgroups.RemoveServer`.
## Servers
* `servers.Reboot` now requires a `servers.RebootOpts` struct:
```golang
rebootOpts := &servers.RebootOpts{
Type: servers.SoftReboot,
}
res := servers.Reboot(client, server.ID, rebootOpts)
```
# Identity
## V3
### Tokens
* `Token.ExpiresAt` is now of type `gophercloud.JSONRFC3339Milli` instead of
`time.Time`

143
vendor/github.com/gophercloud/gophercloud/README.md generated vendored Normal file
View File

@@ -0,0 +1,143 @@
# Gophercloud: an OpenStack SDK for Go
[![Build Status](https://travis-ci.org/gophercloud/gophercloud.svg?branch=master)](https://travis-ci.org/gophercloud/gophercloud)
[![Coverage Status](https://coveralls.io/repos/github/gophercloud/gophercloud/badge.svg?branch=master)](https://coveralls.io/github/gophercloud/gophercloud?branch=master)
Gophercloud is an OpenStack Go SDK.
## Useful links
* [Reference documentation](http://godoc.org/github.com/gophercloud/gophercloud)
* [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://github.com/golang/go/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/gophercloud/gophercloud
# Edit your code to import relevant packages from "github.com/gophercloud/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
* 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/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/openstack"
"github.com/gophercloud/gophercloud/openstack/utils"
)
// Option 1: Pass in the values yourself
opts := gophercloud.AuthOptions{
IdentityEndpoint: "https://openstack.example.com:5000/v2.0",
Username: "{username}",
Password: "{password}",
}
// 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/gophercloud/gophercloud/openstack/compute/v2/servers"
server, err := servers.Create(client, servers.CreateOpts{
Name: "My new server!",
FlavorRef: "flavor_id",
ImageRef: "image_id",
}).Extract()
```
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/gophercloud/gophercloud) struct).
## Advanced Usage
Have a look at the [FAQ](./FAQ.md) for some tips on customizing the way Gophercloud works.
## Backwards-Compatibility Guarantees
None. Vendor it and write tests covering the parts you use.
## Contributing
See the [contributing guide](./.github/CONTRIBUTING.md).
## 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).

View File

@@ -0,0 +1,74 @@
## On Pull Requests
- Before you start a PR there needs to be a Github issue and a discussion about it
on that issue with a core contributor, even if it's just a 'SGTM'.
- A PR's description must reference the issue it closes with a `For <ISSUE NUMBER>` (e.g. For #293).
- A PR's description must contain link(s) to the line(s) in the OpenStack
source code (on Github) that prove(s) the PR code to be valid. Links to documentation
are not good enough. The link(s) should be to a non-`master` branch. For example,
a pull request implementing the creation of a Neutron v2 subnet might put the
following link in the description:
https://github.com/openstack/neutron/blob/stable/mitaka/neutron/api/v2/attributes.py#L749
From that link, a reviewer (or user) can verify the fields in the request/response
objects in the PR.
- A PR that is in-progress should have `[wip]` in front of the PR's title. When
ready for review, remove the `[wip]` and ping a core contributor with an `@`.
- Forcing PRs to be small can have the effect of users submitting PRs in a hierarchical chain, with
one depending on the next. If a PR depends on another one, it should have a [Pending #PRNUM]
prefix in the PR title. In addition, it will be the PR submitter's responsibility to remove the
[Pending #PRNUM] tag once the PR has been updated with the merged, dependent PR. That will
let reviewers know it is ready to review.
- A PR should be small. Even if you intend on implementing an entire
service, a PR should only be one route of that service
(e.g. create server or get server, but not both).
- Unless explicitly asked, do not squash commits in the middle of a review; only
append. It makes it difficult for the reviewer to see what's changed from one
review to the next.
## On Code
- In re design: follow as closely as is reasonable the code already in the library.
Most operations (e.g. create, delete) admit the same design.
- Unit tests and acceptance (integration) tests must be written to cover each PR.
Tests for operations with several options (e.g. list, create) should include all
the options in the tests. This will allow users to verify an operation on their
own infrastructure and see an example of usage.
- If in doubt, ask in-line on the PR.
### File Structure
- The following should be used in most cases:
- `requests.go`: contains all the functions that make HTTP requests and the
types associated with the HTTP request (parameters for URL, body, etc)
- `results.go`: contains all the response objects and their methods
- `urls.go`: contains the endpoints to which the requests are made
### Naming
- For methods on a type in `results.go`, the receiver should be named `r` and the
variable into which it will be unmarshalled `s`.
- Functions in `requests.go`, with the exception of functions that return a
`pagination.Pager`, should be named returns of the name `r`.
- Functions in `requests.go` that accept request bodies should accept as their
last parameter an `interface` named `<Action>OptsBuilder` (eg `CreateOptsBuilder`).
This `interface` should have at the least a method named `To<Resource><Action>Map`
(eg `ToPortCreateMap`).
- Functions in `requests.go` that accept query strings should accept as their
last parameter an `interface` named `<Action>OptsBuilder` (eg `ListOptsBuilder`).
This `interface` should have at the least a method named `To<Resource><Action>Query`
(eg `ToServerListQuery`).

View File

@@ -0,0 +1,113 @@
# 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. Creating a Testing Environment
Running tests on an existing OpenStack cloud can be risky. Malformed tests,
especially ones which require Admin privileges, can cause damage to the
environment. Additionally, you may incur bandwidth and service charges for
the resources used, as mentioned in the note above.
Therefore, it is usually best to first practice running acceptance tests in
an isolated test environment. Two options to easily create a testing
environment are [DevStack](https://docs.openstack.org/devstack/latest/)
and [PackStack](https://www.rdoproject.org/install/packstack/).
The following blog posts detail how to create reusable PackStack environments.
These posts were written with Gophercloud in mind:
* http://terrarum.net/blog/building-openstack-environments.html
* http://terrarum.net/blog/building-openstack-environments-2.html
* http://terrarum.net/blog/building-openstack-environments-3.html
### Step 2. 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|
#### General
|Name|Description|
|---|---|
|`OS_REGION_NAME`|The region you want your resources 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|
|`OS_POOL_NAME`|The Pool from where to obtain Floating IPs|
|`OS_NETWORK_NAME`|The internal/private network to launch instances on|
|`OS_EXTGW_ID`|The external/public network|
#### Database
|Name|Description|
|---|---|
|`OS_DB_DATASTORE_TYPE`|The Datastore type to use. Example: `mariadb`|
|`OS_DB_DATASTORE_VERSION`|The Datastore version to use. Example: `mariadb-10`|
#### Shared file systems
|Name|Description|
|---|---|
|`OS_SHARE_NETWORK_ID`| The share network ID to use when creating shares|
### 3. Run the test suite
From the root directory, run:
```
./script/acceptancetest
```
Alternatively, add the following to your `.bashrc`:
```bash
gophercloudtest() {
if [[ -n $1 ]] && [[ -n $2 ]]; then
pushd $GOPATH/src/github.com/gophercloud/gophercloud
go test -v -tags "fixtures acceptance" -run "$1" github.com/gophercloud/gophercloud/acceptance/openstack/$2 | tee ~/gophercloud.log
popd
fi
}
```
Then run either groups or individual tests by doing:
```shell
$ gophercloudtest TestFlavorsList compute/v2
$ gophercloudtest TestFlavors compute/v2
$ gophercloudtest Test compute/v2
```
### 4. Notes
#### Compute Tests
* In order to run the `TestBootFromVolumeMultiEphemeral` test, a flavor with ephemeral disk space must be used.
* The `TestDefSecRules` tests require a compatible network driver and admin privileges.

View File

@@ -0,0 +1,426 @@
// Package clients contains functions for creating OpenStack service clients
// for use in acceptance tests. It also manages the required environment
// variables to run the tests.
package clients
import (
"fmt"
"os"
"strings"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/openstack"
"github.com/gophercloud/gophercloud/openstack/blockstorage/noauth"
)
// AcceptanceTestChoices contains image and flavor selections for use by the acceptance tests.
type AcceptanceTestChoices 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
// FloatingIPPool contains the name of the pool from where to obtain floating IPs.
FloatingIPPoolName string
// NetworkName is the name of a network to launch the instance on.
NetworkName string
// ExternalNetworkID is the network ID of the external network.
ExternalNetworkID string
// ShareNetworkID is the Manila Share network ID
ShareNetworkID string
// DBDatastoreType is the datastore type for DB tests.
DBDatastoreType string
// DBDatastoreTypeID is the datastore type version for DB tests.
DBDatastoreVersion string
}
// AcceptanceTestChoicesFromEnv populates a ComputeChoices struct from environment variables.
// If any required state is missing, an `error` will be returned that enumerates the missing properties.
func AcceptanceTestChoicesFromEnv() (*AcceptanceTestChoices, error) {
imageID := os.Getenv("OS_IMAGE_ID")
flavorID := os.Getenv("OS_FLAVOR_ID")
flavorIDResize := os.Getenv("OS_FLAVOR_ID_RESIZE")
networkName := os.Getenv("OS_NETWORK_NAME")
floatingIPPoolName := os.Getenv("OS_POOL_NAME")
externalNetworkID := os.Getenv("OS_EXTGW_ID")
shareNetworkID := os.Getenv("OS_SHARE_NETWORK_ID")
dbDatastoreType := os.Getenv("OS_DB_DATASTORE_TYPE")
dbDatastoreVersion := os.Getenv("OS_DB_DATASTORE_VERSION")
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")
}
if floatingIPPoolName == "" {
missing = append(missing, "OS_POOL_NAME")
}
if externalNetworkID == "" {
missing = append(missing, "OS_EXTGW_ID")
}
if networkName == "" {
networkName = "private"
}
if shareNetworkID == "" {
missing = append(missing, "OS_SHARE_NETWORK_ID")
}
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 &AcceptanceTestChoices{
ImageID: imageID,
FlavorID: flavorID,
FlavorIDResize: flavorIDResize,
FloatingIPPoolName: floatingIPPoolName,
NetworkName: networkName,
ExternalNetworkID: externalNetworkID,
ShareNetworkID: shareNetworkID,
DBDatastoreType: dbDatastoreType,
DBDatastoreVersion: dbDatastoreVersion,
}, nil
}
// NewBlockStorageV1Client returns a *ServiceClient for making calls
// to the OpenStack Block Storage v1 API. An error will be returned
// if authentication or client creation was not possible.
func NewBlockStorageV1Client() (*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.NewBlockStorageV1(client, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
}
// NewBlockStorageV2Client returns a *ServiceClient for making calls
// to the OpenStack Block Storage v2 API. An error will be returned
// if authentication or client creation was not possible.
func NewBlockStorageV2Client() (*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.NewBlockStorageV2(client, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
}
// NewBlockStorageV3Client returns a *ServiceClient for making calls
// to the OpenStack Block Storage v3 API. An error will be returned
// if authentication or client creation was not possible.
func NewBlockStorageV3Client() (*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.NewBlockStorageV3(client, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
}
// NewBlockStorageV2NoAuthClient returns a noauth *ServiceClient for
// making calls to the OpenStack Block Storage v2 API. An error will be
// returned if client creation was not possible.
func NewBlockStorageV2NoAuthClient() (*gophercloud.ServiceClient, error) {
client, err := noauth.NewClient(gophercloud.AuthOptions{
Username: os.Getenv("OS_USERNAME"),
TenantName: os.Getenv("OS_TENANT_NAME"),
})
if err != nil {
return nil, err
}
return noauth.NewBlockStorageNoAuth(client, noauth.EndpointOpts{
CinderEndpoint: os.Getenv("CINDER_ENDPOINT"),
})
}
// NewBlockStorageV3NoAuthClient returns a noauth *ServiceClient for
// making calls to the OpenStack Block Storage v2 API. An error will be
// returned if client creation was not possible.
func NewBlockStorageV3NoAuthClient() (*gophercloud.ServiceClient, error) {
client, err := noauth.NewClient(gophercloud.AuthOptions{
Username: os.Getenv("OS_USERNAME"),
TenantName: os.Getenv("OS_TENANT_NAME"),
})
if err != nil {
return nil, err
}
return noauth.NewBlockStorageNoAuth(client, noauth.EndpointOpts{
CinderEndpoint: os.Getenv("CINDER_ENDPOINT"),
})
}
// NewComputeV2Client returns a *ServiceClient for making calls
// to the OpenStack Compute v2 API. An error will be returned
// if authentication or client creation was not possible.
func NewComputeV2Client() (*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"),
})
}
// NewDBV1Client returns a *ServiceClient for making calls
// to the OpenStack Database v1 API. An error will be returned
// if authentication or client creation was not possible.
func NewDBV1Client() (*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.NewDBV1(client, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
}
// NewDNSV2Client returns a *ServiceClient for making calls
// to the OpenStack Compute v2 API. An error will be returned
// if authentication or client creation was not possible.
func NewDNSV2Client() (*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.NewDNSV2(client, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
}
// NewIdentityV2Client returns a *ServiceClient for making calls
// to the OpenStack Identity v2 API. An error will be returned
// if authentication or client creation was not possible.
func NewIdentityV2Client() (*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.NewIdentityV2(client, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
}
// NewIdentityV2AdminClient returns a *ServiceClient for making calls
// to the Admin Endpoint of the OpenStack Identity v2 API. An error
// will be returned if authentication or client creation was not possible.
func NewIdentityV2AdminClient() (*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.NewIdentityV2(client, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
Availability: gophercloud.AvailabilityAdmin,
})
}
// NewIdentityV2UnauthenticatedClient returns an unauthenticated *ServiceClient
// for the OpenStack Identity v2 API. An error will be returned if
// authentication or client creation was not possible.
func NewIdentityV2UnauthenticatedClient() (*gophercloud.ServiceClient, error) {
ao, err := openstack.AuthOptionsFromEnv()
if err != nil {
return nil, err
}
client, err := openstack.NewClient(ao.IdentityEndpoint)
if err != nil {
return nil, err
}
return openstack.NewIdentityV2(client, gophercloud.EndpointOpts{})
}
// NewIdentityV3Client returns a *ServiceClient for making calls
// to the OpenStack Identity v3 API. An error will be returned
// if authentication or client creation was not possible.
func NewIdentityV3Client() (*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.NewIdentityV3(client, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
}
// NewIdentityV3UnauthenticatedClient returns an unauthenticated *ServiceClient
// for the OpenStack Identity v3 API. An error will be returned if
// authentication or client creation was not possible.
func NewIdentityV3UnauthenticatedClient() (*gophercloud.ServiceClient, error) {
ao, err := openstack.AuthOptionsFromEnv()
if err != nil {
return nil, err
}
client, err := openstack.NewClient(ao.IdentityEndpoint)
if err != nil {
return nil, err
}
return openstack.NewIdentityV3(client, gophercloud.EndpointOpts{})
}
// NewImageServiceV2Client returns a *ServiceClient for making calls to the
// OpenStack Image v2 API. An error will be returned if authentication or
// client creation was not possible.
func NewImageServiceV2Client() (*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.NewImageServiceV2(client, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
}
// NewNetworkV2Client returns a *ServiceClient for making calls to the
// OpenStack Networking v2 API. An error will be returned if authentication
// or client creation was not possible.
func NewNetworkV2Client() (*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.NewNetworkV2(client, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
}
// NewObjectStorageV1Client returns a *ServiceClient for making calls to the
// OpenStack Object Storage v1 API. An error will be returned if authentication
// or client creation was not possible.
func NewObjectStorageV1Client() (*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.NewObjectStorageV1(client, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
}
// NewSharedFileSystemV2Client returns a *ServiceClient for making calls
// to the OpenStack Shared File System v2 API. An error will be returned
// if authentication or client creation was not possible.
func NewSharedFileSystemV2Client() (*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.NewSharedFileSystemV2(client, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
}

View File

@@ -0,0 +1,173 @@
// Package extensions contains common functions for creating block storage
// resources that are extensions of the block storage API. See the `*_test.go`
// files for example usages.
package extensions
import (
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
"github.com/gophercloud/gophercloud/openstack/compute/v2/images"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
)
// CreateUploadImage will upload volume it as volume-baked image. An name of new image or err will be
// returned
func CreateUploadImage(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) (volumeactions.VolumeImage, error) {
if testing.Short() {
t.Skip("Skipping test that requires volume-backed image uploading in short mode.")
}
imageName := tools.RandomString("ACPTTEST", 16)
uploadImageOpts := volumeactions.UploadImageOpts{
ImageName: imageName,
Force: true,
}
volumeImage, err := volumeactions.UploadImage(client, volume.ID, uploadImageOpts).Extract()
if err != nil {
return volumeImage, err
}
t.Logf("Uploading volume %s as volume-backed image %s", volume.ID, imageName)
if err := volumes.WaitForStatus(client, volume.ID, "available", 60); err != nil {
return volumeImage, err
}
t.Logf("Uploaded volume %s as volume-backed image %s", volume.ID, imageName)
return volumeImage, nil
}
// DeleteUploadedImage deletes uploaded image. An error will be returned
// if the deletion request failed.
func DeleteUploadedImage(t *testing.T, client *gophercloud.ServiceClient, imageName string) error {
if testing.Short() {
t.Skip("Skipping test that requires volume-backed image removing in short mode.")
}
t.Logf("Getting image id for image name %s", imageName)
imageID, err := images.IDFromName(client, imageName)
if err != nil {
return err
}
t.Logf("Removing image %s", imageID)
err = images.Delete(client, imageID).ExtractErr()
if err != nil {
return err
}
return nil
}
// CreateVolumeAttach will attach a volume to an instance. An error will be
// returned if the attachment failed.
func CreateVolumeAttach(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume, server *servers.Server) error {
if testing.Short() {
t.Skip("Skipping test that requires volume attachment in short mode.")
}
attachOpts := volumeactions.AttachOpts{
MountPoint: "/mnt",
Mode: "rw",
InstanceUUID: server.ID,
}
t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID)
if err := volumeactions.Attach(client, volume.ID, attachOpts).ExtractErr(); err != nil {
return err
}
if err := volumes.WaitForStatus(client, volume.ID, "in-use", 60); err != nil {
return err
}
t.Logf("Attached volume %s to server %s", volume.ID, server.ID)
return nil
}
// CreateVolumeReserve creates a volume reservation. An error will be returned
// if the reservation failed.
func CreateVolumeReserve(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) error {
if testing.Short() {
t.Skip("Skipping test that requires volume reservation in short mode.")
}
t.Logf("Attempting to reserve volume %s", volume.ID)
if err := volumeactions.Reserve(client, volume.ID).ExtractErr(); err != nil {
return err
}
t.Logf("Reserved volume %s", volume.ID)
return nil
}
// DeleteVolumeAttach will detach a volume from an instance. A fatal error will
// occur if the snapshot failed to be deleted. This works best when used as a
// deferred function.
func DeleteVolumeAttach(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) {
t.Logf("Attepting to detach volume volume: %s", volume.ID)
detachOpts := volumeactions.DetachOpts{
AttachmentID: volume.Attachments[0].AttachmentID,
}
if err := volumeactions.Detach(client, volume.ID, detachOpts).ExtractErr(); err != nil {
t.Fatalf("Unable to detach volume %s: %v", volume.ID, err)
}
if err := volumes.WaitForStatus(client, volume.ID, "available", 60); err != nil {
t.Fatalf("Volume %s failed to become unavailable in 60 seconds: %v", volume.ID, err)
}
t.Logf("Detached volume: %s", volume.ID)
}
// DeleteVolumeReserve deletes a volume reservation. A fatal error will occur
// if the deletion request failed. This works best when used as a deferred
// function.
func DeleteVolumeReserve(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) {
if testing.Short() {
t.Skip("Skipping test that requires volume reservation in short mode.")
}
t.Logf("Attempting to unreserve volume %s", volume.ID)
if err := volumeactions.Unreserve(client, volume.ID).ExtractErr(); err != nil {
t.Fatalf("Unable to unreserve volume %s: %v", volume.ID, err)
}
t.Logf("Unreserved volume %s", volume.ID)
}
// ExtendVolumeSize will extend the size of a volume.
func ExtendVolumeSize(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) error {
t.Logf("Attempting to extend the size of volume %s", volume.ID)
extendOpts := volumeactions.ExtendSizeOpts{
NewSize: 2,
}
err := volumeactions.ExtendSize(client, volume.ID, extendOpts).ExtractErr()
if err != nil {
return err
}
if err := volumes.WaitForStatus(client, volume.ID, "available", 60); err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,3 @@
// The extensions package contains acceptance tests for the Openstack Cinder extensions service.
package extensions

View File

@@ -0,0 +1,36 @@
// +build acceptance blockstorage
package extensions
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/schedulerstats"
)
func TestSchedulerStatsList(t *testing.T) {
blockClient, err := clients.NewBlockStorageV2Client()
if err != nil {
t.Fatalf("Unable to create a blockstorage client: %v", err)
}
listOpts := schedulerstats.ListOpts{
Detail: true,
}
allPages, err := schedulerstats.List(blockClient, listOpts).AllPages()
if err != nil {
t.Fatalf("Unable to query schedulerstats: %v", err)
}
allStats, err := schedulerstats.ExtractStoragePools(allPages)
if err != nil {
t.Fatalf("Unable to extract pools: %v", err)
}
for _, stat := range allStats {
tools.PrintResource(t, stat)
}
}

View File

@@ -0,0 +1,170 @@
// +build acceptance blockstorage
package extensions
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
blockstorage "github.com/gophercloud/gophercloud/acceptance/openstack/blockstorage/v2"
compute "github.com/gophercloud/gophercloud/acceptance/openstack/compute/v2"
)
func TestVolumeActionsUploadImageDestroy(t *testing.T) {
blockClient, err := clients.NewBlockStorageV2Client()
if err != nil {
t.Fatalf("Unable to create a blockstorage client: %v", err)
}
computeClient, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
volume, err := blockstorage.CreateVolume(t, blockClient)
if err != nil {
t.Fatalf("Unable to create volume: %v", err)
}
defer blockstorage.DeleteVolume(t, blockClient, volume)
volumeImage, err := CreateUploadImage(t, blockClient, volume)
if err != nil {
t.Fatalf("Unable to upload volume-backed image: %v", err)
}
tools.PrintResource(t, volumeImage)
err = DeleteUploadedImage(t, computeClient, volumeImage.ImageName)
if err != nil {
t.Fatalf("Unable to delete volume-backed image: %v", err)
}
}
func TestVolumeActionsAttachCreateDestroy(t *testing.T) {
blockClient, err := clients.NewBlockStorageV2Client()
if err != nil {
t.Fatalf("Unable to create a blockstorage client: %v", err)
}
computeClient, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
server, err := compute.CreateServer(t, computeClient)
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
defer compute.DeleteServer(t, computeClient, server)
volume, err := blockstorage.CreateVolume(t, blockClient)
if err != nil {
t.Fatalf("Unable to create volume: %v", err)
}
defer blockstorage.DeleteVolume(t, blockClient, volume)
err = CreateVolumeAttach(t, blockClient, volume, server)
if err != nil {
t.Fatalf("Unable to attach volume: %v", err)
}
newVolume, err := volumes.Get(blockClient, volume.ID).Extract()
if err != nil {
t.Fatal("Unable to get updated volume information: %v", err)
}
DeleteVolumeAttach(t, blockClient, newVolume)
}
func TestVolumeActionsReserveUnreserve(t *testing.T) {
client, err := clients.NewBlockStorageV2Client()
if err != nil {
t.Fatalf("Unable to create blockstorage client: %v", err)
}
volume, err := blockstorage.CreateVolume(t, client)
if err != nil {
t.Fatalf("Unable to create volume: %v", err)
}
defer blockstorage.DeleteVolume(t, client, volume)
err = CreateVolumeReserve(t, client, volume)
if err != nil {
t.Fatalf("Unable to create volume reserve: %v", err)
}
defer DeleteVolumeReserve(t, client, volume)
}
func TestVolumeActionsExtendSize(t *testing.T) {
blockClient, err := clients.NewBlockStorageV2Client()
if err != nil {
t.Fatalf("Unable to create a blockstorage client: %v", err)
}
volume, err := blockstorage.CreateVolume(t, blockClient)
if err != nil {
t.Fatalf("Unable to create volume: %v", err)
}
defer blockstorage.DeleteVolume(t, blockClient, volume)
tools.PrintResource(t, volume)
err = ExtendVolumeSize(t, blockClient, volume)
if err != nil {
t.Fatalf("Unable to resize volume: %v", err)
}
newVolume, err := volumes.Get(blockClient, volume.ID).Extract()
if err != nil {
t.Fatal("Unable to get updated volume information: %v", err)
}
tools.PrintResource(t, newVolume)
}
// Note(jtopjian): I plan to work on this at some point, but it requires
// setting up a server with iscsi utils.
/*
func TestVolumeConns(t *testing.T) {
client, err := newClient()
th.AssertNoErr(t, err)
t.Logf("Creating volume")
cv, err := volumes.Create(client, &volumes.CreateOpts{
Size: 1,
Name: "blockv2-volume",
}).Extract()
th.AssertNoErr(t, err)
defer func() {
err = volumes.WaitForStatus(client, cv.ID, "available", 60)
th.AssertNoErr(t, err)
t.Logf("Deleting volume")
err = volumes.Delete(client, cv.ID).ExtractErr()
th.AssertNoErr(t, err)
}()
err = volumes.WaitForStatus(client, cv.ID, "available", 60)
th.AssertNoErr(t, err)
connOpts := &volumeactions.ConnectorOpts{
IP: "127.0.0.1",
Host: "stack",
Initiator: "iqn.1994-05.com.redhat:17cf566367d2",
Multipath: false,
Platform: "x86_64",
OSType: "linux2",
}
t.Logf("Initializing connection")
_, err = volumeactions.InitializeConnection(client, cv.ID, connOpts).Extract()
th.AssertNoErr(t, err)
t.Logf("Terminating connection")
err = volumeactions.TerminateConnection(client, cv.ID, connOpts).ExtractErr()
th.AssertNoErr(t, err)
}
*/

View File

@@ -0,0 +1,142 @@
// Package noauth contains common functions for creating block storage based
// resources for use in acceptance tests. See the `*_test.go` files for
// example usages.
package noauth
import (
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
)
// CreateVolume will create a volume with a random name and size of 1GB. An
// error will be returned if the volume was unable to be created.
func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) {
if testing.Short() {
t.Skip("Skipping test that requires volume creation in short mode.")
}
volumeName := tools.RandomString("ACPTTEST", 16)
t.Logf("Attempting to create volume: %s", volumeName)
createOpts := volumes.CreateOpts{
Size: 1,
Name: volumeName,
}
volume, err := volumes.Create(client, createOpts).Extract()
if err != nil {
return volume, err
}
err = volumes.WaitForStatus(client, volume.ID, "available", 60)
if err != nil {
return volume, err
}
return volume, nil
}
// CreateVolumeFromImage will create a volume from with a random name and size of
// 1GB. An error will be returned if the volume was unable to be created.
func CreateVolumeFromImage(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) {
if testing.Short() {
t.Skip("Skipping test that requires volume creation in short mode.")
}
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
volumeName := tools.RandomString("ACPTTEST", 16)
t.Logf("Attempting to create volume: %s", volumeName)
createOpts := volumes.CreateOpts{
Size: 1,
Name: volumeName,
ImageID: choices.ImageID,
}
volume, err := volumes.Create(client, createOpts).Extract()
if err != nil {
return volume, err
}
err = volumes.WaitForStatus(client, volume.ID, "available", 60)
if err != nil {
return volume, err
}
return volume, nil
}
// DeleteVolume will delete a volume. A fatal error will occur if the volume
// failed to be deleted. This works best when used as a deferred function.
func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) {
err := volumes.Delete(client, volume.ID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete volume %s: %v", volume.ID, err)
}
t.Logf("Deleted volume: %s", volume.ID)
}
// CreateSnapshot will create a snapshot of the specified volume.
// Snapshot will be assigned a random name and description.
func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) (*snapshots.Snapshot, error) {
if testing.Short() {
t.Skip("Skipping test that requires snapshot creation in short mode.")
}
snapshotName := tools.RandomString("ACPTTEST", 16)
snapshotDescription := tools.RandomString("ACPTTEST", 16)
t.Logf("Attempting to create snapshot: %s", snapshotName)
createOpts := snapshots.CreateOpts{
VolumeID: volume.ID,
Name: snapshotName,
Description: snapshotDescription,
}
snapshot, err := snapshots.Create(client, createOpts).Extract()
if err != nil {
return snapshot, err
}
err = snapshots.WaitForStatus(client, snapshot.ID, "available", 60)
if err != nil {
return snapshot, err
}
return snapshot, nil
}
// DeleteSnapshot will delete a snapshot. A fatal error will occur if the
// snapshot failed to be deleted.
func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) {
err := snapshots.Delete(client, snapshot.ID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete snapshot %s: %+v", snapshot.ID, err)
}
// Volumes can't be deleted until their snapshots have been,
// so block up to 120 seconds for the snapshot to delete.
err = gophercloud.WaitFor(120, func() (bool, error) {
_, err := snapshots.Get(client, snapshot.ID).Extract()
if err != nil {
return true, nil
}
return false, nil
})
if err != nil {
t.Fatalf("Error waiting for snapshot to delete: %v", err)
}
t.Logf("Deleted snapshot: %s", snapshot.ID)
}

View File

@@ -0,0 +1,3 @@
// The noauth package contains acceptance tests for the Openstack Cinder standalone service.
package noauth

View File

@@ -0,0 +1,58 @@
// +build acceptance blockstorage
package noauth
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots"
)
func TestSnapshotsList(t *testing.T) {
client, err := clients.NewBlockStorageV2NoAuthClient()
if err != nil {
t.Fatalf("Unable to create a blockstorage client: %v", err)
}
allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages()
if err != nil {
t.Fatalf("Unable to retrieve snapshots: %v", err)
}
allSnapshots, err := snapshots.ExtractSnapshots(allPages)
if err != nil {
t.Fatalf("Unable to extract snapshots: %v", err)
}
for _, snapshot := range allSnapshots {
tools.PrintResource(t, snapshot)
}
}
func TestSnapshotsCreateDelete(t *testing.T) {
client, err := clients.NewBlockStorageV2NoAuthClient()
if err != nil {
t.Fatalf("Unable to create a blockstorage client: %v", err)
}
volume, err := CreateVolume(t, client)
if err != nil {
t.Fatalf("Unable to create volume: %v", err)
}
defer DeleteVolume(t, client, volume)
snapshot, err := CreateSnapshot(t, client, volume)
if err != nil {
t.Fatalf("Unable to create snapshot: %v", err)
}
defer DeleteSnapshot(t, client, snapshot)
newSnapshot, err := snapshots.Get(client, snapshot.ID).Extract()
if err != nil {
t.Errorf("Unable to retrieve snapshot: %v", err)
}
tools.PrintResource(t, newSnapshot)
}

View File

@@ -0,0 +1,52 @@
// +build acceptance blockstorage
package noauth
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
)
func TestVolumesList(t *testing.T) {
client, err := clients.NewBlockStorageV2NoAuthClient()
if err != nil {
t.Fatalf("Unable to create a blockstorage client: %v", err)
}
allPages, err := volumes.List(client, volumes.ListOpts{}).AllPages()
if err != nil {
t.Fatalf("Unable to retrieve volumes: %v", err)
}
allVolumes, err := volumes.ExtractVolumes(allPages)
if err != nil {
t.Fatalf("Unable to extract volumes: %v", err)
}
for _, volume := range allVolumes {
tools.PrintResource(t, volume)
}
}
func TestVolumesCreateDestroy(t *testing.T) {
client, err := clients.NewBlockStorageV2NoAuthClient()
if err != nil {
t.Fatalf("Unable to create blockstorage client: %v", err)
}
volume, err := CreateVolume(t, client)
if err != nil {
t.Fatalf("Unable to create volume: %v", err)
}
defer DeleteVolume(t, client, volume)
newVolume, err := volumes.Get(client, volume.ID).Extract()
if err != nil {
t.Errorf("Unable to retrieve volume: %v", err)
}
tools.PrintResource(t, newVolume)
}

View File

@@ -0,0 +1,142 @@
// Package v1 contains common functions for creating block storage based
// resources for use in acceptance tests. See the `*_test.go` files for
// example usages.
package v1
import (
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/snapshots"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumetypes"
)
// CreateSnapshot will create a volume snapshot based off of a given volume and
// with a random name. An error will be returned if the snapshot failed to be
// created.
func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) (*snapshots.Snapshot, error) {
if testing.Short() {
t.Skip("Skipping test that requires snapshot creation in short mode.")
}
snapshotName := tools.RandomString("ACPTTEST", 16)
t.Logf("Attempting to create snapshot %s based on volume %s", snapshotName, volume.ID)
createOpts := snapshots.CreateOpts{
Name: snapshotName,
VolumeID: volume.ID,
}
snapshot, err := snapshots.Create(client, createOpts).Extract()
if err != nil {
return snapshot, err
}
err = snapshots.WaitForStatus(client, snapshot.ID, "available", 60)
if err != nil {
return snapshot, err
}
return snapshot, nil
}
// CreateVolume will create a volume with a random name and size of 1GB. An
// error will be returned if the volume was unable to be created.
func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) {
if testing.Short() {
t.Skip("Skipping test that requires volume creation in short mode.")
}
volumeName := tools.RandomString("ACPTTEST", 16)
t.Logf("Attempting to create volume: %s", volumeName)
createOpts := volumes.CreateOpts{
Size: 1,
Name: volumeName,
}
volume, err := volumes.Create(client, createOpts).Extract()
if err != nil {
return volume, err
}
err = volumes.WaitForStatus(client, volume.ID, "available", 60)
if err != nil {
return volume, err
}
return volume, nil
}
// CreateVolumeType will create a volume type with a random name. An error will
// be returned if the volume type was unable to be created.
func CreateVolumeType(t *testing.T, client *gophercloud.ServiceClient) (*volumetypes.VolumeType, error) {
volumeTypeName := tools.RandomString("ACPTTEST", 16)
t.Logf("Attempting to create volume type: %s", volumeTypeName)
createOpts := volumetypes.CreateOpts{
Name: volumeTypeName,
ExtraSpecs: map[string]interface{}{
"capabilities": "ssd",
"priority": 3,
},
}
volumeType, err := volumetypes.Create(client, createOpts).Extract()
if err != nil {
return volumeType, err
}
return volumeType, nil
}
// DeleteSnapshot will delete a snapshot. A fatal error will occur if the
// snapshot failed to be deleted. This works best when used as a deferred
// function.
func DeleteSnapshotshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) {
err := snapshots.Delete(client, snapshot.ID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete snapshot %s: %v", snapshot.ID, err)
}
// Volumes can't be deleted until their snapshots have been,
// so block up to 120 seconds for the snapshot to delete.
err = gophercloud.WaitFor(120, func() (bool, error) {
_, err := snapshots.Get(client, snapshot.ID).Extract()
if err != nil {
return true, nil
}
return false, nil
})
if err != nil {
t.Fatalf("Unable to wait for snapshot to delete: %v", err)
}
t.Logf("Deleted snapshot: %s", snapshot.ID)
}
// DeleteVolume will delete a volume. A fatal error will occur if the volume
// failed to be deleted. This works best when used as a deferred function.
func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) {
err := volumes.Delete(client, volume.ID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete volume %s: %v", volume.ID, err)
}
t.Logf("Deleted volume: %s", volume.ID)
}
// DeleteVolumeType will delete a volume type. A fatal error will occur if the
// volume type failed to be deleted. This works best when used as a deferred
// function.
func DeleteVolumeType(t *testing.T, client *gophercloud.ServiceClient, volumeType *volumetypes.VolumeType) {
err := volumetypes.Delete(client, volumeType.ID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete volume type %s: %v", volumeType.ID, err)
}
t.Logf("Deleted volume type: %s", volumeType.ID)
}

View File

@@ -0,0 +1,2 @@
// Package v1 contains openstack cinder acceptance tests
package v1

View File

@@ -0,0 +1,58 @@
// +build acceptance blockstorage
package v1
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/snapshots"
)
func TestSnapshotsList(t *testing.T) {
client, err := clients.NewBlockStorageV1Client()
if err != nil {
t.Fatalf("Unable to create a blockstorage client: %v", err)
}
allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages()
if err != nil {
t.Fatalf("Unable to retrieve snapshots: %v", err)
}
allSnapshots, err := snapshots.ExtractSnapshots(allPages)
if err != nil {
t.Fatalf("Unable to extract snapshots: %v", err)
}
for _, snapshot := range allSnapshots {
tools.PrintResource(t, snapshot)
}
}
func TestSnapshotsCreateDelete(t *testing.T) {
client, err := clients.NewBlockStorageV1Client()
if err != nil {
t.Fatalf("Unable to create a blockstorage client: %v", err)
}
volume, err := CreateVolume(t, client)
if err != nil {
t.Fatalf("Unable to create volume: %v", err)
}
defer DeleteVolume(t, client, volume)
snapshot, err := CreateSnapshot(t, client, volume)
if err != nil {
t.Fatalf("Unable to create snapshot: %v", err)
}
defer DeleteSnapshotshot(t, client, snapshot)
newSnapshot, err := snapshots.Get(client, snapshot.ID).Extract()
if err != nil {
t.Errorf("Unable to retrieve snapshot: %v", err)
}
tools.PrintResource(t, newSnapshot)
}

View File

@@ -0,0 +1,52 @@
// +build acceptance blockstorage
package v1
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes"
)
func TestVolumesList(t *testing.T) {
client, err := clients.NewBlockStorageV1Client()
if err != nil {
t.Fatalf("Unable to create a blockstorage client: %v", err)
}
allPages, err := volumes.List(client, volumes.ListOpts{}).AllPages()
if err != nil {
t.Fatalf("Unable to retrieve volumes: %v", err)
}
allVolumes, err := volumes.ExtractVolumes(allPages)
if err != nil {
t.Fatalf("Unable to extract volumes: %v", err)
}
for _, volume := range allVolumes {
tools.PrintResource(t, volume)
}
}
func TestVolumesCreateDestroy(t *testing.T) {
client, err := clients.NewBlockStorageV1Client()
if err != nil {
t.Fatalf("Unable to create blockstorage client: %v", err)
}
volume, err := CreateVolume(t, client)
if err != nil {
t.Fatalf("Unable to create volume: %v", err)
}
defer DeleteVolume(t, client, volume)
newVolume, err := volumes.Get(client, volume.ID).Extract()
if err != nil {
t.Errorf("Unable to retrieve volume: %v", err)
}
tools.PrintResource(t, newVolume)
}

View File

@@ -0,0 +1,47 @@
// +build acceptance blockstorage
package v1
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumetypes"
)
func TestVolumeTypesList(t *testing.T) {
client, err := clients.NewBlockStorageV1Client()
if err != nil {
t.Fatalf("Unable to create a blockstorage client: %v", err)
}
allPages, err := volumetypes.List(client).AllPages()
if err != nil {
t.Fatalf("Unable to retrieve volume types: %v", err)
}
allVolumeTypes, err := volumetypes.ExtractVolumeTypes(allPages)
if err != nil {
t.Fatalf("Unable to extract volume types: %v", err)
}
for _, volumeType := range allVolumeTypes {
tools.PrintResource(t, volumeType)
}
}
func TestVolumeTypesCreateDestroy(t *testing.T) {
client, err := clients.NewBlockStorageV1Client()
if err != nil {
t.Fatalf("Unable to create a blockstorage client: %v", err)
}
volumeType, err := CreateVolumeType(t, client)
if err != nil {
t.Fatalf("Unable to create volume type: %v", err)
}
defer DeleteVolumeType(t, client, volumeType)
tools.PrintResource(t, volumeType)
}

View File

@@ -0,0 +1,142 @@
// Package v2 contains common functions for creating block storage based
// resources for use in acceptance tests. See the `*_test.go` files for
// example usages.
package v2
import (
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
)
// CreateVolume will create a volume with a random name and size of 1GB. An
// error will be returned if the volume was unable to be created.
func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) {
if testing.Short() {
t.Skip("Skipping test that requires volume creation in short mode.")
}
volumeName := tools.RandomString("ACPTTEST", 16)
t.Logf("Attempting to create volume: %s", volumeName)
createOpts := volumes.CreateOpts{
Size: 1,
Name: volumeName,
}
volume, err := volumes.Create(client, createOpts).Extract()
if err != nil {
return volume, err
}
err = volumes.WaitForStatus(client, volume.ID, "available", 60)
if err != nil {
return volume, err
}
return volume, nil
}
// CreateVolumeFromImage will create a volume from with a random name and size of
// 1GB. An error will be returned if the volume was unable to be created.
func CreateVolumeFromImage(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) {
if testing.Short() {
t.Skip("Skipping test that requires volume creation in short mode.")
}
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
volumeName := tools.RandomString("ACPTTEST", 16)
t.Logf("Attempting to create volume: %s", volumeName)
createOpts := volumes.CreateOpts{
Size: 1,
Name: volumeName,
ImageID: choices.ImageID,
}
volume, err := volumes.Create(client, createOpts).Extract()
if err != nil {
return volume, err
}
err = volumes.WaitForStatus(client, volume.ID, "available", 60)
if err != nil {
return volume, err
}
return volume, nil
}
// DeleteVolume will delete a volume. A fatal error will occur if the volume
// failed to be deleted. This works best when used as a deferred function.
func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) {
err := volumes.Delete(client, volume.ID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete volume %s: %v", volume.ID, err)
}
t.Logf("Deleted volume: %s", volume.ID)
}
// CreateSnapshot will create a snapshot of the specified volume.
// Snapshot will be assigned a random name and description.
func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) (*snapshots.Snapshot, error) {
if testing.Short() {
t.Skip("Skipping test that requires snapshot creation in short mode.")
}
snapshotName := tools.RandomString("ACPTTEST", 16)
snapshotDescription := tools.RandomString("ACPTTEST", 16)
t.Logf("Attempting to create snapshot: %s", snapshotName)
createOpts := snapshots.CreateOpts{
VolumeID: volume.ID,
Name: snapshotName,
Description: snapshotDescription,
}
snapshot, err := snapshots.Create(client, createOpts).Extract()
if err != nil {
return snapshot, err
}
err = snapshots.WaitForStatus(client, snapshot.ID, "available", 60)
if err != nil {
return snapshot, err
}
return snapshot, nil
}
// DeleteSnapshot will delete a snapshot. A fatal error will occur if the
// snapshot failed to be deleted.
func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) {
err := snapshots.Delete(client, snapshot.ID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete snapshot %s: %+v", snapshot.ID, err)
}
// Volumes can't be deleted until their snapshots have been,
// so block up to 120 seconds for the snapshot to delete.
err = gophercloud.WaitFor(120, func() (bool, error) {
_, err := snapshots.Get(client, snapshot.ID).Extract()
if err != nil {
return true, nil
}
return false, nil
})
if err != nil {
t.Fatalf("Error waiting for snapshot to delete: %v", err)
}
t.Logf("Deleted snapshot: %s", snapshot.ID)
}

View File

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

View File

@@ -0,0 +1,58 @@
// +build acceptance blockstorage
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots"
)
func TestSnapshotsList(t *testing.T) {
client, err := clients.NewBlockStorageV2Client()
if err != nil {
t.Fatalf("Unable to create a blockstorage client: %v", err)
}
allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages()
if err != nil {
t.Fatalf("Unable to retrieve snapshots: %v", err)
}
allSnapshots, err := snapshots.ExtractSnapshots(allPages)
if err != nil {
t.Fatalf("Unable to extract snapshots: %v", err)
}
for _, snapshot := range allSnapshots {
tools.PrintResource(t, snapshot)
}
}
func TestSnapshotsCreateDelete(t *testing.T) {
client, err := clients.NewBlockStorageV2Client()
if err != nil {
t.Fatalf("Unable to create a blockstorage client: %v", err)
}
volume, err := CreateVolume(t, client)
if err != nil {
t.Fatalf("Unable to create volume: %v", err)
}
defer DeleteVolume(t, client, volume)
snapshot, err := CreateSnapshot(t, client, volume)
if err != nil {
t.Fatalf("Unable to create snapshot: %v", err)
}
defer DeleteSnapshot(t, client, snapshot)
newSnapshot, err := snapshots.Get(client, snapshot.ID).Extract()
if err != nil {
t.Errorf("Unable to retrieve snapshot: %v", err)
}
tools.PrintResource(t, newSnapshot)
}

View File

@@ -0,0 +1,52 @@
// +build acceptance blockstorage
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
)
func TestVolumesList(t *testing.T) {
client, err := clients.NewBlockStorageV2Client()
if err != nil {
t.Fatalf("Unable to create a blockstorage client: %v", err)
}
allPages, err := volumes.List(client, volumes.ListOpts{}).AllPages()
if err != nil {
t.Fatalf("Unable to retrieve volumes: %v", err)
}
allVolumes, err := volumes.ExtractVolumes(allPages)
if err != nil {
t.Fatalf("Unable to extract volumes: %v", err)
}
for _, volume := range allVolumes {
tools.PrintResource(t, volume)
}
}
func TestVolumesCreateDestroy(t *testing.T) {
client, err := clients.NewBlockStorageV2Client()
if err != nil {
t.Fatalf("Unable to create blockstorage client: %v", err)
}
volume, err := CreateVolume(t, client)
if err != nil {
t.Fatalf("Unable to create volume: %v", err)
}
defer DeleteVolume(t, client, volume)
newVolume, err := volumes.Get(client, volume.ID).Extract()
if err != nil {
t.Errorf("Unable to retrieve volume: %v", err)
}
tools.PrintResource(t, newVolume)
}

View File

@@ -0,0 +1,86 @@
// +build acceptance
package openstack
import (
"os"
"testing"
"time"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/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)
}
}
func TestReauth(t *testing.T) {
ao, err := openstack.AuthOptionsFromEnv()
if err != nil {
t.Fatalf("Unable to obtain environment auth options: %v", err)
}
// Allow reauth
ao.AllowReauth = true
provider, err := openstack.NewClient(ao.IdentityEndpoint)
if err != nil {
t.Fatalf("Unable to create provider: %v", err)
}
err = openstack.Authenticate(provider, ao)
if err != nil {
t.Fatalf("Unable to authenticate: %v", err)
}
t.Logf("Creating a compute client")
_, err = openstack.NewComputeV2(provider, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
if err != nil {
t.Fatalf("Unable to create compute client: %v", err)
}
t.Logf("Sleeping for 1 second")
time.Sleep(1 * time.Second)
t.Logf("Attempting to reauthenticate")
err = provider.ReauthFunc()
if err != nil {
t.Fatalf("Unable to reauthenticate: %v", err)
}
t.Logf("Creating a compute client")
_, err = openstack.NewComputeV2(provider, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
if err != nil {
t.Fatalf("Unable to create compute client: %v", err)
}
}

View File

@@ -0,0 +1,19 @@
// Package openstack contains common functions that can be used
// across all OpenStack components for acceptance testing.
package openstack
import (
"testing"
"github.com/gophercloud/gophercloud/openstack/common/extensions"
)
// PrintExtension prints an extension and all of its attributes.
func PrintExtension(t *testing.T, extension *extensions.Extension) {
t.Logf("Name: %s", extension.Name)
t.Logf("Namespace: %s", extension.Namespace)
t.Logf("Alias: %s", extension.Alias)
t.Logf("Description: %s", extension.Description)
t.Logf("Updated: %s", extension.Updated)
t.Logf("Links: %v", extension.Links)
}

View File

@@ -0,0 +1,261 @@
// +build acceptance compute bootfromvolume
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
blockstorage "github.com/gophercloud/gophercloud/acceptance/openstack/blockstorage/v2"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
)
func TestBootFromImage(t *testing.T) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
blockDevices := []bootfromvolume.BlockDevice{
bootfromvolume.BlockDevice{
BootIndex: 0,
DeleteOnTermination: true,
DestinationType: bootfromvolume.DestinationLocal,
SourceType: bootfromvolume.SourceImage,
UUID: choices.ImageID,
},
}
server, err := CreateBootableVolumeServer(t, client, blockDevices)
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
defer DeleteServer(t, client, server)
tools.PrintResource(t, server)
}
func TestBootFromNewVolume(t *testing.T) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
blockDevices := []bootfromvolume.BlockDevice{
bootfromvolume.BlockDevice{
DeleteOnTermination: true,
DestinationType: bootfromvolume.DestinationVolume,
SourceType: bootfromvolume.SourceImage,
UUID: choices.ImageID,
VolumeSize: 2,
},
}
server, err := CreateBootableVolumeServer(t, client, blockDevices)
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
defer DeleteServer(t, client, server)
tools.PrintResource(t, server)
}
func TestBootFromExistingVolume(t *testing.T) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
computeClient, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
blockStorageClient, err := clients.NewBlockStorageV2Client()
if err != nil {
t.Fatalf("Unable to create a block storage client: %v", err)
}
volume, err := blockstorage.CreateVolumeFromImage(t, blockStorageClient)
if err != nil {
t.Fatal(err)
}
blockDevices := []bootfromvolume.BlockDevice{
bootfromvolume.BlockDevice{
DeleteOnTermination: true,
DestinationType: bootfromvolume.DestinationVolume,
SourceType: bootfromvolume.SourceVolume,
UUID: volume.ID,
},
}
server, err := CreateBootableVolumeServer(t, computeClient, blockDevices)
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
defer DeleteServer(t, computeClient, server)
tools.PrintResource(t, server)
}
func TestBootFromMultiEphemeralServer(t *testing.T) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
blockDevices := []bootfromvolume.BlockDevice{
bootfromvolume.BlockDevice{
BootIndex: 0,
DestinationType: bootfromvolume.DestinationLocal,
DeleteOnTermination: true,
SourceType: bootfromvolume.SourceImage,
UUID: choices.ImageID,
VolumeSize: 5,
},
bootfromvolume.BlockDevice{
BootIndex: -1,
DestinationType: bootfromvolume.DestinationLocal,
DeleteOnTermination: true,
GuestFormat: "ext4",
SourceType: bootfromvolume.SourceBlank,
VolumeSize: 1,
},
bootfromvolume.BlockDevice{
BootIndex: -1,
DestinationType: bootfromvolume.DestinationLocal,
DeleteOnTermination: true,
GuestFormat: "ext4",
SourceType: bootfromvolume.SourceBlank,
VolumeSize: 1,
},
}
server, err := CreateMultiEphemeralServer(t, client, blockDevices)
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
defer DeleteServer(t, client, server)
tools.PrintResource(t, server)
}
func TestAttachNewVolume(t *testing.T) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
blockDevices := []bootfromvolume.BlockDevice{
bootfromvolume.BlockDevice{
BootIndex: 0,
DeleteOnTermination: true,
DestinationType: bootfromvolume.DestinationLocal,
SourceType: bootfromvolume.SourceImage,
UUID: choices.ImageID,
},
bootfromvolume.BlockDevice{
BootIndex: 1,
DeleteOnTermination: true,
DestinationType: bootfromvolume.DestinationVolume,
SourceType: bootfromvolume.SourceBlank,
VolumeSize: 2,
},
}
server, err := CreateBootableVolumeServer(t, client, blockDevices)
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
defer DeleteServer(t, client, server)
tools.PrintResource(t, server)
}
func TestAttachExistingVolume(t *testing.T) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
computeClient, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
blockStorageClient, err := clients.NewBlockStorageV2Client()
if err != nil {
t.Fatalf("Unable to create a block storage client: %v", err)
}
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
volume, err := blockstorage.CreateVolume(t, blockStorageClient)
if err != nil {
t.Fatal(err)
}
blockDevices := []bootfromvolume.BlockDevice{
bootfromvolume.BlockDevice{
BootIndex: 0,
DeleteOnTermination: true,
DestinationType: bootfromvolume.DestinationLocal,
SourceType: bootfromvolume.SourceImage,
UUID: choices.ImageID,
},
bootfromvolume.BlockDevice{
BootIndex: 1,
DeleteOnTermination: true,
DestinationType: bootfromvolume.DestinationVolume,
SourceType: bootfromvolume.SourceVolume,
UUID: volume.ID,
},
}
server, err := CreateBootableVolumeServer(t, computeClient, blockDevices)
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
defer DeleteServer(t, computeClient, server)
tools.PrintResource(t, server)
}

View File

@@ -0,0 +1,829 @@
// Package v2 contains common functions for creating compute-based resources
// for use in acceptance tests. See the `*_test.go` files for example usages.
package v2
import (
"crypto/rand"
"crypto/rsa"
"fmt"
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
dsr "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/defsecrules"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/networks"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/quotasets"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tenantnetworks"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach"
"github.com/gophercloud/gophercloud/openstack/compute/v2/flavors"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
"golang.org/x/crypto/ssh"
)
// AssociateFloatingIP will associate a floating IP with an instance. An error
// will be returned if the floating IP was unable to be associated.
func AssociateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, server *servers.Server) error {
associateOpts := floatingips.AssociateOpts{
FloatingIP: floatingIP.IP,
}
t.Logf("Attempting to associate floating IP %s to instance %s", floatingIP.IP, server.ID)
err := floatingips.AssociateInstance(client, server.ID, associateOpts).ExtractErr()
if err != nil {
return err
}
return nil
}
// AssociateFloatingIPWithFixedIP will associate a floating IP with an
// instance's specific fixed IP. An error will be returend if the floating IP
// was unable to be associated.
func AssociateFloatingIPWithFixedIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, server *servers.Server, fixedIP string) error {
associateOpts := floatingips.AssociateOpts{
FloatingIP: floatingIP.IP,
FixedIP: fixedIP,
}
t.Logf("Attempting to associate floating IP %s to fixed IP %s on instance %s", floatingIP.IP, fixedIP, server.ID)
err := floatingips.AssociateInstance(client, server.ID, associateOpts).ExtractErr()
if err != nil {
return err
}
return nil
}
// CreateBootableVolumeServer works like CreateServer but is configured with
// one or more block devices defined by passing in []bootfromvolume.BlockDevice.
// An error will be returned if a server was unable to be created.
func CreateBootableVolumeServer(t *testing.T, client *gophercloud.ServiceClient, blockDevices []bootfromvolume.BlockDevice) (*servers.Server, error) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
var server *servers.Server
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName)
if err != nil {
return server, err
}
name := tools.RandomString("ACPTTEST", 16)
t.Logf("Attempting to create bootable volume server: %s", name)
serverCreateOpts := servers.CreateOpts{
Name: name,
FlavorRef: choices.FlavorID,
Networks: []servers.Network{
servers.Network{UUID: networkID},
},
}
if blockDevices[0].SourceType == bootfromvolume.SourceImage && blockDevices[0].DestinationType == bootfromvolume.DestinationLocal {
serverCreateOpts.ImageRef = blockDevices[0].UUID
}
server, err = bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{
CreateOptsBuilder: serverCreateOpts,
BlockDevice: blockDevices,
}).Extract()
if err != nil {
return server, err
}
if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
return server, err
}
newServer, err := servers.Get(client, server.ID).Extract()
return newServer, nil
}
// CreateDefaultRule will create a default security group rule with a
// random port range between 80 and 90. An error will be returned if
// a default rule was unable to be created.
func CreateDefaultRule(t *testing.T, client *gophercloud.ServiceClient) (dsr.DefaultRule, error) {
createOpts := dsr.CreateOpts{
FromPort: tools.RandomInt(80, 89),
ToPort: tools.RandomInt(90, 99),
IPProtocol: "TCP",
CIDR: "0.0.0.0/0",
}
defaultRule, err := dsr.Create(client, createOpts).Extract()
if err != nil {
return *defaultRule, err
}
t.Logf("Created default rule: %s", defaultRule.ID)
return *defaultRule, nil
}
// CreateFlavor will create a flavor with a random name.
// An error will be returned if the flavor could not be created.
func CreateFlavor(t *testing.T, client *gophercloud.ServiceClient) (*flavors.Flavor, error) {
flavorName := tools.RandomString("flavor_", 5)
t.Logf("Attempting to create flavor %s", flavorName)
isPublic := true
createOpts := flavors.CreateOpts{
Name: flavorName,
RAM: 1,
VCPUs: 1,
Disk: gophercloud.IntToPointer(1),
IsPublic: &isPublic,
}
flavor, err := flavors.Create(client, createOpts).Extract()
if err != nil {
return nil, err
}
t.Logf("Successfully created flavor %s", flavor.ID)
return flavor, nil
}
// CreateFloatingIP will allocate a floating IP.
// An error will be returend if one was unable to be allocated.
func CreateFloatingIP(t *testing.T, client *gophercloud.ServiceClient) (*floatingips.FloatingIP, error) {
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
createOpts := floatingips.CreateOpts{
Pool: choices.FloatingIPPoolName,
}
floatingIP, err := floatingips.Create(client, createOpts).Extract()
if err != nil {
return floatingIP, err
}
t.Logf("Created floating IP: %s", floatingIP.ID)
return floatingIP, nil
}
func createKey() (string, error) {
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return "", err
}
publicKey := privateKey.PublicKey
pub, err := ssh.NewPublicKey(&publicKey)
if err != nil {
return "", err
}
pubBytes := ssh.MarshalAuthorizedKey(pub)
pk := string(pubBytes)
return pk, nil
}
// CreateKeyPair will create a KeyPair with a random name. An error will occur
// if the keypair failed to be created. An error will be returned if the
// keypair was unable to be created.
func CreateKeyPair(t *testing.T, client *gophercloud.ServiceClient) (*keypairs.KeyPair, error) {
keyPairName := tools.RandomString("keypair_", 5)
t.Logf("Attempting to create keypair: %s", keyPairName)
createOpts := keypairs.CreateOpts{
Name: keyPairName,
}
keyPair, err := keypairs.Create(client, createOpts).Extract()
if err != nil {
return keyPair, err
}
t.Logf("Created keypair: %s", keyPairName)
return keyPair, nil
}
// CreateMultiEphemeralServer works like CreateServer but is configured with
// one or more block devices defined by passing in []bootfromvolume.BlockDevice.
// These block devices act like block devices when booting from a volume but
// are actually local ephemeral disks.
// An error will be returned if a server was unable to be created.
func CreateMultiEphemeralServer(t *testing.T, client *gophercloud.ServiceClient, blockDevices []bootfromvolume.BlockDevice) (*servers.Server, error) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
var server *servers.Server
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName)
if err != nil {
return server, err
}
name := tools.RandomString("ACPTTEST", 16)
t.Logf("Attempting to create bootable volume server: %s", name)
serverCreateOpts := servers.CreateOpts{
Name: name,
FlavorRef: choices.FlavorID,
ImageRef: choices.ImageID,
Networks: []servers.Network{
servers.Network{UUID: networkID},
},
}
server, err = bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{
CreateOptsBuilder: serverCreateOpts,
BlockDevice: blockDevices,
}).Extract()
if err != nil {
return server, err
}
if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
return server, err
}
newServer, err := servers.Get(client, server.ID).Extract()
return newServer, nil
}
// CreatePrivateFlavor will create a private flavor with a random name.
// An error will be returned if the flavor could not be created.
func CreatePrivateFlavor(t *testing.T, client *gophercloud.ServiceClient) (*flavors.Flavor, error) {
flavorName := tools.RandomString("flavor_", 5)
t.Logf("Attempting to create flavor %s", flavorName)
isPublic := false
createOpts := flavors.CreateOpts{
Name: flavorName,
RAM: 1,
VCPUs: 1,
Disk: gophercloud.IntToPointer(1),
IsPublic: &isPublic,
}
flavor, err := flavors.Create(client, createOpts).Extract()
if err != nil {
return nil, err
}
t.Logf("Successfully created flavor %s", flavor.ID)
return flavor, nil
}
// CreateSecurityGroup will create a security group with a random name.
// An error will be returned if one was failed to be created.
func CreateSecurityGroup(t *testing.T, client *gophercloud.ServiceClient) (secgroups.SecurityGroup, error) {
createOpts := secgroups.CreateOpts{
Name: tools.RandomString("secgroup_", 5),
Description: "something",
}
securityGroup, err := secgroups.Create(client, createOpts).Extract()
if err != nil {
return *securityGroup, err
}
t.Logf("Created security group: %s", securityGroup.ID)
return *securityGroup, nil
}
// CreateSecurityGroupRule will create a security group rule with a random name
// and a random TCP port range between port 80 and 99. An error will be
// returned if the rule failed to be created.
func CreateSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, securityGroupID string) (secgroups.Rule, error) {
createOpts := secgroups.CreateRuleOpts{
ParentGroupID: securityGroupID,
FromPort: tools.RandomInt(80, 89),
ToPort: tools.RandomInt(90, 99),
IPProtocol: "TCP",
CIDR: "0.0.0.0/0",
}
rule, err := secgroups.CreateRule(client, createOpts).Extract()
if err != nil {
return *rule, err
}
t.Logf("Created security group rule: %s", rule.ID)
return *rule, nil
}
// CreateServer creates a basic instance with a randomly generated name.
// The flavor of the instance will be the value of the OS_FLAVOR_ID environment variable.
// The image will be the value of the OS_IMAGE_ID environment variable.
// The instance will be launched on the network specified in OS_NETWORK_NAME.
// An error will be returned if the instance was unable to be created.
func CreateServer(t *testing.T, client *gophercloud.ServiceClient) (*servers.Server, error) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
var server *servers.Server
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName)
if err != nil {
return server, err
}
name := tools.RandomString("ACPTTEST", 16)
t.Logf("Attempting to create server: %s", name)
pwd := tools.MakeNewPassword("")
server, err = servers.Create(client, servers.CreateOpts{
Name: name,
FlavorRef: choices.FlavorID,
ImageRef: choices.ImageID,
AdminPass: pwd,
Networks: []servers.Network{
servers.Network{UUID: networkID},
},
Metadata: map[string]string{
"abc": "def",
},
Personality: servers.Personality{
&servers.File{
Path: "/etc/test",
Contents: []byte("hello world"),
},
},
}).Extract()
if err != nil {
return server, err
}
if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
return server, err
}
return server, nil
}
// CreateServerWithoutImageRef creates a basic instance with a randomly generated name.
// The flavor of the instance will be the value of the OS_FLAVOR_ID environment variable.
// The image is intentionally missing to trigger an error.
// The instance will be launched on the network specified in OS_NETWORK_NAME.
// An error will be returned if the instance was unable to be created.
func CreateServerWithoutImageRef(t *testing.T, client *gophercloud.ServiceClient) (*servers.Server, error) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
var server *servers.Server
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName)
if err != nil {
return server, err
}
name := tools.RandomString("ACPTTEST", 16)
t.Logf("Attempting to create server: %s", name)
pwd := tools.MakeNewPassword("")
server, err = servers.Create(client, servers.CreateOpts{
Name: name,
FlavorRef: choices.FlavorID,
AdminPass: pwd,
Networks: []servers.Network{
servers.Network{UUID: networkID},
},
Personality: servers.Personality{
&servers.File{
Path: "/etc/test",
Contents: []byte("hello world"),
},
},
}).Extract()
if err != nil {
return server, err
}
if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
return server, err
}
return server, nil
}
// CreateServerGroup will create a server with a random name. An error will be
// returned if the server group failed to be created.
func CreateServerGroup(t *testing.T, client *gophercloud.ServiceClient, policy string) (*servergroups.ServerGroup, error) {
sg, err := servergroups.Create(client, &servergroups.CreateOpts{
Name: "test",
Policies: []string{policy},
}).Extract()
if err != nil {
return sg, err
}
return sg, nil
}
// CreateServerInServerGroup works like CreateServer but places the instance in
// a specified Server Group.
func CreateServerInServerGroup(t *testing.T, client *gophercloud.ServiceClient, serverGroup *servergroups.ServerGroup) (*servers.Server, error) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
var server *servers.Server
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName)
if err != nil {
return server, err
}
name := tools.RandomString("ACPTTEST", 16)
t.Logf("Attempting to create server: %s", name)
pwd := tools.MakeNewPassword("")
serverCreateOpts := servers.CreateOpts{
Name: name,
FlavorRef: choices.FlavorID,
ImageRef: choices.ImageID,
AdminPass: pwd,
Networks: []servers.Network{
servers.Network{UUID: networkID},
},
}
schedulerHintsOpts := schedulerhints.CreateOptsExt{
CreateOptsBuilder: serverCreateOpts,
SchedulerHints: schedulerhints.SchedulerHints{
Group: serverGroup.ID,
},
}
server, err = servers.Create(client, schedulerHintsOpts).Extract()
if err != nil {
return server, err
}
return server, nil
}
// CreateServerWithPublicKey works the same as CreateServer, but additionally
// configures the server with a specified Key Pair name.
func CreateServerWithPublicKey(t *testing.T, client *gophercloud.ServiceClient, keyPairName string) (*servers.Server, error) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
var server *servers.Server
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName)
if err != nil {
return server, err
}
name := tools.RandomString("ACPTTEST", 16)
t.Logf("Attempting to create server: %s", name)
serverCreateOpts := servers.CreateOpts{
Name: name,
FlavorRef: choices.FlavorID,
ImageRef: choices.ImageID,
Networks: []servers.Network{
servers.Network{UUID: networkID},
},
}
server, err = servers.Create(client, keypairs.CreateOptsExt{
CreateOptsBuilder: serverCreateOpts,
KeyName: keyPairName,
}).Extract()
if err != nil {
return server, err
}
if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
return server, err
}
return server, nil
}
// CreateVolumeAttachment will attach a volume to a server. An error will be
// returned if the volume failed to attach.
func CreateVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, server *servers.Server, volume *volumes.Volume) (*volumeattach.VolumeAttachment, error) {
volumeAttachOptions := volumeattach.CreateOpts{
VolumeID: volume.ID,
}
t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID)
volumeAttachment, err := volumeattach.Create(client, server.ID, volumeAttachOptions).Extract()
if err != nil {
return volumeAttachment, err
}
if err := volumes.WaitForStatus(blockClient, volume.ID, "in-use", 60); err != nil {
return volumeAttachment, err
}
return volumeAttachment, nil
}
// DeleteDefaultRule deletes a default security group rule.
// A fatal error will occur if the rule failed to delete. This works best when
// using it as a deferred function.
func DeleteDefaultRule(t *testing.T, client *gophercloud.ServiceClient, defaultRule dsr.DefaultRule) {
err := dsr.Delete(client, defaultRule.ID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete default rule %s: %v", defaultRule.ID, err)
}
t.Logf("Deleted default rule: %s", defaultRule.ID)
}
// DeleteFlavor will delete a flavor. A fatal error will occur if the flavor
// could not be deleted. This works best when using it as a deferred function.
func DeleteFlavor(t *testing.T, client *gophercloud.ServiceClient, flavor *flavors.Flavor) {
err := flavors.Delete(client, flavor.ID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete flavor %s", flavor.ID)
}
t.Logf("Deleted flavor: %s", flavor.ID)
}
// DeleteFloatingIP will de-allocate a floating IP. A fatal error will occur if
// the floating IP failed to de-allocate. This works best when using it as a
// deferred function.
func DeleteFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP) {
err := floatingips.Delete(client, floatingIP.ID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete floating IP %s: %v", floatingIP.ID, err)
}
t.Logf("Deleted floating IP: %s", floatingIP.ID)
}
// DeleteKeyPair will delete a specified keypair. A fatal error will occur if
// the keypair failed to be deleted. This works best when used as a deferred
// function.
func DeleteKeyPair(t *testing.T, client *gophercloud.ServiceClient, keyPair *keypairs.KeyPair) {
err := keypairs.Delete(client, keyPair.Name).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete keypair %s: %v", keyPair.Name, err)
}
t.Logf("Deleted keypair: %s", keyPair.Name)
}
// DeleteSecurityGroup will delete a security group. A fatal error will occur
// if the group failed to be deleted. This works best as a deferred function.
func DeleteSecurityGroup(t *testing.T, client *gophercloud.ServiceClient, securityGroup secgroups.SecurityGroup) {
err := secgroups.Delete(client, securityGroup.ID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete security group %s: %s", securityGroup.ID, err)
}
t.Logf("Deleted security group: %s", securityGroup.ID)
}
// DeleteSecurityGroupRule will delete a security group rule. A fatal error
// will occur if the rule failed to be deleted. This works best when used
// as a deferred function.
func DeleteSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, rule secgroups.Rule) {
err := secgroups.DeleteRule(client, rule.ID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete rule: %v", err)
}
t.Logf("Deleted security group rule: %s", rule.ID)
}
// DeleteServer deletes an instance via its UUID.
// A fatal error will occur if the instance failed to be destroyed. This works
// best when using it as a deferred function.
func DeleteServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) {
err := servers.Delete(client, server.ID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete server %s: %s", server.ID, err)
}
t.Logf("Deleted server: %s", server.ID)
}
// DeleteServerGroup will delete a server group. A fatal error will occur if
// the server group failed to be deleted. This works best when used as a
// deferred function.
func DeleteServerGroup(t *testing.T, client *gophercloud.ServiceClient, serverGroup *servergroups.ServerGroup) {
err := servergroups.Delete(client, serverGroup.ID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete server group %s: %v", serverGroup.ID, err)
}
t.Logf("Deleted server group %s", serverGroup.ID)
}
// DeleteVolumeAttachment will disconnect a volume from an instance. A fatal
// error will occur if the volume failed to detach. This works best when used
// as a deferred function.
func DeleteVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, server *servers.Server, volumeAttachment *volumeattach.VolumeAttachment) {
err := volumeattach.Delete(client, server.ID, volumeAttachment.VolumeID).ExtractErr()
if err != nil {
t.Fatalf("Unable to detach volume: %v", err)
}
if err := volumes.WaitForStatus(blockClient, volumeAttachment.ID, "available", 60); err != nil {
t.Fatalf("Unable to wait for volume: %v", err)
}
t.Logf("Deleted volume: %s", volumeAttachment.VolumeID)
}
// DisassociateFloatingIP will disassociate a floating IP from an instance. A
// fatal error will occur if the floating IP failed to disassociate. This works
// best when using it as a deferred function.
func DisassociateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, server *servers.Server) {
disassociateOpts := floatingips.DisassociateOpts{
FloatingIP: floatingIP.IP,
}
err := floatingips.DisassociateInstance(client, server.ID, disassociateOpts).ExtractErr()
if err != nil {
t.Fatalf("Unable to disassociate floating IP %s from server %s: %v", floatingIP.IP, server.ID, err)
}
t.Logf("Disassociated floating IP %s from server %s", floatingIP.IP, server.ID)
}
// GetNetworkIDFromNetworks will return the network ID from a specified network
// UUID using the os-networks API extension. An error will be returned if the
// network could not be retrieved.
func GetNetworkIDFromNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) {
allPages, err := networks.List(client).AllPages()
if err != nil {
t.Fatalf("Unable to list networks: %v", err)
}
networkList, err := networks.ExtractNetworks(allPages)
if err != nil {
t.Fatalf("Unable to list networks: %v", err)
}
networkID := ""
for _, network := range networkList {
t.Logf("Network: %v", network)
if network.Label == networkName {
networkID = network.ID
}
}
t.Logf("Found network ID for %s: %s", networkName, networkID)
return networkID, nil
}
// GetNetworkIDFromTenantNetworks will return the network UUID for a given
// network name using the os-tenant-networks API extension. An error will be
// returned if the network could not be retrieved.
func GetNetworkIDFromTenantNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) {
allPages, err := tenantnetworks.List(client).AllPages()
if err != nil {
return "", err
}
allTenantNetworks, err := tenantnetworks.ExtractNetworks(allPages)
if err != nil {
return "", err
}
for _, network := range allTenantNetworks {
if network.Name == networkName {
return network.ID, nil
}
}
return "", fmt.Errorf("Failed to obtain network ID for network %s", networkName)
}
// ImportPublicKey will create a KeyPair with a random name and a specified
// public key. An error will be returned if the keypair failed to be created.
func ImportPublicKey(t *testing.T, client *gophercloud.ServiceClient, publicKey string) (*keypairs.KeyPair, error) {
keyPairName := tools.RandomString("keypair_", 5)
t.Logf("Attempting to create keypair: %s", keyPairName)
createOpts := keypairs.CreateOpts{
Name: keyPairName,
PublicKey: publicKey,
}
keyPair, err := keypairs.Create(client, createOpts).Extract()
if err != nil {
return keyPair, err
}
t.Logf("Created keypair: %s", keyPairName)
return keyPair, nil
}
// ResizeServer performs a resize action on an instance. An error will be
// returned if the instance failed to resize.
// The new flavor that the instance will be resized to is specified in OS_FLAVOR_ID_RESIZE.
func ResizeServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) error {
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
opts := &servers.ResizeOpts{
FlavorRef: choices.FlavorIDResize,
}
if res := servers.Resize(client, server.ID, opts); res.Err != nil {
return res.Err
}
if err := WaitForComputeStatus(client, server, "VERIFY_RESIZE"); err != nil {
return err
}
return nil
}
// WaitForComputeStatus will poll an instance's status until it either matches
// the specified status or the status becomes ERROR.
func WaitForComputeStatus(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
}
if latest.Status == "ERROR" {
return false, fmt.Errorf("Instance in ERROR state")
}
return false, nil
})
}
//Convenience method to fill an QuotaSet-UpdateOpts-struct from a QuotaSet-struct
func FillUpdateOptsFromQuotaSet(src quotasets.QuotaSet, dest *quotasets.UpdateOpts) {
dest.FixedIPs = &src.FixedIPs
dest.FloatingIPs = &src.FloatingIPs
dest.InjectedFileContentBytes = &src.InjectedFileContentBytes
dest.InjectedFilePathBytes = &src.InjectedFilePathBytes
dest.InjectedFiles = &src.InjectedFiles
dest.KeyPairs = &src.KeyPairs
dest.RAM = &src.RAM
dest.SecurityGroupRules = &src.SecurityGroupRules
dest.SecurityGroups = &src.SecurityGroups
dest.Cores = &src.Cores
dest.Instances = &src.Instances
dest.ServerGroups = &src.ServerGroups
dest.ServerGroupMembers = &src.ServerGroupMembers
dest.MetadataItems = &src.MetadataItems
}

View File

@@ -0,0 +1,67 @@
// +build acceptance compute defsecrules
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
dsr "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/defsecrules"
)
func TestDefSecRulesList(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
allPages, err := dsr.List(client).AllPages()
if err != nil {
t.Fatalf("Unable to list default rules: %v", err)
}
allDefaultRules, err := dsr.ExtractDefaultRules(allPages)
if err != nil {
t.Fatalf("Unable to extract default rules: %v", err)
}
for _, defaultRule := range allDefaultRules {
tools.PrintResource(t, defaultRule)
}
}
func TestDefSecRulesCreate(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
defaultRule, err := CreateDefaultRule(t, client)
if err != nil {
t.Fatalf("Unable to create default rule: %v", err)
}
defer DeleteDefaultRule(t, client, defaultRule)
tools.PrintResource(t, defaultRule)
}
func TestDefSecRulesGet(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
defaultRule, err := CreateDefaultRule(t, client)
if err != nil {
t.Fatalf("Unable to create default rule: %v", err)
}
defer DeleteDefaultRule(t, client, defaultRule)
newDefaultRule, err := dsr.Get(client, defaultRule.ID).Extract()
if err != nil {
t.Fatalf("Unable to get default rule %s: %v", defaultRule.ID, err)
}
tools.PrintResource(t, newDefaultRule)
}

View File

@@ -0,0 +1,46 @@
// +build acceptance compute extensions
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/common/extensions"
)
func TestExtensionsList(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
allPages, err := extensions.List(client).AllPages()
if err != nil {
t.Fatalf("Unable to list extensions: %v", err)
}
allExtensions, err := extensions.ExtractExtensions(allPages)
if err != nil {
t.Fatalf("Unable to extract extensions: %v", err)
}
for _, extension := range allExtensions {
tools.PrintResource(t, extension)
}
}
func TestExtensionGet(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
extension, err := extensions.Get(client, "os-admin-actions").Extract()
if err != nil {
t.Fatalf("Unable to get extension os-admin-actions: %v", err)
}
tools.PrintResource(t, extension)
}

View File

@@ -0,0 +1,116 @@
// +build acceptance compute flavors
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/compute/v2/flavors"
)
func TestFlavorsList(t *testing.T) {
t.Logf("** Default flavors (same as Project flavors): **")
t.Logf("")
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
allPages, err := flavors.ListDetail(client, nil).AllPages()
if err != nil {
t.Fatalf("Unable to retrieve flavors: %v", err)
}
allFlavors, err := flavors.ExtractFlavors(allPages)
if err != nil {
t.Fatalf("Unable to extract flavor results: %v", err)
}
for _, flavor := range allFlavors {
tools.PrintResource(t, flavor)
}
flavorAccessTypes := [3]flavors.AccessType{flavors.PublicAccess, flavors.PrivateAccess, flavors.AllAccess}
for _, flavorAccessType := range flavorAccessTypes {
t.Logf("** %s flavors: **", flavorAccessType)
t.Logf("")
allPages, err := flavors.ListDetail(client, flavors.ListOpts{AccessType: flavorAccessType}).AllPages()
if err != nil {
t.Fatalf("Unable to retrieve flavors: %v", err)
}
allFlavors, err := flavors.ExtractFlavors(allPages)
if err != nil {
t.Fatalf("Unable to extract flavor results: %v", err)
}
for _, flavor := range allFlavors {
tools.PrintResource(t, flavor)
t.Logf("")
}
}
}
func TestFlavorsGet(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
choices, err := clients.AcceptanceTestChoicesFromEnv()
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)
}
tools.PrintResource(t, flavor)
}
func TestFlavorCreateDelete(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
flavor, err := CreateFlavor(t, client)
if err != nil {
t.Fatalf("Unable to create flavor: %v", err)
}
defer DeleteFlavor(t, client, flavor)
tools.PrintResource(t, flavor)
}
func TestFlavorAccessesList(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
flavor, err := CreatePrivateFlavor(t, client)
if err != nil {
t.Fatalf("Unable to create flavor: %v", err)
}
defer DeleteFlavor(t, client, flavor)
allPages, err := flavors.ListAccesses(client, flavor.ID).AllPages()
if err != nil {
t.Fatalf("Unable to list flavor accesses: %v", err)
}
allAccesses, err := flavors.ExtractAccesses(allPages)
if err != nil {
t.Fatalf("Unable to extract accesses: %v", err)
}
for _, access := range allAccesses {
tools.PrintResource(t, access)
}
}

View File

@@ -0,0 +1,148 @@
// +build acceptance compute servers
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
)
func TestFloatingIPsList(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
allPages, err := floatingips.List(client).AllPages()
if err != nil {
t.Fatalf("Unable to retrieve floating IPs: %v", err)
}
allFloatingIPs, err := floatingips.ExtractFloatingIPs(allPages)
if err != nil {
t.Fatalf("Unable to extract floating IPs: %v", err)
}
for _, floatingIP := range allFloatingIPs {
tools.PrintResource(t, floatingIP)
}
}
func TestFloatingIPsCreate(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
floatingIP, err := CreateFloatingIP(t, client)
if err != nil {
t.Fatalf("Unable to create floating IP: %v", err)
}
defer DeleteFloatingIP(t, client, floatingIP)
tools.PrintResource(t, floatingIP)
}
func TestFloatingIPsAssociate(t *testing.T) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
server, err := CreateServer(t, client)
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
defer DeleteServer(t, client, server)
floatingIP, err := CreateFloatingIP(t, client)
if err != nil {
t.Fatalf("Unable to create floating IP: %v", err)
}
defer DeleteFloatingIP(t, client, floatingIP)
tools.PrintResource(t, floatingIP)
err = AssociateFloatingIP(t, client, floatingIP, server)
if err != nil {
t.Fatalf("Unable to associate floating IP %s with server %s: %v", floatingIP.IP, server.ID, err)
}
defer DisassociateFloatingIP(t, client, floatingIP, server)
newFloatingIP, err := floatingips.Get(client, floatingIP.ID).Extract()
if err != nil {
t.Fatalf("Unable to get floating IP %s: %v", floatingIP.ID, err)
}
t.Logf("Floating IP %s is associated with Fixed IP %s", floatingIP.IP, newFloatingIP.FixedIP)
tools.PrintResource(t, newFloatingIP)
}
func TestFloatingIPsFixedIPAssociate(t *testing.T) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
server, err := CreateServer(t, client)
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
defer DeleteServer(t, client, server)
newServer, err := servers.Get(client, server.ID).Extract()
if err != nil {
t.Fatalf("Unable to get server %s: %v", server.ID, err)
}
floatingIP, err := CreateFloatingIP(t, client)
if err != nil {
t.Fatalf("Unable to create floating IP: %v", err)
}
defer DeleteFloatingIP(t, client, floatingIP)
tools.PrintResource(t, floatingIP)
var fixedIP string
for _, networkAddresses := range newServer.Addresses[choices.NetworkName].([]interface{}) {
address := networkAddresses.(map[string]interface{})
if address["OS-EXT-IPS:type"] == "fixed" {
if address["version"].(float64) == 4 {
fixedIP = address["addr"].(string)
}
}
}
err = AssociateFloatingIPWithFixedIP(t, client, floatingIP, newServer, fixedIP)
if err != nil {
t.Fatalf("Unable to associate floating IP %s with server %s: %v", floatingIP.IP, newServer.ID, err)
}
defer DisassociateFloatingIP(t, client, floatingIP, newServer)
newFloatingIP, err := floatingips.Get(client, floatingIP.ID).Extract()
if err != nil {
t.Fatalf("Unable to get floating IP %s: %v", floatingIP.ID, err)
}
t.Logf("Floating IP %s is associated with Fixed IP %s", floatingIP.IP, newFloatingIP.FixedIP)
tools.PrintResource(t, newFloatingIP)
}

View File

@@ -0,0 +1,32 @@
// +build acceptance compute hypervisors
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/hypervisors"
)
func TestHypervisorsList(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
allPages, err := hypervisors.List(client).AllPages()
if err != nil {
t.Fatalf("Unable to list hypervisors: %v", err)
}
allHypervisors, err := hypervisors.ExtractHypervisors(allPages)
if err != nil {
t.Fatalf("Unable to extract hypervisors")
}
for _, h := range allHypervisors {
tools.PrintResource(t, h)
}
}

View File

@@ -0,0 +1,51 @@
// +build acceptance compute images
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/compute/v2/images"
)
func TestImagesList(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute: client: %v", err)
}
allPages, err := images.ListDetail(client, nil).AllPages()
if err != nil {
t.Fatalf("Unable to retrieve images: %v", err)
}
allImages, err := images.ExtractImages(allPages)
if err != nil {
t.Fatalf("Unable to extract image results: %v", err)
}
for _, image := range allImages {
tools.PrintResource(t, image)
}
}
func TestImagesGet(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute: client: %v", err)
}
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
image, err := images.Get(client, choices.ImageID).Extract()
if err != nil {
t.Fatalf("Unable to get image information: %v", err)
}
tools.PrintResource(t, image)
}

View File

@@ -0,0 +1,107 @@
// +build acceptance compute keypairs
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
)
const keyName = "gophercloud_test_key_pair"
func TestKeypairsList(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
allPages, err := keypairs.List(client).AllPages()
if err != nil {
t.Fatalf("Unable to retrieve keypairs: %s", err)
}
allKeys, err := keypairs.ExtractKeyPairs(allPages)
if err != nil {
t.Fatalf("Unable to extract keypairs results: %s", err)
}
for _, keypair := range allKeys {
tools.PrintResource(t, keypair)
}
}
func TestKeypairsCreate(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
keyPair, err := CreateKeyPair(t, client)
if err != nil {
t.Fatalf("Unable to create key pair: %v", err)
}
defer DeleteKeyPair(t, client, keyPair)
tools.PrintResource(t, keyPair)
}
func TestKeypairsImportPublicKey(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
publicKey, err := createKey()
if err != nil {
t.Fatalf("Unable to create public key: %s", err)
}
keyPair, err := ImportPublicKey(t, client, publicKey)
if err != nil {
t.Fatalf("Unable to create keypair: %s", err)
}
defer DeleteKeyPair(t, client, keyPair)
tools.PrintResource(t, keyPair)
}
func TestKeypairsServerCreateWithKey(t *testing.T) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
publicKey, err := createKey()
if err != nil {
t.Fatalf("Unable to create public key: %s", err)
}
keyPair, err := ImportPublicKey(t, client, publicKey)
if err != nil {
t.Fatalf("Unable to create keypair: %s", err)
}
defer DeleteKeyPair(t, client, keyPair)
server, err := CreateServerWithPublicKey(t, client, keyPair.Name)
if err != nil {
t.Fatalf("Unable to create server: %s", err)
}
defer DeleteServer(t, client, server)
server, err = servers.Get(client, server.ID).Extract()
if err != nil {
t.Fatalf("Unable to retrieve server: %s", err)
}
if server.KeyName != keyPair.Name {
t.Fatalf("key name of server %s is %s, not %s", server.ID, server.KeyName, keyPair.Name)
}
}

View File

@@ -0,0 +1,52 @@
// +build acceptance compute limits
package v2
import (
"strings"
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/limits"
)
func TestLimits(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
limits, err := limits.Get(client, nil).Extract()
if err != nil {
t.Fatalf("Unable to get limits: %v", err)
}
t.Logf("Limits for scoped user:")
t.Logf("%#v", limits)
}
func TestLimitsForTenant(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
// I think this is the easiest way to get the tenant ID while being
// agnostic to Identity v2 and v3.
// Technically we're just returning the limits for ourselves, but it's
// the fact that we're specifying a tenant ID that is important here.
endpointParts := strings.Split(client.Endpoint, "/")
tenantID := endpointParts[4]
getOpts := limits.GetOpts{
TenantID: tenantID,
}
limits, err := limits.Get(client, getOpts).Extract()
if err != nil {
t.Fatalf("Unable to get absolute limits: %v", err)
}
t.Logf("Limits for tenant %s:", tenantID)
t.Logf("%#v", limits)
}

View File

@@ -0,0 +1,30 @@
// +build acceptance compute servers
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/migrate"
)
func TestMigrate(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
server, err := CreateServer(t, client)
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
defer DeleteServer(t, client, server)
t.Logf("Attempting to migrate server %s", server.ID)
err = migrate.Migrate(client, server.ID).ExtractErr()
if err != nil {
t.Fatalf("Error during migration: %v", err)
}
}

View File

@@ -0,0 +1,56 @@
// +build acceptance compute servers
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/networks"
)
func TestNetworksList(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
allPages, err := networks.List(client).AllPages()
if err != nil {
t.Fatalf("Unable to list networks: %v", err)
}
allNetworks, err := networks.ExtractNetworks(allPages)
if err != nil {
t.Fatalf("Unable to list networks: %v", err)
}
for _, network := range allNetworks {
tools.PrintResource(t, network)
}
}
func TestNetworksGet(t *testing.T) {
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName)
if err != nil {
t.Fatal(err)
}
network, err := networks.Get(client, networkID).Extract()
if err != nil {
t.Fatalf("Unable to get network %s: %v", networkID, err)
}
tools.PrintResource(t, network)
}

View File

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

View File

@@ -0,0 +1,184 @@
// +build acceptance compute quotasets
package v2
import (
"fmt"
"os"
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/quotasets"
"github.com/gophercloud/gophercloud/openstack/identity/v2/tenants"
th "github.com/gophercloud/gophercloud/testhelper"
)
func TestQuotasetGet(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
identityClient, err := clients.NewIdentityV2Client()
if err != nil {
t.Fatalf("Unable to get a new identity client: %v", err)
}
tenantID, err := getTenantID(t, identityClient)
if err != nil {
t.Fatal(err)
}
quotaSet, err := quotasets.Get(client, tenantID).Extract()
if err != nil {
t.Fatal(err)
}
tools.PrintResource(t, quotaSet)
}
func getTenantID(t *testing.T, client *gophercloud.ServiceClient) (string, error) {
allPages, err := tenants.List(client, nil).AllPages()
if err != nil {
t.Fatalf("Unable to get list of tenants: %v", err)
}
allTenants, err := tenants.ExtractTenants(allPages)
if err != nil {
t.Fatalf("Unable to extract tenants: %v", err)
}
for _, tenant := range allTenants {
return tenant.ID, nil
}
return "", fmt.Errorf("Unable to get tenant ID")
}
func getTenantIDByName(t *testing.T, client *gophercloud.ServiceClient, name string) (string, error) {
allPages, err := tenants.List(client, nil).AllPages()
if err != nil {
t.Fatalf("Unable to get list of tenants: %v", err)
}
allTenants, err := tenants.ExtractTenants(allPages)
if err != nil {
t.Fatalf("Unable to extract tenants: %v", err)
}
for _, tenant := range allTenants {
if tenant.Name == name {
return tenant.ID, nil
}
}
return "", fmt.Errorf("Unable to get tenant ID")
}
//What will be sent as desired Quotas to the Server
var UpdatQuotaOpts = quotasets.UpdateOpts{
FixedIPs: gophercloud.IntToPointer(10),
FloatingIPs: gophercloud.IntToPointer(10),
InjectedFileContentBytes: gophercloud.IntToPointer(10240),
InjectedFilePathBytes: gophercloud.IntToPointer(255),
InjectedFiles: gophercloud.IntToPointer(5),
KeyPairs: gophercloud.IntToPointer(10),
MetadataItems: gophercloud.IntToPointer(128),
RAM: gophercloud.IntToPointer(20000),
SecurityGroupRules: gophercloud.IntToPointer(20),
SecurityGroups: gophercloud.IntToPointer(10),
Cores: gophercloud.IntToPointer(10),
Instances: gophercloud.IntToPointer(4),
ServerGroups: gophercloud.IntToPointer(2),
ServerGroupMembers: gophercloud.IntToPointer(3),
}
//What the Server hopefully returns as the new Quotas
var UpdatedQuotas = quotasets.QuotaSet{
FixedIPs: 10,
FloatingIPs: 10,
InjectedFileContentBytes: 10240,
InjectedFilePathBytes: 255,
InjectedFiles: 5,
KeyPairs: 10,
MetadataItems: 128,
RAM: 20000,
SecurityGroupRules: 20,
SecurityGroups: 10,
Cores: 10,
Instances: 4,
ServerGroups: 2,
ServerGroupMembers: 3,
}
func TestQuotasetUpdateDelete(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
idclient, err := clients.NewIdentityV2Client()
if err != nil {
t.Fatalf("Could not create IdentityClient to look up tenant id!")
}
tenantid, err := getTenantIDByName(t, idclient, os.Getenv("OS_TENANT_NAME"))
if err != nil {
t.Fatalf("Id for Tenant named '%' not found. Please set OS_TENANT_NAME appropriately", os.Getenv("OS_TENANT_NAME"))
}
//save original quotas
orig, err := quotasets.Get(client, tenantid).Extract()
th.AssertNoErr(t, err)
//Test Update
res, err := quotasets.Update(client, tenantid, UpdatQuotaOpts).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, UpdatedQuotas, *res)
//Test Delete
_, err = quotasets.Delete(client, tenantid).Extract()
th.AssertNoErr(t, err)
//We dont know the default quotas, so just check if the quotas are not the same as before
newres, err := quotasets.Get(client, tenantid).Extract()
if newres == res {
t.Fatalf("Quotas after delete equal quotas before delete!")
}
restore := quotasets.UpdateOpts{}
FillUpdateOptsFromQuotaSet(*orig, &restore)
//restore original quotas
res, err = quotasets.Update(client, tenantid, restore).Extract()
th.AssertNoErr(t, err)
orig.ID = ""
th.AssertEquals(t, *orig, *res)
}
// Makes sure that the FillUpdateOptsFromQuotaSet() helper function works properly
func TestFillFromQuotaSetHelperFunction(t *testing.T) {
op := &quotasets.UpdateOpts{}
expected := `
{
"fixed_ips": 10,
"floating_ips": 10,
"injected_file_content_bytes": 10240,
"injected_file_path_bytes": 255,
"injected_files": 5,
"key_pairs": 10,
"metadata_items": 128,
"ram": 20000,
"security_group_rules": 20,
"security_groups": 10,
"cores": 10,
"instances": 4,
"server_groups": 2,
"server_group_members": 3
}`
FillUpdateOptsFromQuotaSet(UpdatedQuotas, op)
th.AssertJSONEquals(t, expected, op)
}

View File

@@ -0,0 +1,137 @@
// +build acceptance compute secgroups
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups"
)
func TestSecGroupsList(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
allPages, err := secgroups.List(client).AllPages()
if err != nil {
t.Fatalf("Unable to retrieve security groups: %v", err)
}
allSecGroups, err := secgroups.ExtractSecurityGroups(allPages)
if err != nil {
t.Fatalf("Unable to extract security groups: %v", err)
}
for _, secgroup := range allSecGroups {
tools.PrintResource(t, secgroup)
}
}
func TestSecGroupsCreate(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
securityGroup, err := CreateSecurityGroup(t, client)
if err != nil {
t.Fatalf("Unable to create security group: %v", err)
}
defer DeleteSecurityGroup(t, client, securityGroup)
}
func TestSecGroupsUpdate(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
securityGroup, err := CreateSecurityGroup(t, client)
if err != nil {
t.Fatalf("Unable to create security group: %v", err)
}
defer DeleteSecurityGroup(t, client, securityGroup)
updateOpts := secgroups.UpdateOpts{
Name: tools.RandomString("secgroup_", 4),
Description: tools.RandomString("dec_", 10),
}
updatedSecurityGroup, err := secgroups.Update(client, securityGroup.ID, updateOpts).Extract()
if err != nil {
t.Fatalf("Unable to update security group: %v", err)
}
t.Logf("Updated %s's name to %s", updatedSecurityGroup.ID, updatedSecurityGroup.Name)
}
func TestSecGroupsRuleCreate(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
securityGroup, err := CreateSecurityGroup(t, client)
if err != nil {
t.Fatalf("Unable to create security group: %v", err)
}
defer DeleteSecurityGroup(t, client, securityGroup)
rule, err := CreateSecurityGroupRule(t, client, securityGroup.ID)
if err != nil {
t.Fatalf("Unable to create rule: %v", err)
}
defer DeleteSecurityGroupRule(t, client, rule)
newSecurityGroup, err := secgroups.Get(client, securityGroup.ID).Extract()
if err != nil {
t.Fatalf("Unable to obtain security group: %v", err)
}
tools.PrintResource(t, newSecurityGroup)
}
func TestSecGroupsAddGroupToServer(t *testing.T) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
server, err := CreateServer(t, client)
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
defer DeleteServer(t, client, server)
securityGroup, err := CreateSecurityGroup(t, client)
if err != nil {
t.Fatalf("Unable to create security group: %v", err)
}
defer DeleteSecurityGroup(t, client, securityGroup)
rule, err := CreateSecurityGroupRule(t, client, securityGroup.ID)
if err != nil {
t.Fatalf("Unable to create rule: %v", err)
}
defer DeleteSecurityGroupRule(t, client, rule)
t.Logf("Adding group %s to server %s", securityGroup.ID, server.ID)
err = secgroups.AddServer(client, server.ID, securityGroup.Name).ExtractErr()
if err != nil && err.Error() != "EOF" {
t.Fatalf("Unable to add group %s to server %s: %s", securityGroup.ID, server.ID, err)
}
t.Logf("Removing group %s from server %s", securityGroup.ID, server.ID)
err = secgroups.RemoveServer(client, server.ID, securityGroup.Name).ExtractErr()
if err != nil && err.Error() != "EOF" {
t.Fatalf("Unable to remove group %s from server %s: %s", securityGroup.ID, server.ID, err)
}
}

View File

@@ -0,0 +1,93 @@
// +build acceptance compute servergroups
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
)
func TestServergroupsList(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
allPages, err := servergroups.List(client).AllPages()
if err != nil {
t.Fatalf("Unable to list server groups: %v", err)
}
allServerGroups, err := servergroups.ExtractServerGroups(allPages)
if err != nil {
t.Fatalf("Unable to extract server groups: %v", err)
}
for _, serverGroup := range allServerGroups {
tools.PrintResource(t, serverGroup)
}
}
func TestServergroupsCreate(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
serverGroup, err := CreateServerGroup(t, client, "anti-affinity")
if err != nil {
t.Fatalf("Unable to create server group: %v", err)
}
defer DeleteServerGroup(t, client, serverGroup)
serverGroup, err = servergroups.Get(client, serverGroup.ID).Extract()
if err != nil {
t.Fatalf("Unable to get server group: %v", err)
}
tools.PrintResource(t, serverGroup)
}
func TestServergroupsAffinityPolicy(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
serverGroup, err := CreateServerGroup(t, client, "affinity")
if err != nil {
t.Fatalf("Unable to create server group: %v", err)
}
defer DeleteServerGroup(t, client, serverGroup)
firstServer, err := CreateServerInServerGroup(t, client, serverGroup)
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
if err = WaitForComputeStatus(client, firstServer, "ACTIVE"); err != nil {
t.Fatalf("Unable to wait for server: %v", err)
}
defer DeleteServer(t, client, firstServer)
firstServer, err = servers.Get(client, firstServer.ID).Extract()
secondServer, err := CreateServerInServerGroup(t, client, serverGroup)
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
if err = WaitForComputeStatus(client, secondServer, "ACTIVE"); err != nil {
t.Fatalf("Unable to wait for server: %v", err)
}
defer DeleteServer(t, client, secondServer)
secondServer, err = servers.Get(client, secondServer.ID).Extract()
if firstServer.HostID != secondServer.HostID {
t.Fatalf("%s and %s were not scheduled on the same host.", firstServer.ID, secondServer.ID)
}
}

View File

@@ -0,0 +1,518 @@
// +build acceptance compute servers
package v2
import (
"strings"
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/attachinterfaces"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/lockunlock"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/pauseunpause"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/suspendresume"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
th "github.com/gophercloud/gophercloud/testhelper"
)
func TestServersList(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
allPages, err := servers.List(client, servers.ListOpts{}).AllPages()
if err != nil {
t.Fatalf("Unable to retrieve servers: %v", err)
}
allServers, err := servers.ExtractServers(allPages)
if err != nil {
t.Fatalf("Unable to extract servers: %v", err)
}
for _, server := range allServers {
tools.PrintResource(t, server)
}
}
func TestServersCreateDestroy(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
server, err := CreateServer(t, client)
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
defer DeleteServer(t, client, server)
newServer, err := servers.Get(client, server.ID).Extract()
if err != nil {
t.Errorf("Unable to retrieve server: %v", err)
}
tools.PrintResource(t, newServer)
allAddressPages, err := servers.ListAddresses(client, server.ID).AllPages()
if err != nil {
t.Errorf("Unable to list server addresses: %v", err)
}
allAddresses, err := servers.ExtractAddresses(allAddressPages)
if err != nil {
t.Errorf("Unable to extract server addresses: %v", err)
}
for network, address := range allAddresses {
t.Logf("Addresses on %s: %+v", network, address)
}
allInterfacePages, err := attachinterfaces.List(client, server.ID).AllPages()
if err != nil {
t.Errorf("Unable to list server Interfaces: %v", err)
}
allInterfaces, err := attachinterfaces.ExtractInterfaces(allInterfacePages)
if err != nil {
t.Errorf("Unable to extract server Interfaces: %v", err)
}
for _, Interface := range allInterfaces {
t.Logf("Interfaces: %+v", Interface)
}
allNetworkAddressPages, err := servers.ListAddressesByNetwork(client, server.ID, choices.NetworkName).AllPages()
if err != nil {
t.Errorf("Unable to list server addresses: %v", err)
}
allNetworkAddresses, err := servers.ExtractNetworkAddresses(allNetworkAddressPages)
if err != nil {
t.Errorf("Unable to extract server addresses: %v", err)
}
t.Logf("Addresses on %s:", choices.NetworkName)
for _, address := range allNetworkAddresses {
t.Logf("%+v", address)
}
}
func TestServersCreateDestroyWithExtensions(t *testing.T) {
var extendedServer struct {
servers.Server
availabilityzones.ServerAvailabilityZoneExt
}
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
server, err := CreateServer(t, client)
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
defer DeleteServer(t, client, server)
err = servers.Get(client, server.ID).ExtractInto(&extendedServer)
if err != nil {
t.Errorf("Unable to retrieve server: %v", err)
}
tools.PrintResource(t, extendedServer)
t.Logf("Availability Zone: %s\n", extendedServer.AvailabilityZone)
}
func TestServersWithoutImageRef(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
server, err := CreateServerWithoutImageRef(t, client)
if err != nil {
if err400, ok := err.(*gophercloud.ErrUnexpectedResponseCode); ok {
if !strings.Contains("Missing imageRef attribute", string(err400.Body)) {
defer DeleteServer(t, client, server)
}
}
}
}
func TestServersUpdate(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
server, err := CreateServer(t, client)
if err != nil {
t.Fatal(err)
}
defer DeleteServer(t, client, server)
alternateName := tools.RandomString("ACPTTEST", 16)
for alternateName == server.Name {
alternateName = tools.RandomString("ACPTTEST", 16)
}
t.Logf("Attempting to rename the server to %s.", alternateName)
updateOpts := servers.UpdateOpts{
Name: alternateName,
}
updated, err := servers.Update(client, server.ID, updateOpts).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 TestServersMetadata(t *testing.T) {
t.Parallel()
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
server, err := CreateServer(t, client)
if err != nil {
t.Fatal(err)
}
defer DeleteServer(t, client, server)
metadata, err := servers.UpdateMetadata(client, server.ID, servers.MetadataOpts{
"foo": "bar",
"this": "that",
}).Extract()
if err != nil {
t.Fatalf("Unable to update metadata: %v", err)
}
t.Logf("UpdateMetadata result: %+v\n", metadata)
err = servers.DeleteMetadatum(client, server.ID, "foo").ExtractErr()
if err != nil {
t.Fatalf("Unable to delete metadatum: %v", err)
}
metadata, err = servers.CreateMetadatum(client, server.ID, servers.MetadatumOpts{
"foo": "baz",
}).Extract()
if err != nil {
t.Fatalf("Unable to create metadatum: %v", err)
}
t.Logf("CreateMetadatum result: %+v\n", metadata)
metadata, err = servers.Metadatum(client, server.ID, "foo").Extract()
if err != nil {
t.Fatalf("Unable to get metadatum: %v", err)
}
t.Logf("Metadatum result: %+v\n", metadata)
th.AssertEquals(t, "baz", metadata["foo"])
metadata, err = servers.Metadata(client, server.ID).Extract()
if err != nil {
t.Fatalf("Unable to get metadata: %v", err)
}
t.Logf("Metadata result: %+v\n", metadata)
metadata, err = servers.ResetMetadata(client, server.ID, servers.MetadataOpts{}).Extract()
if err != nil {
t.Fatalf("Unable to reset metadata: %v", err)
}
t.Logf("ResetMetadata result: %+v\n", metadata)
th.AssertDeepEquals(t, map[string]string{}, metadata)
}
func TestServersActionChangeAdminPassword(t *testing.T) {
t.Parallel()
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
server, err := CreateServer(t, client)
if err != nil {
t.Fatal(err)
}
defer DeleteServer(t, client, server)
randomPassword := tools.MakeNewPassword(server.AdminPass)
res := servers.ChangeAdminPassword(client, server.ID, randomPassword)
if res.Err != nil {
t.Fatal(res.Err)
}
if err = WaitForComputeStatus(client, server, "PASSWORD"); err != nil {
t.Fatal(err)
}
if err = WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
t.Fatal(err)
}
}
func TestServersActionReboot(t *testing.T) {
t.Parallel()
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
server, err := CreateServer(t, client)
if err != nil {
t.Fatal(err)
}
defer DeleteServer(t, client, server)
rebootOpts := &servers.RebootOpts{
Type: servers.SoftReboot,
}
t.Logf("Attempting reboot of server %s", server.ID)
res := servers.Reboot(client, server.ID, rebootOpts)
if res.Err != nil {
t.Fatalf("Unable to reboot server: %v", res.Err)
}
if err = WaitForComputeStatus(client, server, "REBOOT"); err != nil {
t.Fatal(err)
}
if err = WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
t.Fatal(err)
}
}
func TestServersActionRebuild(t *testing.T) {
t.Parallel()
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
server, err := CreateServer(t, client)
if err != nil {
t.Fatal(err)
}
defer DeleteServer(t, client, server)
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 = WaitForComputeStatus(client, rebuilt, "REBUILD"); err != nil {
t.Fatal(err)
}
if err = WaitForComputeStatus(client, rebuilt, "ACTIVE"); err != nil {
t.Fatal(err)
}
}
func TestServersActionResizeConfirm(t *testing.T) {
t.Parallel()
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
server, err := CreateServer(t, client)
if err != nil {
t.Fatal(err)
}
defer DeleteServer(t, client, server)
t.Logf("Attempting to resize server %s", server.ID)
ResizeServer(t, client, server)
t.Logf("Attempting to confirm resize for server %s", server.ID)
if res := servers.ConfirmResize(client, server.ID); res.Err != nil {
t.Fatal(res.Err)
}
if err = WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
t.Fatal(err)
}
}
func TestServersActionResizeRevert(t *testing.T) {
t.Parallel()
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
server, err := CreateServer(t, client)
if err != nil {
t.Fatal(err)
}
defer DeleteServer(t, client, server)
t.Logf("Attempting to resize server %s", server.ID)
ResizeServer(t, client, server)
t.Logf("Attempting to revert resize for server %s", server.ID)
if res := servers.RevertResize(client, server.ID); res.Err != nil {
t.Fatal(res.Err)
}
if err = WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
t.Fatal(err)
}
}
func TestServersActionPause(t *testing.T) {
t.Parallel()
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
server, err := CreateServer(t, client)
if err != nil {
t.Fatal(err)
}
defer DeleteServer(t, client, server)
t.Logf("Attempting to pause server %s", server.ID)
err = pauseunpause.Pause(client, server.ID).ExtractErr()
if err != nil {
t.Fatal(err)
}
err = WaitForComputeStatus(client, server, "PAUSED")
if err != nil {
t.Fatal(err)
}
err = pauseunpause.Unpause(client, server.ID).ExtractErr()
if err != nil {
t.Fatal(err)
}
err = WaitForComputeStatus(client, server, "ACTIVE")
if err != nil {
t.Fatal(err)
}
}
func TestServersActionSuspend(t *testing.T) {
t.Parallel()
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
server, err := CreateServer(t, client)
if err != nil {
t.Fatal(err)
}
defer DeleteServer(t, client, server)
t.Logf("Attempting to suspend server %s", server.ID)
err = suspendresume.Suspend(client, server.ID).ExtractErr()
if err != nil {
t.Fatal(err)
}
err = WaitForComputeStatus(client, server, "SUSPENDED")
if err != nil {
t.Fatal(err)
}
err = suspendresume.Resume(client, server.ID).ExtractErr()
if err != nil {
t.Fatal(err)
}
err = WaitForComputeStatus(client, server, "ACTIVE")
if err != nil {
t.Fatal(err)
}
}
func TestServersActionLock(t *testing.T) {
t.Parallel()
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
server, err := CreateServer(t, client)
if err != nil {
t.Fatal(err)
}
defer DeleteServer(t, client, server)
t.Logf("Attempting to Lock server %s", server.ID)
err = lockunlock.Lock(client, server.ID).ExtractErr()
if err != nil {
t.Fatal(err)
}
err = servers.Delete(client, server.ID).ExtractErr()
if err == nil {
t.Fatalf("Should not have been able to delete the server")
}
err = lockunlock.Unlock(client, server.ID).ExtractErr()
if err != nil {
t.Fatal(err)
}
err = WaitForComputeStatus(client, server, "ACTIVE")
if err != nil {
t.Fatal(err)
}
}

View File

@@ -0,0 +1,56 @@
// +build acceptance compute servers
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tenantnetworks"
)
func TestTenantNetworksList(t *testing.T) {
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
allPages, err := tenantnetworks.List(client).AllPages()
if err != nil {
t.Fatalf("Unable to list networks: %v", err)
}
allTenantNetworks, err := tenantnetworks.ExtractNetworks(allPages)
if err != nil {
t.Fatalf("Unable to list networks: %v", err)
}
for _, network := range allTenantNetworks {
tools.PrintResource(t, network)
}
}
func TestTenantNetworksGet(t *testing.T) {
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName)
if err != nil {
t.Fatal(err)
}
network, err := tenantnetworks.Get(client, networkID).Extract()
if err != nil {
t.Fatalf("Unable to get network %s: %v", networkID, err)
}
tools.PrintResource(t, network)
}

View File

@@ -0,0 +1,78 @@
// +build acceptance compute volumeattach
package v2
import (
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes"
)
func TestVolumeAttachAttachment(t *testing.T) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
client, err := clients.NewComputeV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
blockClient, err := clients.NewBlockStorageV1Client()
if err != nil {
t.Fatalf("Unable to create a blockstorage client: %v", err)
}
server, err := CreateServer(t, client)
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
defer DeleteServer(t, client, server)
volume, err := createVolume(t, blockClient)
if err != nil {
t.Fatalf("Unable to create volume: %v", err)
}
if err = volumes.WaitForStatus(blockClient, volume.ID, "available", 60); err != nil {
t.Fatalf("Unable to wait for volume: %v", err)
}
defer deleteVolume(t, blockClient, volume)
volumeAttachment, err := CreateVolumeAttachment(t, client, blockClient, server, volume)
if err != nil {
t.Fatalf("Unable to attach volume: %v", err)
}
defer DeleteVolumeAttachment(t, client, blockClient, server, volumeAttachment)
tools.PrintResource(t, volumeAttachment)
}
func createVolume(t *testing.T, blockClient *gophercloud.ServiceClient) (*volumes.Volume, error) {
volumeName := tools.RandomString("ACPTTEST", 16)
createOpts := volumes.CreateOpts{
Size: 1,
Name: volumeName,
}
volume, err := volumes.Create(blockClient, createOpts).Extract()
if err != nil {
return volume, err
}
t.Logf("Created volume: %s", volume.ID)
return volume, nil
}
func deleteVolume(t *testing.T, blockClient *gophercloud.ServiceClient, volume *volumes.Volume) {
err := volumes.Delete(blockClient, volume.ID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete volume: %v", err)
}
t.Logf("Deleted volume: %s", volume.ID)
}

View File

@@ -0,0 +1,55 @@
// +build acceptance db
package v1
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/db/v1/databases"
)
// Because it takes so long to create an instance,
// all tests will be housed in a single function.
func TestDatabases(t *testing.T) {
if testing.Short() {
t.Skip("Skipping in short mode")
}
client, err := clients.NewDBV1Client()
if err != nil {
t.Fatalf("Unable to create a DB client: %v", err)
}
// Create and Get an instance.
instance, err := CreateInstance(t, client)
if err != nil {
t.Fatalf("Unable to create instance: %v", err)
}
defer DeleteInstance(t, client, instance.ID)
// Create a database.
err = CreateDatabase(t, client, instance.ID)
if err != nil {
t.Fatalf("Unable to create database: %v", err)
}
// List all databases.
allPages, err := databases.List(client, instance.ID).AllPages()
if err != nil {
t.Fatalf("Unable to list databases: %v", err)
}
allDatabases, err := databases.ExtractDBs(allPages)
if err != nil {
t.Fatalf("Unable to extract databases: %v", err)
}
for _, db := range allDatabases {
tools.PrintResource(t, db)
}
defer DeleteDatabase(t, client, instance.ID, allDatabases[0].Name)
}

View File

@@ -0,0 +1,145 @@
// Package v2 contains common functions for creating db resources for use
// in acceptance tests. See the `*_test.go` files for example usages.
package v1
import (
"fmt"
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/db/v1/databases"
"github.com/gophercloud/gophercloud/openstack/db/v1/instances"
"github.com/gophercloud/gophercloud/openstack/db/v1/users"
)
// CreateDatabase will create a database with a randomly generated name.
// An error will be returned if the database was unable to be created.
func CreateDatabase(t *testing.T, client *gophercloud.ServiceClient, instanceID string) error {
name := tools.RandomString("ACPTTEST", 8)
t.Logf("Attempting to create database: %s", name)
createOpts := databases.BatchCreateOpts{
databases.CreateOpts{
Name: name,
},
}
return databases.Create(client, instanceID, createOpts).ExtractErr()
}
// CreateInstance will create an instance with a randomly generated name.
// The flavor of the instance will be the value of the OS_FLAVOR_ID
// environment variable. The Datastore will be pulled from the
// OS_DATASTORE_TYPE_ID environment variable.
// An error will be returned if the instance was unable to be created.
func CreateInstance(t *testing.T, client *gophercloud.ServiceClient) (*instances.Instance, error) {
if testing.Short() {
t.Skip("Skipping test that requires instance creation in short mode.")
}
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
return nil, err
}
name := tools.RandomString("ACPTTEST", 8)
t.Logf("Attempting to create instance: %s", name)
createOpts := instances.CreateOpts{
FlavorRef: choices.FlavorID,
Size: 1,
Name: name,
Datastore: &instances.DatastoreOpts{
Type: choices.DBDatastoreType,
Version: choices.DBDatastoreVersion,
},
}
instance, err := instances.Create(client, createOpts).Extract()
if err != nil {
return instance, err
}
if err := WaitForInstanceStatus(client, instance, "ACTIVE"); err != nil {
return instance, err
}
return instances.Get(client, instance.ID).Extract()
}
// CreateUser will create a user with a randomly generated name.
// An error will be returned if the user was unable to be created.
func CreateUser(t *testing.T, client *gophercloud.ServiceClient, instanceID string) error {
name := tools.RandomString("ACPTTEST", 8)
password := tools.RandomString("", 8)
t.Logf("Attempting to create user: %s", name)
createOpts := users.BatchCreateOpts{
users.CreateOpts{
Name: name,
Password: password,
},
}
return users.Create(client, instanceID, createOpts).ExtractErr()
}
// DeleteDatabase deletes a database. A fatal error will occur if the database
// failed to delete. This works best when used as a deferred function.
func DeleteDatabase(t *testing.T, client *gophercloud.ServiceClient, instanceID, name string) {
t.Logf("Attempting to delete database: %s", name)
err := databases.Delete(client, instanceID, name).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete database %s: %s", name, err)
}
t.Logf("Deleted database: %s", name)
}
// DeleteInstance deletes an instance. A fatal error will occur if the instance
// failed to delete. This works best when used as a deferred function.
func DeleteInstance(t *testing.T, client *gophercloud.ServiceClient, id string) {
t.Logf("Attempting to delete instance: %s", id)
err := instances.Delete(client, id).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete instance %s: %s", id, err)
}
t.Logf("Deleted instance: %s", id)
}
// DeleteUser deletes a user. A fatal error will occur if the user
// failed to delete. This works best when used as a deferred function.
func DeleteUser(t *testing.T, client *gophercloud.ServiceClient, instanceID, name string) {
t.Logf("Attempting to delete user: %s", name)
err := users.Delete(client, instanceID, name).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete users %s: %s", name, err)
}
t.Logf("Deleted users: %s", name)
}
// WaitForInstanceState will poll an instance's status until it either matches
// the specified status or the status becomes ERROR.
func WaitForInstanceStatus(
client *gophercloud.ServiceClient, instance *instances.Instance, status string) error {
return tools.WaitFor(func() (bool, error) {
latest, err := instances.Get(client, instance.ID).Extract()
if err != nil {
return false, err
}
if latest.Status == status {
return true, nil
}
if latest.Status == "ERROR" {
return false, fmt.Errorf("Instance in ERROR state")
}
return false, nil
})
}

View File

@@ -0,0 +1,58 @@
// +build acceptance db
package v1
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/db/v1/flavors"
)
func TestFlavorsList(t *testing.T) {
client, err := clients.NewDBV1Client()
if err != nil {
t.Fatalf("Unable to create a DB client: %v", err)
}
allPages, err := flavors.List(client).AllPages()
if err != nil {
t.Fatalf("Unable to retrieve flavors: %v", err)
}
allFlavors, err := flavors.ExtractFlavors(allPages)
if err != nil {
t.Fatalf("Unable to extract flavors: %v", err)
}
for _, flavor := range allFlavors {
tools.PrintResource(t, &flavor)
}
}
func TestFlavorsGet(t *testing.T) {
client, err := clients.NewDBV1Client()
if err != nil {
t.Fatalf("Unable to create a DB client: %v", err)
}
allPages, err := flavors.List(client).AllPages()
if err != nil {
t.Fatalf("Unable to retrieve flavors: %v", err)
}
allFlavors, err := flavors.ExtractFlavors(allPages)
if err != nil {
t.Fatalf("Unable to extract flavors: %v", err)
}
if len(allFlavors) > 0 {
flavor, err := flavors.Get(client, allFlavors[0].StrID).Extract()
if err != nil {
t.Fatalf("Unable to get flavor: %v", err)
}
tools.PrintResource(t, flavor)
}
}

View File

@@ -0,0 +1,71 @@
// +build acceptance db
package v1
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/db/v1/instances"
)
// Because it takes so long to create an instance,
// all tests will be housed in a single function.
func TestInstances(t *testing.T) {
if testing.Short() {
t.Skip("Skipping in short mode")
}
client, err := clients.NewDBV1Client()
if err != nil {
t.Fatalf("Unable to create a DB client: %v", err)
}
// Create and Get an instance.
instance, err := CreateInstance(t, client)
if err != nil {
t.Fatalf("Unable to create instance: %v", err)
}
defer DeleteInstance(t, client, instance.ID)
tools.PrintResource(t, &instance)
// List all instances.
allPages, err := instances.List(client).AllPages()
if err != nil {
t.Fatalf("Unable to list instances: %v", err)
}
allInstances, err := instances.ExtractInstances(allPages)
if err != nil {
t.Fatalf("Unable to extract instances: %v", err)
}
for _, instance := range allInstances {
tools.PrintResource(t, instance)
}
// Enable root user.
_, err = instances.EnableRootUser(client, instance.ID).Extract()
if err != nil {
t.Fatalf("Unable to enable root user: %v", err)
}
enabled, err := instances.IsRootEnabled(client, instance.ID).Extract()
if err != nil {
t.Fatalf("Unable to check if root user is enabled: %v", err)
}
t.Logf("Root user is enabled: %t", enabled)
// Restart
err = instances.Restart(client, instance.ID).ExtractErr()
if err != nil {
t.Fatalf("Unable to restart instance: %v", err)
}
err = WaitForInstanceStatus(client, instance, "ACTIVE")
if err != nil {
t.Fatalf("Unable to restart instance: %v", err)
}
}

View File

@@ -0,0 +1 @@
package v1

View File

@@ -0,0 +1,54 @@
// +build acceptance db
package v1
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/db/v1/users"
)
// Because it takes so long to create an instance,
// all tests will be housed in a single function.
func TestUsers(t *testing.T) {
if testing.Short() {
t.Skip("Skipping in short mode")
}
client, err := clients.NewDBV1Client()
if err != nil {
t.Fatalf("Unable to create a DB client: %v", err)
}
// Create and Get an instance.
instance, err := CreateInstance(t, client)
if err != nil {
t.Fatalf("Unable to create instance: %v", err)
}
defer DeleteInstance(t, client, instance.ID)
// Create a user.
err = CreateUser(t, client, instance.ID)
if err != nil {
t.Fatalf("Unable to create user: %v", err)
}
// List all users.
allPages, err := users.List(client, instance.ID).AllPages()
if err != nil {
t.Fatalf("Unable to list users: %v", err)
}
allUsers, err := users.ExtractUsers(allPages)
if err != nil {
t.Fatalf("Unable to extract users: %v", err)
}
for _, user := range allUsers {
tools.PrintResource(t, user)
}
defer DeleteUser(t, client, instance.ID, allUsers[0].Name)
}

View File

@@ -0,0 +1,164 @@
package v2
import (
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets"
"github.com/gophercloud/gophercloud/openstack/dns/v2/zones"
)
// CreateRecordSet will create a RecordSet with a random name. An error will
// be returned if the zone was unable to be created.
func CreateRecordSet(t *testing.T, client *gophercloud.ServiceClient, zone *zones.Zone) (*recordsets.RecordSet, error) {
t.Logf("Attempting to create recordset: %s", zone.Name)
createOpts := recordsets.CreateOpts{
Name: zone.Name,
Type: "A",
TTL: 3600,
Description: "Test recordset",
Records: []string{"10.1.0.2"},
}
rs, err := recordsets.Create(client, zone.ID, createOpts).Extract()
if err != nil {
return rs, err
}
if err := WaitForRecordSetStatus(client, rs, "ACTIVE"); err != nil {
return rs, err
}
newRS, err := recordsets.Get(client, rs.ZoneID, rs.ID).Extract()
if err != nil {
return newRS, err
}
t.Logf("Created record set: %s", newRS.Name)
return rs, nil
}
// CreateZone will create a Zone with a random name. An error will
// be returned if the zone was unable to be created.
func CreateZone(t *testing.T, client *gophercloud.ServiceClient) (*zones.Zone, error) {
zoneName := tools.RandomString("ACPTTEST", 8) + ".com."
t.Logf("Attempting to create zone: %s", zoneName)
createOpts := zones.CreateOpts{
Name: zoneName,
Email: "root@example.com",
Type: "PRIMARY",
TTL: 7200,
Description: "Test zone",
}
zone, err := zones.Create(client, createOpts).Extract()
if err != nil {
return zone, err
}
if err := WaitForZoneStatus(client, zone, "ACTIVE"); err != nil {
return zone, err
}
newZone, err := zones.Get(client, zone.ID).Extract()
if err != nil {
return zone, err
}
t.Logf("Created Zone: %s", zoneName)
return newZone, nil
}
// CreateSecondaryZone will create a Zone with a random name. An error will
// be returned if the zone was unable to be created.
//
// This is only for example purposes as it will try to do a zone transfer.
func CreateSecondaryZone(t *testing.T, client *gophercloud.ServiceClient) (*zones.Zone, error) {
zoneName := tools.RandomString("ACPTTEST", 8) + ".com."
t.Logf("Attempting to create zone: %s", zoneName)
createOpts := zones.CreateOpts{
Name: zoneName,
Type: "SECONDARY",
Masters: []string{"10.0.0.1"},
}
zone, err := zones.Create(client, createOpts).Extract()
if err != nil {
return zone, err
}
if err := WaitForZoneStatus(client, zone, "ACTIVE"); err != nil {
return zone, err
}
newZone, err := zones.Get(client, zone.ID).Extract()
if err != nil {
return zone, err
}
t.Logf("Created Zone: %s", zoneName)
return newZone, nil
}
// DeleteRecordSet will delete a specified record set. A fatal error will occur if
// the record set failed to be deleted. This works best when used as a deferred
// function.
func DeleteRecordSet(t *testing.T, client *gophercloud.ServiceClient, rs *recordsets.RecordSet) {
err := recordsets.Delete(client, rs.ZoneID, rs.ID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete record set %s: %v", rs.ID, err)
}
t.Logf("Deleted record set: %s", rs.ID)
}
// DeleteZone will delete a specified zone. A fatal error will occur if
// the zone failed to be deleted. This works best when used as a deferred
// function.
func DeleteZone(t *testing.T, client *gophercloud.ServiceClient, zone *zones.Zone) {
_, err := zones.Delete(client, zone.ID).Extract()
if err != nil {
t.Fatalf("Unable to delete zone %s: %v", zone.ID, err)
}
t.Logf("Deleted zone: %s", zone.ID)
}
// WaitForRecordSetStatus will poll a record set's status until it either matches
// the specified status or the status becomes ERROR.
func WaitForRecordSetStatus(client *gophercloud.ServiceClient, rs *recordsets.RecordSet, status string) error {
return gophercloud.WaitFor(60, func() (bool, error) {
current, err := recordsets.Get(client, rs.ZoneID, rs.ID).Extract()
if err != nil {
return false, err
}
if current.Status == status {
return true, nil
}
return false, nil
})
}
// WaitForZoneStatus will poll a zone's status until it either matches
// the specified status or the status becomes ERROR.
func WaitForZoneStatus(client *gophercloud.ServiceClient, zone *zones.Zone, status string) error {
return gophercloud.WaitFor(60, func() (bool, error) {
current, err := zones.Get(client, zone.ID).Extract()
if err != nil {
return false, err
}
if current.Status == status {
return true, nil
}
return false, nil
})
}

View File

@@ -0,0 +1,105 @@
// +build acceptance dns recordsets
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets"
)
func TestRecordSetsListByZone(t *testing.T) {
client, err := clients.NewDNSV2Client()
if err != nil {
t.Fatalf("Unable to create a DNS client: %v", err)
}
zone, err := CreateZone(t, client)
if err != nil {
t.Fatal(err)
}
defer DeleteZone(t, client, zone)
var allRecordSets []recordsets.RecordSet
allPages, err := recordsets.ListByZone(client, zone.ID, nil).AllPages()
if err != nil {
t.Fatalf("Unable to retrieve recordsets: %v", err)
}
allRecordSets, err = recordsets.ExtractRecordSets(allPages)
if err != nil {
t.Fatalf("Unable to extract recordsets: %v", err)
}
for _, recordset := range allRecordSets {
tools.PrintResource(t, &recordset)
}
}
func TestRecordSetsListByZoneLimited(t *testing.T) {
client, err := clients.NewDNSV2Client()
if err != nil {
t.Fatalf("Unable to create a DNS client: %v", err)
}
zone, err := CreateZone(t, client)
if err != nil {
t.Fatal(err)
}
defer DeleteZone(t, client, zone)
var allRecordSets []recordsets.RecordSet
listOpts := recordsets.ListOpts{
Limit: 1,
}
allPages, err := recordsets.ListByZone(client, zone.ID, listOpts).AllPages()
if err != nil {
t.Fatalf("Unable to retrieve recordsets: %v", err)
}
allRecordSets, err = recordsets.ExtractRecordSets(allPages)
if err != nil {
t.Fatalf("Unable to extract recordsets: %v", err)
}
for _, recordset := range allRecordSets {
tools.PrintResource(t, &recordset)
}
}
func TestRecordSetCRUD(t *testing.T) {
client, err := clients.NewDNSV2Client()
if err != nil {
t.Fatalf("Unable to create a DNS client: %v", err)
}
zone, err := CreateZone(t, client)
if err != nil {
t.Fatal(err)
}
defer DeleteZone(t, client, zone)
tools.PrintResource(t, &zone)
rs, err := CreateRecordSet(t, client, zone)
if err != nil {
t.Fatal(err)
}
defer DeleteRecordSet(t, client, rs)
tools.PrintResource(t, &rs)
updateOpts := recordsets.UpdateOpts{
Description: "New description",
TTL: 0,
}
newRS, err := recordsets.Update(client, rs.ZoneID, rs.ID, updateOpts).Extract()
if err != nil {
t.Fatal(err)
}
tools.PrintResource(t, &newRS)
}

View File

@@ -0,0 +1,60 @@
// +build acceptance dns zones
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/dns/v2/zones"
)
func TestZonesList(t *testing.T) {
client, err := clients.NewDNSV2Client()
if err != nil {
t.Fatalf("Unable to create a DNS client: %v", err)
}
var allZones []zones.Zone
allPages, err := zones.List(client, nil).AllPages()
if err != nil {
t.Fatalf("Unable to retrieve zones: %v", err)
}
allZones, err = zones.ExtractZones(allPages)
if err != nil {
t.Fatalf("Unable to extract zones: %v", err)
}
for _, zone := range allZones {
tools.PrintResource(t, &zone)
}
}
func TestZonesCRUD(t *testing.T) {
client, err := clients.NewDNSV2Client()
if err != nil {
t.Fatalf("Unable to create a DNS client: %v", err)
}
zone, err := CreateZone(t, client)
if err != nil {
t.Fatal(err)
}
defer DeleteZone(t, client, zone)
tools.PrintResource(t, &zone)
updateOpts := zones.UpdateOpts{
Description: "New description",
TTL: 0,
}
newZone, err := zones.Update(client, zone.ID, updateOpts).Extract()
if err != nil {
t.Fatal(err)
}
tools.PrintResource(t, &newZone)
}

View File

@@ -0,0 +1,46 @@
// +build acceptance identity
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/identity/v2/extensions"
)
func TestExtensionsList(t *testing.T) {
client, err := clients.NewIdentityV2Client()
if err != nil {
t.Fatalf("Unable to create an identity client: %v", err)
}
allPages, err := extensions.List(client).AllPages()
if err != nil {
t.Fatalf("Unable to list extensions: %v", err)
}
allExtensions, err := extensions.ExtractExtensions(allPages)
if err != nil {
t.Fatalf("Unable to extract extensions: %v", err)
}
for _, extension := range allExtensions {
tools.PrintResource(t, extension)
}
}
func TestExtensionsGet(t *testing.T) {
client, err := clients.NewIdentityV2Client()
if err != nil {
t.Fatalf("Unable to create an identity client: %v", err)
}
extension, err := extensions.Get(client, "OS-KSCRUD").Extract()
if err != nil {
t.Fatalf("Unable to get extension OS-KSCRUD: %v", err)
}
tools.PrintResource(t, extension)
}

View File

@@ -0,0 +1,186 @@
// Package v2 contains common functions for creating identity-based resources
// for use in acceptance tests. See the `*_test.go` files for example usages.
package v2
import (
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/identity/v2/extensions/admin/roles"
"github.com/gophercloud/gophercloud/openstack/identity/v2/tenants"
"github.com/gophercloud/gophercloud/openstack/identity/v2/users"
)
// AddUserRole will grant a role to a user in a tenant. An error will be
// returned if the grant was unsuccessful.
func AddUserRole(t *testing.T, client *gophercloud.ServiceClient, tenant *tenants.Tenant, user *users.User, role *roles.Role) error {
t.Logf("Attempting to grant user %s role %s in tenant %s", user.ID, role.ID, tenant.ID)
err := roles.AddUser(client, tenant.ID, user.ID, role.ID).ExtractErr()
if err != nil {
return err
}
t.Logf("Granted user %s role %s in tenant %s", user.ID, role.ID, tenant.ID)
return nil
}
// CreateTenant will create a project with a random name.
// It takes an optional createOpts parameter since creating a project
// has so many options. An error will be returned if the project was
// unable to be created.
func CreateTenant(t *testing.T, client *gophercloud.ServiceClient, c *tenants.CreateOpts) (*tenants.Tenant, error) {
name := tools.RandomString("ACPTTEST", 8)
t.Logf("Attempting to create tenant: %s", name)
var createOpts tenants.CreateOpts
if c != nil {
createOpts = *c
} else {
createOpts = tenants.CreateOpts{}
}
createOpts.Name = name
tenant, err := tenants.Create(client, createOpts).Extract()
if err != nil {
t.Logf("Foo")
return tenant, err
}
t.Logf("Successfully created project %s with ID %s", name, tenant.ID)
return tenant, nil
}
// CreateUser will create a user with a random name and adds them to the given
// tenant. An error will be returned if the user was unable to be created.
func CreateUser(t *testing.T, client *gophercloud.ServiceClient, tenant *tenants.Tenant) (*users.User, error) {
userName := tools.RandomString("user_", 5)
userEmail := userName + "@foo.com"
t.Logf("Creating user: %s", userName)
createOpts := users.CreateOpts{
Name: userName,
Enabled: gophercloud.Disabled,
TenantID: tenant.ID,
Email: userEmail,
}
user, err := users.Create(client, createOpts).Extract()
if err != nil {
return user, err
}
return user, nil
}
// DeleteTenant will delete a tenant by ID. A fatal error will occur if
// the tenant ID failed to be deleted. This works best when using it as
// a deferred function.
func DeleteTenant(t *testing.T, client *gophercloud.ServiceClient, tenantID string) {
err := tenants.Delete(client, tenantID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete tenant %s: %v", tenantID, err)
}
t.Logf("Deleted tenant: %s", tenantID)
}
// DeleteUser will delete a user. A fatal error will occur if the delete was
// unsuccessful. This works best when used as a deferred function.
func DeleteUser(t *testing.T, client *gophercloud.ServiceClient, user *users.User) {
t.Logf("Attempting to delete user: %s", user.Name)
result := users.Delete(client, user.ID)
if result.Err != nil {
t.Fatalf("Unable to delete user")
}
t.Logf("Deleted user: %s", user.Name)
}
// DeleteUserRole will revoke a role of a user in a tenant. A fatal error will
// occur if the revoke was unsuccessful. This works best when used as a
// deferred function.
func DeleteUserRole(t *testing.T, client *gophercloud.ServiceClient, tenant *tenants.Tenant, user *users.User, role *roles.Role) {
t.Logf("Attempting to remove role %s from user %s in tenant %s", role.ID, user.ID, tenant.ID)
err := roles.DeleteUser(client, tenant.ID, user.ID, role.ID).ExtractErr()
if err != nil {
t.Fatalf("Unable to remove role")
}
t.Logf("Removed role %s from user %s in tenant %s", role.ID, user.ID, tenant.ID)
}
// FindRole finds all roles that the current authenticated client has access
// to and returns the first one found. An error will be returned if the lookup
// was unsuccessful.
func FindRole(t *testing.T, client *gophercloud.ServiceClient) (*roles.Role, error) {
var role *roles.Role
allPages, err := roles.List(client).AllPages()
if err != nil {
return role, err
}
allRoles, err := roles.ExtractRoles(allPages)
if err != nil {
return role, err
}
for _, r := range allRoles {
role = &r
break
}
return role, nil
}
// FindTenant finds all tenants that the current authenticated client has access
// to and returns the first one found. An error will be returned if the lookup
// was unsuccessful.
func FindTenant(t *testing.T, client *gophercloud.ServiceClient) (*tenants.Tenant, error) {
var tenant *tenants.Tenant
allPages, err := tenants.List(client, nil).AllPages()
if err != nil {
return tenant, err
}
allTenants, err := tenants.ExtractTenants(allPages)
if err != nil {
return tenant, err
}
for _, t := range allTenants {
tenant = &t
break
}
return tenant, nil
}
// UpdateUser will update an existing user with a new randomly generated name.
// An error will be returned if the update was unsuccessful.
func UpdateUser(t *testing.T, client *gophercloud.ServiceClient, user *users.User) (*users.User, error) {
userName := tools.RandomString("user_", 5)
userEmail := userName + "@foo.com"
t.Logf("Attempting to update user name from %s to %s", user.Name, userName)
updateOpts := users.UpdateOpts{
Name: userName,
Email: userEmail,
}
newUser, err := users.Update(client, user.ID, updateOpts).Extract()
if err != nil {
return newUser, err
}
return newUser, nil
}

View File

@@ -0,0 +1 @@
package v2

View File

@@ -0,0 +1,77 @@
// +build acceptance identity roles
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/identity/v2/extensions/admin/roles"
"github.com/gophercloud/gophercloud/openstack/identity/v2/users"
)
func TestRolesAddToUser(t *testing.T) {
client, err := clients.NewIdentityV2AdminClient()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
tenant, err := FindTenant(t, client)
if err != nil {
t.Fatalf("Unable to get a tenant: %v", err)
}
role, err := FindRole(t, client)
if err != nil {
t.Fatalf("Unable to get a role: %v", err)
}
user, err := CreateUser(t, client, tenant)
if err != nil {
t.Fatalf("Unable to create a user: %v", err)
}
defer DeleteUser(t, client, user)
err = AddUserRole(t, client, tenant, user, role)
if err != nil {
t.Fatalf("Unable to add role to user: %v", err)
}
defer DeleteUserRole(t, client, tenant, user, role)
allPages, err := users.ListRoles(client, tenant.ID, user.ID).AllPages()
if err != nil {
t.Fatalf("Unable to obtain roles for user: %v", err)
}
allRoles, err := users.ExtractRoles(allPages)
if err != nil {
t.Fatalf("Unable to extract roles: %v", err)
}
t.Logf("Roles of user %s:", user.Name)
for _, role := range allRoles {
tools.PrintResource(t, role)
}
}
func TestRolesList(t *testing.T) {
client, err := clients.NewIdentityV2AdminClient()
if err != nil {
t.Fatalf("Unable to create an identity client: %v", err)
}
allPages, err := roles.List(client).AllPages()
if err != nil {
t.Fatalf("Unable to list all roles: %v", err)
}
allRoles, err := roles.ExtractRoles(allPages)
if err != nil {
t.Fatalf("Unable to extract roles: %v", err)
}
for _, r := range allRoles {
tools.PrintResource(t, r)
}
}

View File

@@ -0,0 +1,63 @@
// +build acceptance identity
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/identity/v2/tenants"
)
func TestTenantsList(t *testing.T) {
client, err := clients.NewIdentityV2Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v")
}
allPages, err := tenants.List(client, nil).AllPages()
if err != nil {
t.Fatalf("Unable to list tenants: %v", err)
}
allTenants, err := tenants.ExtractTenants(allPages)
if err != nil {
t.Fatalf("Unable to extract tenants: %v", err)
}
for _, tenant := range allTenants {
tools.PrintResource(t, tenant)
}
}
func TestTenantsCRUD(t *testing.T) {
client, err := clients.NewIdentityV2AdminClient()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v")
}
tenant, err := CreateTenant(t, client, nil)
if err != nil {
t.Fatalf("Unable to create tenant: %v", err)
}
defer DeleteTenant(t, client, tenant.ID)
tenant, err = tenants.Get(client, tenant.ID).Extract()
if err != nil {
t.Fatalf("Unable to get tenant: %v", err)
}
tools.PrintResource(t, tenant)
updateOpts := tenants.UpdateOpts{
Description: "some tenant",
}
newTenant, err := tenants.Update(client, tenant.ID, updateOpts).Extract()
if err != nil {
t.Fatalf("Unable to update tenant: %v", err)
}
tools.PrintResource(t, newTenant)
}

View File

@@ -0,0 +1,69 @@
// +build acceptance identity
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack"
"github.com/gophercloud/gophercloud/openstack/identity/v2/tokens"
)
func TestTokenAuthenticate(t *testing.T) {
client, err := clients.NewIdentityV2UnauthenticatedClient()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
authOptions, err := openstack.AuthOptionsFromEnv()
if err != nil {
t.Fatalf("Unable to obtain authentication options: %v", err)
}
result := tokens.Create(client, authOptions)
token, err := result.ExtractToken()
if err != nil {
t.Fatalf("Unable to extract token: %v", err)
}
tools.PrintResource(t, token)
catalog, err := result.ExtractServiceCatalog()
if err != nil {
t.Fatalf("Unable to extract service catalog: %v", err)
}
for _, entry := range catalog.Entries {
tools.PrintResource(t, entry)
}
}
func TestTokenValidate(t *testing.T) {
client, err := clients.NewIdentityV2Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
authOptions, err := openstack.AuthOptionsFromEnv()
if err != nil {
t.Fatalf("Unable to obtain authentication options: %v", err)
}
result := tokens.Create(client, authOptions)
token, err := result.ExtractToken()
if err != nil {
t.Fatalf("Unable to extract token: %v", err)
}
tools.PrintResource(t, token)
getResult := tokens.Get(client, token.ID)
user, err := getResult.ExtractUser()
if err != nil {
t.Fatalf("Unable to extract user: %v", err)
}
tools.PrintResource(t, user)
}

View File

@@ -0,0 +1,59 @@
// +build acceptance identity
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/identity/v2/users"
)
func TestUsersList(t *testing.T) {
client, err := clients.NewIdentityV2AdminClient()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
allPages, err := users.List(client).AllPages()
if err != nil {
t.Fatalf("Unable to list users: %v", err)
}
allUsers, err := users.ExtractUsers(allPages)
if err != nil {
t.Fatalf("Unable to extract users: %v", err)
}
for _, user := range allUsers {
tools.PrintResource(t, user)
}
}
func TestUsersCreateUpdateDelete(t *testing.T) {
client, err := clients.NewIdentityV2AdminClient()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
tenant, err := FindTenant(t, client)
if err != nil {
t.Fatalf("Unable to get a tenant: %v", err)
}
user, err := CreateUser(t, client, tenant)
if err != nil {
t.Fatalf("Unable to create a user: %v", err)
}
defer DeleteUser(t, client, user)
tools.PrintResource(t, user)
newUser, err := UpdateUser(t, client, user)
if err != nil {
t.Fatalf("Unable to update user: %v", err)
}
tools.PrintResource(t, newUser)
}

View File

@@ -0,0 +1,96 @@
// +build acceptance
package v3
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/identity/v3/domains"
)
func TestDomainsList(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
var iTrue bool = true
listOpts := domains.ListOpts{
Enabled: &iTrue,
}
allPages, err := domains.List(client, listOpts).AllPages()
if err != nil {
t.Fatalf("Unable to list domains: %v", err)
}
allDomains, err := domains.ExtractDomains(allPages)
if err != nil {
t.Fatalf("Unable to extract domains: %v", err)
}
for _, domain := range allDomains {
tools.PrintResource(t, domain)
}
}
func TestDomainsGet(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
allPages, err := domains.List(client, nil).AllPages()
if err != nil {
t.Fatalf("Unable to list domains: %v", err)
}
allDomains, err := domains.ExtractDomains(allPages)
if err != nil {
t.Fatalf("Unable to extract domains: %v", err)
}
domain := allDomains[0]
p, err := domains.Get(client, domain.ID).Extract()
if err != nil {
t.Fatalf("Unable to get domain: %v", err)
}
tools.PrintResource(t, p)
}
func TestDomainsCRUD(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
var iTrue bool = true
createOpts := domains.CreateOpts{
Description: "Testing Domain",
Enabled: &iTrue,
}
domain, err := CreateDomain(t, client, &createOpts)
if err != nil {
t.Fatalf("Unable to create domain: %v", err)
}
defer DeleteDomain(t, client, domain.ID)
tools.PrintResource(t, domain)
var iFalse bool = false
updateOpts := domains.UpdateOpts{
Description: "Staging Test Domain",
Enabled: &iFalse,
}
newDomain, err := domains.Update(client, domain.ID, updateOpts).Extract()
if err != nil {
t.Fatalf("Unable to update domain: %v", err)
}
tools.PrintResource(t, newDomain)
}

View File

@@ -0,0 +1,86 @@
// +build acceptance
package v3
import (
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/identity/v3/endpoints"
"github.com/gophercloud/gophercloud/openstack/identity/v3/services"
)
func TestEndpointsList(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v")
}
allPages, err := endpoints.List(client, nil).AllPages()
if err != nil {
t.Fatalf("Unable to list endpoints: %v", err)
}
allEndpoints, err := endpoints.ExtractEndpoints(allPages)
if err != nil {
t.Fatalf("Unable to extract endpoints: %v", err)
}
for _, endpoint := range allEndpoints {
tools.PrintResource(t, endpoint)
}
}
func TestEndpointsNavigateCatalog(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v")
}
// Discover the service we're interested in.
serviceListOpts := services.ListOpts{
ServiceType: "compute",
}
allPages, err := services.List(client, serviceListOpts).AllPages()
if err != nil {
t.Fatalf("Unable to lookup compute service: %v", err)
}
allServices, err := services.ExtractServices(allPages)
if err != nil {
t.Fatalf("Unable to extract service: %v")
}
if len(allServices) != 1 {
t.Fatalf("Expected one service, got %d", len(allServices))
}
computeService := allServices[0]
tools.PrintResource(t, computeService)
// Enumerate the endpoints available for this service.
endpointListOpts := endpoints.ListOpts{
Availability: gophercloud.AvailabilityPublic,
ServiceID: computeService.ID,
}
allPages, err = endpoints.List(client, endpointListOpts).AllPages()
if err != nil {
t.Fatalf("Unable to lookup compute endpoint: %v", err)
}
allEndpoints, err := endpoints.ExtractEndpoints(allPages)
if err != nil {
t.Fatalf("Unable to extract endpoint: %v")
}
if len(allEndpoints) != 1 {
t.Fatalf("Expected one endpoint, got %d", len(allEndpoints))
}
tools.PrintResource(t, allEndpoints[0])
}

View File

@@ -0,0 +1,79 @@
// +build acceptance
package v3
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/identity/v3/groups"
)
func TestGroupCRUD(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
createOpts := groups.CreateOpts{
Name: "testgroup",
DomainID: "default",
Extra: map[string]interface{}{
"email": "testgroup@example.com",
},
}
// Create Group in the default domain
group, err := CreateGroup(t, client, &createOpts)
if err != nil {
t.Fatalf("Unable to create group: %v", err)
}
defer DeleteGroup(t, client, group.ID)
tools.PrintResource(t, group)
tools.PrintResource(t, group.Extra)
updateOpts := groups.UpdateOpts{
Description: "Test Users",
Extra: map[string]interface{}{
"email": "thetestgroup@example.com",
},
}
newGroup, err := groups.Update(client, group.ID, updateOpts).Extract()
if err != nil {
t.Fatalf("Unable to update group: %v", err)
}
tools.PrintResource(t, newGroup)
tools.PrintResource(t, newGroup.Extra)
listOpts := groups.ListOpts{
DomainID: "default",
}
// List all Groups in default domain
allPages, err := groups.List(client, listOpts).AllPages()
if err != nil {
t.Fatalf("Unable to list groups: %v", err)
}
allGroups, err := groups.ExtractGroups(allPages)
if err != nil {
t.Fatalf("Unable to extract groups: %v", err)
}
for _, g := range allGroups {
tools.PrintResource(t, g)
tools.PrintResource(t, g.Extra)
}
// Get the recently created group by ID
p, err := groups.Get(client, group.ID).Extract()
if err != nil {
t.Fatalf("Unable to get group: %v", err)
}
tools.PrintResource(t, p)
}

View File

@@ -0,0 +1,326 @@
package v3
import (
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/identity/v3/domains"
"github.com/gophercloud/gophercloud/openstack/identity/v3/groups"
"github.com/gophercloud/gophercloud/openstack/identity/v3/projects"
"github.com/gophercloud/gophercloud/openstack/identity/v3/regions"
"github.com/gophercloud/gophercloud/openstack/identity/v3/roles"
"github.com/gophercloud/gophercloud/openstack/identity/v3/services"
"github.com/gophercloud/gophercloud/openstack/identity/v3/users"
)
// CreateProject will create a project with a random name.
// It takes an optional createOpts parameter since creating a project
// has so many options. An error will be returned if the project was
// unable to be created.
func CreateProject(t *testing.T, client *gophercloud.ServiceClient, c *projects.CreateOpts) (*projects.Project, error) {
name := tools.RandomString("ACPTTEST", 8)
t.Logf("Attempting to create project: %s", name)
var createOpts projects.CreateOpts
if c != nil {
createOpts = *c
} else {
createOpts = projects.CreateOpts{}
}
createOpts.Name = name
project, err := projects.Create(client, createOpts).Extract()
if err != nil {
return project, err
}
t.Logf("Successfully created project %s with ID %s", name, project.ID)
return project, nil
}
// CreateUser will create a user with a random name.
// It takes an optional createOpts parameter since creating a user
// has so many options. An error will be returned if the user was
// unable to be created.
func CreateUser(t *testing.T, client *gophercloud.ServiceClient, c *users.CreateOpts) (*users.User, error) {
name := tools.RandomString("ACPTTEST", 8)
t.Logf("Attempting to create user: %s", name)
var createOpts users.CreateOpts
if c != nil {
createOpts = *c
} else {
createOpts = users.CreateOpts{}
}
createOpts.Name = name
user, err := users.Create(client, createOpts).Extract()
if err != nil {
return user, err
}
t.Logf("Successfully created user %s with ID %s", name, user.ID)
return user, nil
}
// CreateGroup will create a group with a random name.
// It takes an optional createOpts parameter since creating a group
// has so many options. An error will be returned if the group was
// unable to be created.
func CreateGroup(t *testing.T, client *gophercloud.ServiceClient, c *groups.CreateOpts) (*groups.Group, error) {
name := tools.RandomString("ACPTTEST", 8)
t.Logf("Attempting to create group: %s", name)
var createOpts groups.CreateOpts
if c != nil {
createOpts = *c
} else {
createOpts = groups.CreateOpts{}
}
createOpts.Name = name
group, err := groups.Create(client, createOpts).Extract()
if err != nil {
return group, err
}
t.Logf("Successfully created group %s with ID %s", name, group.ID)
return group, nil
}
// CreateDomain will create a domain with a random name.
// It takes an optional createOpts parameter since creating a domain
// has many options. An error will be returned if the domain was
// unable to be created.
func CreateDomain(t *testing.T, client *gophercloud.ServiceClient, c *domains.CreateOpts) (*domains.Domain, error) {
name := tools.RandomString("ACPTTEST", 8)
t.Logf("Attempting to create domain: %s", name)
var createOpts domains.CreateOpts
if c != nil {
createOpts = *c
} else {
createOpts = domains.CreateOpts{}
}
createOpts.Name = name
domain, err := domains.Create(client, createOpts).Extract()
if err != nil {
return domain, err
}
t.Logf("Successfully created domain %s with ID %s", name, domain.ID)
return domain, nil
}
// CreateRole will create a role with a random name.
// It takes an optional createOpts parameter since creating a role
// has so many options. An error will be returned if the role was
// unable to be created.
func CreateRole(t *testing.T, client *gophercloud.ServiceClient, c *roles.CreateOpts) (*roles.Role, error) {
name := tools.RandomString("ACPTTEST", 8)
t.Logf("Attempting to create role: %s", name)
var createOpts roles.CreateOpts
if c != nil {
createOpts = *c
} else {
createOpts = roles.CreateOpts{}
}
createOpts.Name = name
role, err := roles.Create(client, createOpts).Extract()
if err != nil {
return role, err
}
t.Logf("Successfully created role %s with ID %s", name, role.ID)
return role, nil
}
// CreateRegion will create a region with a random name.
// It takes an optional createOpts parameter since creating a region
// has so many options. An error will be returned if the region was
// unable to be created.
func CreateRegion(t *testing.T, client *gophercloud.ServiceClient, c *regions.CreateOpts) (*regions.Region, error) {
id := tools.RandomString("ACPTTEST", 8)
t.Logf("Attempting to create region: %s", id)
var createOpts regions.CreateOpts
if c != nil {
createOpts = *c
} else {
createOpts = regions.CreateOpts{}
}
createOpts.ID = id
region, err := regions.Create(client, createOpts).Extract()
if err != nil {
return region, err
}
t.Logf("Successfully created region %s", id)
return region, nil
}
// CreateService will create a service with a random name.
// It takes an optional createOpts parameter since creating a service
// has so many options. An error will be returned if the service was
// unable to be created.
func CreateService(t *testing.T, client *gophercloud.ServiceClient, c *services.CreateOpts) (*services.Service, error) {
name := tools.RandomString("ACPTTEST", 8)
t.Logf("Attempting to create service: %s", name)
var createOpts services.CreateOpts
if c != nil {
createOpts = *c
} else {
createOpts = services.CreateOpts{}
}
createOpts.Extra["name"] = name
service, err := services.Create(client, createOpts).Extract()
if err != nil {
return service, err
}
t.Logf("Successfully created service %s", service.ID)
return service, nil
}
// DeleteProject will delete a project by ID. A fatal error will occur if
// the project ID failed to be deleted. This works best when using it as
// a deferred function.
func DeleteProject(t *testing.T, client *gophercloud.ServiceClient, projectID string) {
err := projects.Delete(client, projectID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete project %s: %v", projectID, err)
}
t.Logf("Deleted project: %s", projectID)
}
// DeleteUser will delete a user by ID. A fatal error will occur if
// the user failed to be deleted. This works best when using it as
// a deferred function.
func DeleteUser(t *testing.T, client *gophercloud.ServiceClient, userID string) {
err := users.Delete(client, userID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete user with ID %s: %v", userID, err)
}
t.Logf("Deleted user with ID: %s", userID)
}
// DeleteGroup will delete a group by ID. A fatal error will occur if
// the group failed to be deleted. This works best when using it as
// a deferred function.
func DeleteGroup(t *testing.T, client *gophercloud.ServiceClient, groupID string) {
err := groups.Delete(client, groupID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete group %s: %v", groupID, err)
}
t.Logf("Deleted group: %s", groupID)
}
// DeleteDomain will delete a domain by ID. A fatal error will occur if
// the project ID failed to be deleted. This works best when using it as
// a deferred function.
func DeleteDomain(t *testing.T, client *gophercloud.ServiceClient, domainID string) {
err := domains.Delete(client, domainID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete domain %s: %v", domainID, err)
}
t.Logf("Deleted domain: %s", domainID)
}
// DeleteRole will delete a role by ID. A fatal error will occur if
// the role failed to be deleted. This works best when using it as
// a deferred function.
func DeleteRole(t *testing.T, client *gophercloud.ServiceClient, roleID string) {
err := roles.Delete(client, roleID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete role %s: %v", roleID, err)
}
t.Logf("Deleted role: %s", roleID)
}
// DeleteRegion will delete a reg by ID. A fatal error will occur if
// the region failed to be deleted. This works best when using it as
// a deferred function.
func DeleteRegion(t *testing.T, client *gophercloud.ServiceClient, regionID string) {
err := regions.Delete(client, regionID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete region %s: %v", regionID, err)
}
t.Logf("Deleted region: %s", regionID)
}
// DeleteService will delete a reg by ID. A fatal error will occur if
// the service failed to be deleted. This works best when using it as
// a deferred function.
func DeleteService(t *testing.T, client *gophercloud.ServiceClient, serviceID string) {
err := services.Delete(client, serviceID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete service %s: %v", serviceID, err)
}
t.Logf("Deleted service: %s", serviceID)
}
// UnassignRole will delete a role assigned to a user/group on a project/domain
// A fatal error will occur if it fails to delete the assignment.
// This works best when using it as a deferred function.
func UnassignRole(t *testing.T, client *gophercloud.ServiceClient, roleID string, opts *roles.UnassignOpts) {
err := roles.Unassign(client, roleID, *opts).ExtractErr()
if err != nil {
t.Fatalf("Unable to unassign a role %v on context %+v: %v", roleID, *opts, err)
}
t.Logf("Unassigned the role %v on context %+v", roleID, *opts)
}
// FindRole finds all roles that the current authenticated client has access
// to and returns the first one found. An error will be returned if the lookup
// was unsuccessful.
func FindRole(t *testing.T, client *gophercloud.ServiceClient) (*roles.Role, error) {
t.Log("Attempting to find a role")
var role *roles.Role
allPages, err := roles.List(client, nil).AllPages()
if err != nil {
return nil, err
}
allRoles, err := roles.ExtractRoles(allPages)
if err != nil {
return nil, err
}
for _, r := range allRoles {
role = &r
break
}
t.Logf("Successfully found a role %s with ID %s", role.Name, role.ID)
return role, nil
}

View File

@@ -0,0 +1 @@
package v3

View File

@@ -0,0 +1,158 @@
// +build acceptance
package v3
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/identity/v3/projects"
)
func TestProjectsList(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
var iTrue bool = true
listOpts := projects.ListOpts{
Enabled: &iTrue,
}
allPages, err := projects.List(client, listOpts).AllPages()
if err != nil {
t.Fatalf("Unable to list projects: %v", err)
}
allProjects, err := projects.ExtractProjects(allPages)
if err != nil {
t.Fatalf("Unable to extract projects: %v", err)
}
for _, project := range allProjects {
tools.PrintResource(t, project)
}
}
func TestProjectsGet(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
allPages, err := projects.List(client, nil).AllPages()
if err != nil {
t.Fatalf("Unable to list projects: %v", err)
}
allProjects, err := projects.ExtractProjects(allPages)
if err != nil {
t.Fatalf("Unable to extract projects: %v", err)
}
project := allProjects[0]
p, err := projects.Get(client, project.ID).Extract()
if err != nil {
t.Fatalf("Unable to get project: %v", err)
}
tools.PrintResource(t, p)
}
func TestProjectsCRUD(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v")
}
project, err := CreateProject(t, client, nil)
if err != nil {
t.Fatalf("Unable to create project: %v", err)
}
defer DeleteProject(t, client, project.ID)
tools.PrintResource(t, project)
var iFalse bool = false
updateOpts := projects.UpdateOpts{
Enabled: &iFalse,
}
updatedProject, err := projects.Update(client, project.ID, updateOpts).Extract()
if err != nil {
t.Fatalf("Unable to update project: %v", err)
}
tools.PrintResource(t, updatedProject)
}
func TestProjectsDomain(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v")
}
var iTrue = true
createOpts := projects.CreateOpts{
IsDomain: &iTrue,
}
projectDomain, err := CreateProject(t, client, &createOpts)
if err != nil {
t.Fatalf("Unable to create project: %v", err)
}
defer DeleteProject(t, client, projectDomain.ID)
tools.PrintResource(t, projectDomain)
createOpts = projects.CreateOpts{
DomainID: projectDomain.ID,
}
project, err := CreateProject(t, client, &createOpts)
if err != nil {
t.Fatalf("Unable to create project: %v", err)
}
defer DeleteProject(t, client, project.ID)
tools.PrintResource(t, project)
var iFalse = false
updateOpts := projects.UpdateOpts{
Enabled: &iFalse,
}
_, err = projects.Update(client, projectDomain.ID, updateOpts).Extract()
if err != nil {
t.Fatalf("Unable to disable domain: %v")
}
}
func TestProjectsNested(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v")
}
projectMain, err := CreateProject(t, client, nil)
if err != nil {
t.Fatalf("Unable to create project: %v", err)
}
defer DeleteProject(t, client, projectMain.ID)
tools.PrintResource(t, projectMain)
createOpts := projects.CreateOpts{
ParentID: projectMain.ID,
}
project, err := CreateProject(t, client, &createOpts)
if err != nil {
t.Fatalf("Unable to create project: %v", err)
}
defer DeleteProject(t, client, project.ID)
tools.PrintResource(t, project)
}

View File

@@ -0,0 +1,107 @@
// +build acceptance
package v3
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/identity/v3/regions"
)
func TestRegionsList(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
listOpts := regions.ListOpts{
ParentRegionID: "RegionOne",
}
allPages, err := regions.List(client, listOpts).AllPages()
if err != nil {
t.Fatalf("Unable to list regions: %v", err)
}
allRegions, err := regions.ExtractRegions(allPages)
if err != nil {
t.Fatalf("Unable to extract regions: %v", err)
}
for _, region := range allRegions {
tools.PrintResource(t, region)
}
}
func TestRegionsGet(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
allPages, err := regions.List(client, nil).AllPages()
if err != nil {
t.Fatalf("Unable to list regions: %v", err)
}
allRegions, err := regions.ExtractRegions(allPages)
if err != nil {
t.Fatalf("Unable to extract regions: %v", err)
}
region := allRegions[0]
p, err := regions.Get(client, region.ID).Extract()
if err != nil {
t.Fatalf("Unable to get region: %v", err)
}
tools.PrintResource(t, p)
}
func TestRegionsCRUD(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
createOpts := regions.CreateOpts{
ID: "testregion",
Description: "Region for testing",
Extra: map[string]interface{}{
"email": "testregion@example.com",
},
}
// Create region in the default domain
region, err := CreateRegion(t, client, &createOpts)
if err != nil {
t.Fatalf("Unable to create region: %v", err)
}
defer DeleteRegion(t, client, region.ID)
tools.PrintResource(t, region)
tools.PrintResource(t, region.Extra)
updateOpts := regions.UpdateOpts{
Description: "Region A for testing",
/*
// Due to a bug in Keystone, the Extra column of the Region table
// is not updatable, see: https://bugs.launchpad.net/keystone/+bug/1729933
// The following lines should be uncommented once the fix is merged.
Extra: map[string]interface{}{
"email": "testregionA@example.com",
},
*/
}
newRegion, err := regions.Update(client, region.ID, updateOpts).Extract()
if err != nil {
t.Fatalf("Unable to update region: %v", err)
}
tools.PrintResource(t, newRegion)
tools.PrintResource(t, newRegion.Extra)
}

View File

@@ -0,0 +1,328 @@
// +build acceptance
package v3
import (
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/identity/v3/domains"
"github.com/gophercloud/gophercloud/openstack/identity/v3/roles"
)
func TestRolesList(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
listOpts := roles.ListOpts{
DomainID: "default",
}
allPages, err := roles.List(client, listOpts).AllPages()
if err != nil {
t.Fatalf("Unable to list roles: %v", err)
}
allRoles, err := roles.ExtractRoles(allPages)
if err != nil {
t.Fatalf("Unable to extract roles: %v", err)
}
for _, role := range allRoles {
tools.PrintResource(t, role)
}
}
func TestRolesGet(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
role, err := FindRole(t, client)
if err != nil {
t.Fatalf("Unable to find a role: %v", err)
}
p, err := roles.Get(client, role.ID).Extract()
if err != nil {
t.Fatalf("Unable to get role: %v", err)
}
tools.PrintResource(t, p)
}
func TestRoleCRUD(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
createOpts := roles.CreateOpts{
Name: "testrole",
DomainID: "default",
Extra: map[string]interface{}{
"description": "test role description",
},
}
// Create Role in the default domain
role, err := CreateRole(t, client, &createOpts)
if err != nil {
t.Fatalf("Unable to create role: %v", err)
}
defer DeleteRole(t, client, role.ID)
tools.PrintResource(t, role)
tools.PrintResource(t, role.Extra)
updateOpts := roles.UpdateOpts{
Extra: map[string]interface{}{
"description": "updated test role description",
},
}
newRole, err := roles.Update(client, role.ID, updateOpts).Extract()
if err != nil {
t.Fatalf("Unable to update role: %v", err)
}
tools.PrintResource(t, newRole)
tools.PrintResource(t, newRole.Extra)
}
func TestRoleAssignToUserOnProject(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an indentity client: %v", err)
}
project, err := CreateProject(t, client, nil)
if err != nil {
t.Fatal("Unable to create a project")
}
defer DeleteProject(t, client, project.ID)
role, err := FindRole(t, client)
if err != nil {
t.Fatalf("Unable to get a role: %v", err)
}
user, err := CreateUser(t, client, nil)
if err != nil {
t.Fatalf("Unable to create user: %v", err)
}
defer DeleteUser(t, client, user.ID)
t.Logf("Attempting to assign a role %s to a user %s on a project %s", role.Name, user.Name, project.Name)
err = roles.Assign(client, role.ID, roles.AssignOpts{
UserID: user.ID,
ProjectID: project.ID,
}).ExtractErr()
if err != nil {
t.Fatalf("Unable to assign a role to a user on a project: %v", err)
}
t.Logf("Successfully assigned a role %s to a user %s on a project %s", role.Name, user.Name, project.Name)
defer UnassignRole(t, client, role.ID, &roles.UnassignOpts{
UserID: user.ID,
ProjectID: project.ID,
})
allPages, err := roles.ListAssignments(client, roles.ListAssignmentsOpts{
RoleID: role.ID,
ScopeProjectID: project.ID,
UserID: user.ID,
}).AllPages()
if err != nil {
t.Fatalf("Unable to list role assignments: %v", err)
}
allRoleAssignments, err := roles.ExtractRoleAssignments(allPages)
if err != nil {
t.Fatalf("Unable to extract role assignments: %v", err)
}
t.Logf("Role assignments of user %s on project %s:", user.Name, project.Name)
for _, roleAssignment := range allRoleAssignments {
tools.PrintResource(t, roleAssignment)
}
}
func TestRoleAssignToUserOnDomain(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an indentity client: %v", err)
}
domain, err := CreateDomain(t, client, &domains.CreateOpts{
Enabled: gophercloud.Disabled,
})
if err != nil {
t.Fatal("Unable to create a domain")
}
defer DeleteDomain(t, client, domain.ID)
role, err := FindRole(t, client)
if err != nil {
t.Fatalf("Unable to get a role: %v", err)
}
user, err := CreateUser(t, client, nil)
if err != nil {
t.Fatalf("Unable to create user: %v", err)
}
defer DeleteUser(t, client, user.ID)
t.Logf("Attempting to assign a role %s to a user %s on a domain %s", role.Name, user.Name, domain.Name)
err = roles.Assign(client, role.ID, roles.AssignOpts{
UserID: user.ID,
DomainID: domain.ID,
}).ExtractErr()
if err != nil {
t.Fatalf("Unable to assign a role to a user on a domain: %v", err)
}
t.Logf("Successfully assigned a role %s to a user %s on a domain %s", role.Name, user.Name, domain.Name)
defer UnassignRole(t, client, role.ID, &roles.UnassignOpts{
UserID: user.ID,
DomainID: domain.ID,
})
allPages, err := roles.ListAssignments(client, roles.ListAssignmentsOpts{
RoleID: role.ID,
ScopeDomainID: domain.ID,
UserID: user.ID,
}).AllPages()
if err != nil {
t.Fatalf("Unable to list role assignments: %v", err)
}
allRoleAssignments, err := roles.ExtractRoleAssignments(allPages)
if err != nil {
t.Fatalf("Unable to extract role assignments: %v", err)
}
t.Logf("Role assignments of user %s on domain %s:", user.Name, domain.Name)
for _, roleAssignment := range allRoleAssignments {
tools.PrintResource(t, roleAssignment)
}
}
func TestRoleAssignToGroupOnDomain(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an indentity client: %v", err)
}
domain, err := CreateDomain(t, client, &domains.CreateOpts{
Enabled: gophercloud.Disabled,
})
if err != nil {
t.Fatal("Unable to create a domain")
}
defer DeleteDomain(t, client, domain.ID)
role, err := FindRole(t, client)
if err != nil {
t.Fatalf("Unable to get a role: %v", err)
}
group, err := CreateGroup(t, client, nil)
if err != nil {
t.Fatalf("Unable to create group: %v", err)
}
defer DeleteGroup(t, client, group.ID)
t.Logf("Attempting to assign a role %s to a group %s on a domain %s", role.Name, group.Name, domain.Name)
err = roles.Assign(client, role.ID, roles.AssignOpts{
GroupID: group.ID,
DomainID: domain.ID,
}).ExtractErr()
if err != nil {
t.Fatalf("Unable to assign a role to a group on a domain: %v", err)
}
t.Logf("Successfully assigned a role %s to a group %s on a domain %s", role.Name, group.Name, domain.Name)
defer UnassignRole(t, client, role.ID, &roles.UnassignOpts{
GroupID: group.ID,
DomainID: domain.ID,
})
allPages, err := roles.ListAssignments(client, roles.ListAssignmentsOpts{
RoleID: role.ID,
ScopeDomainID: domain.ID,
GroupID: group.ID,
}).AllPages()
if err != nil {
t.Fatalf("Unable to list role assignments: %v", err)
}
allRoleAssignments, err := roles.ExtractRoleAssignments(allPages)
if err != nil {
t.Fatalf("Unable to extract role assignments: %v", err)
}
t.Logf("Role assignments of group %s on domain %s:", group.Name, domain.Name)
for _, roleAssignment := range allRoleAssignments {
tools.PrintResource(t, roleAssignment)
}
}
func TestRoleAssignToGroupOnProject(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an indentity client: %v", err)
}
project, err := CreateProject(t, client, nil)
if err != nil {
t.Fatal("Unable to create a project")
}
defer DeleteProject(t, client, project.ID)
role, err := FindRole(t, client)
if err != nil {
t.Fatalf("Unable to get a role: %v", err)
}
group, err := CreateGroup(t, client, nil)
if err != nil {
t.Fatalf("Unable to create group: %v", err)
}
defer DeleteGroup(t, client, group.ID)
t.Logf("Attempting to assign a role %s to a group %s on a project %s", role.Name, group.Name, project.Name)
err = roles.Assign(client, role.ID, roles.AssignOpts{
GroupID: group.ID,
ProjectID: project.ID,
}).ExtractErr()
if err != nil {
t.Fatalf("Unable to assign a role to a group on a project: %v", err)
}
t.Logf("Successfully assigned a role %s to a group %s on a project %s", role.Name, group.Name, project.Name)
defer UnassignRole(t, client, role.ID, &roles.UnassignOpts{
GroupID: group.ID,
ProjectID: project.ID,
})
allPages, err := roles.ListAssignments(client, roles.ListAssignmentsOpts{
RoleID: role.ID,
ScopeProjectID: project.ID,
GroupID: group.ID,
}).AllPages()
if err != nil {
t.Fatalf("Unable to list role assignments: %v", err)
}
allRoleAssignments, err := roles.ExtractRoleAssignments(allPages)
if err != nil {
t.Fatalf("Unable to extract role assignments: %v", err)
}
t.Logf("Role assignments of group %s on project %s:", group.Name, project.Name)
for _, roleAssignment := range allRoleAssignments {
tools.PrintResource(t, roleAssignment)
}
}

View File

@@ -0,0 +1,77 @@
// +build acceptance
package v3
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/identity/v3/services"
)
func TestServicesList(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v")
}
listOpts := services.ListOpts{
ServiceType: "identity",
}
allPages, err := services.List(client, listOpts).AllPages()
if err != nil {
t.Fatalf("Unable to list services: %v", err)
}
allServices, err := services.ExtractServices(allPages)
if err != nil {
t.Fatalf("Unable to extract services: %v", err)
}
for _, service := range allServices {
tools.PrintResource(t, service)
}
}
func TestServicesCRUD(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
createOpts := services.CreateOpts{
Type: "testing",
Extra: map[string]interface{}{
"email": "testservice@example.com",
},
}
// Create service in the default domain
service, err := CreateService(t, client, &createOpts)
if err != nil {
t.Fatalf("Unable to create service: %v", err)
}
defer DeleteService(t, client, service.ID)
tools.PrintResource(t, service)
tools.PrintResource(t, service.Extra)
updateOpts := services.UpdateOpts{
Type: "testing2",
Extra: map[string]interface{}{
"description": "Test Users",
"email": "thetestservice@example.com",
},
}
newService, err := services.Update(client, service.ID, updateOpts).Extract()
if err != nil {
t.Fatalf("Unable to update service: %v", err)
}
tools.PrintResource(t, newService)
tools.PrintResource(t, newService.Extra)
}

View File

@@ -0,0 +1,60 @@
// +build acceptance
package v3
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack"
"github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
)
func TestGetToken(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v")
}
ao, err := openstack.AuthOptionsFromEnv()
if err != nil {
t.Fatalf("Unable to obtain environment auth options: %v", err)
}
authOptions := tokens.AuthOptions{
Username: ao.Username,
Password: ao.Password,
DomainName: "default",
}
token, err := tokens.Create(client, &authOptions).Extract()
if err != nil {
t.Fatalf("Unable to get token: %v", err)
}
tools.PrintResource(t, token)
catalog, err := tokens.Get(client, token.ID).ExtractServiceCatalog()
if err != nil {
t.Fatalf("Unable to get catalog from token: %v", err)
}
tools.PrintResource(t, catalog)
user, err := tokens.Get(client, token.ID).ExtractUser()
if err != nil {
t.Fatalf("Unable to get user from token: %v", err)
}
tools.PrintResource(t, user)
roles, err := tokens.Get(client, token.ID).ExtractRoles()
if err != nil {
t.Fatalf("Unable to get roles from token: %v", err)
}
tools.PrintResource(t, roles)
project, err := tokens.Get(client, token.ID).ExtractProject()
if err != nil {
t.Fatalf("Unable to get project from token: %v", err)
}
tools.PrintResource(t, project)
}

View File

@@ -0,0 +1,222 @@
// +build acceptance
package v3
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/identity/v3/groups"
"github.com/gophercloud/gophercloud/openstack/identity/v3/projects"
"github.com/gophercloud/gophercloud/openstack/identity/v3/users"
)
func TestUsersList(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
var iTrue bool = true
listOpts := users.ListOpts{
Enabled: &iTrue,
}
allPages, err := users.List(client, listOpts).AllPages()
if err != nil {
t.Fatalf("Unable to list users: %v", err)
}
allUsers, err := users.ExtractUsers(allPages)
if err != nil {
t.Fatalf("Unable to extract users: %v", err)
}
for _, user := range allUsers {
tools.PrintResource(t, user)
tools.PrintResource(t, user.Extra)
}
}
func TestUsersGet(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
allPages, err := users.List(client, nil).AllPages()
if err != nil {
t.Fatalf("Unable to list users: %v", err)
}
allUsers, err := users.ExtractUsers(allPages)
if err != nil {
t.Fatalf("Unable to extract users: %v", err)
}
user := allUsers[0]
p, err := users.Get(client, user.ID).Extract()
if err != nil {
t.Fatalf("Unable to get user: %v", err)
}
tools.PrintResource(t, p)
}
func TestUserCRUD(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
project, err := CreateProject(t, client, nil)
if err != nil {
t.Fatalf("Unable to create project: %v", err)
}
defer DeleteProject(t, client, project.ID)
tools.PrintResource(t, project)
createOpts := users.CreateOpts{
DefaultProjectID: project.ID,
Password: "foobar",
DomainID: "default",
Options: map[users.Option]interface{}{
users.IgnorePasswordExpiry: true,
users.MultiFactorAuthRules: []interface{}{
[]string{"password", "totp"},
[]string{"password", "custom-auth-method"},
},
},
Extra: map[string]interface{}{
"email": "jsmith@example.com",
},
}
user, err := CreateUser(t, client, &createOpts)
if err != nil {
t.Fatalf("Unable to create user: %v", err)
}
defer DeleteUser(t, client, user.ID)
tools.PrintResource(t, user)
tools.PrintResource(t, user.Extra)
iFalse := false
updateOpts := users.UpdateOpts{
Enabled: &iFalse,
Options: map[users.Option]interface{}{
users.MultiFactorAuthRules: nil,
},
Extra: map[string]interface{}{
"disabled_reason": "DDOS",
},
}
newUser, err := users.Update(client, user.ID, updateOpts).Extract()
if err != nil {
t.Fatalf("Unable to update user: %v", err)
}
tools.PrintResource(t, newUser)
tools.PrintResource(t, newUser.Extra)
}
func TestUsersListGroups(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
allUserPages, err := users.List(client, nil).AllPages()
if err != nil {
t.Fatalf("Unable to list users: %v", err)
}
allUsers, err := users.ExtractUsers(allUserPages)
if err != nil {
t.Fatalf("Unable to extract users: %v", err)
}
user := allUsers[0]
allGroupPages, err := users.ListGroups(client, user.ID).AllPages()
if err != nil {
t.Fatalf("Unable to list groups: %v", err)
}
allGroups, err := groups.ExtractGroups(allGroupPages)
if err != nil {
t.Fatalf("Unable to extract groups: %v", err)
}
for _, group := range allGroups {
tools.PrintResource(t, group)
tools.PrintResource(t, group.Extra)
}
}
func TestUsersListProjects(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
allUserPages, err := users.List(client, nil).AllPages()
if err != nil {
t.Fatalf("Unable to list users: %v", err)
}
allUsers, err := users.ExtractUsers(allUserPages)
if err != nil {
t.Fatalf("Unable to extract users: %v", err)
}
user := allUsers[0]
allProjectPages, err := users.ListProjects(client, user.ID).AllPages()
if err != nil {
t.Fatalf("Unable to list projects: %v", err)
}
allProjects, err := projects.ExtractProjects(allProjectPages)
if err != nil {
t.Fatalf("Unable to extract projects: %v", err)
}
for _, project := range allProjects {
tools.PrintResource(t, project)
}
}
func TestUsersListInGroup(t *testing.T) {
client, err := clients.NewIdentityV3Client()
if err != nil {
t.Fatalf("Unable to obtain an identity client: %v", err)
}
allGroupPages, err := groups.List(client, nil).AllPages()
if err != nil {
t.Fatalf("Unable to list groups: %v", err)
}
allGroups, err := groups.ExtractGroups(allGroupPages)
if err != nil {
t.Fatalf("Unable to extract groups: %v", err)
}
group := allGroups[0]
allUserPages, err := users.ListInGroup(client, group.ID, nil).AllPages()
if err != nil {
t.Fatalf("Unable to list users: %v", err)
}
allUsers, err := users.ExtractUsers(allUserPages)
if err != nil {
t.Fatalf("Unable to extract users: %v", err)
}
for _, user := range allUsers {
tools.PrintResource(t, user)
tools.PrintResource(t, user.Extra)
}
}

View File

@@ -0,0 +1,80 @@
// +build acceptance imageservice images
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
"github.com/gophercloud/gophercloud/pagination"
)
func TestImagesListEachPage(t *testing.T) {
client, err := clients.NewImageServiceV2Client()
if err != nil {
t.Fatalf("Unable to create an image service client: %v", err)
}
listOpts := images.ListOpts{
Limit: 1,
}
pager := images.List(client, listOpts)
err = pager.EachPage(func(page pagination.Page) (bool, error) {
images, err := images.ExtractImages(page)
if err != nil {
t.Fatalf("Unable to extract images: %v", err)
}
for _, image := range images {
tools.PrintResource(t, image)
tools.PrintResource(t, image.Properties)
}
return true, nil
})
}
func TestImagesListAllPages(t *testing.T) {
client, err := clients.NewImageServiceV2Client()
if err != nil {
t.Fatalf("Unable to create an image service client: %v", err)
}
listOpts := images.ListOpts{
Limit: 1,
}
allPages, err := images.List(client, listOpts).AllPages()
if err != nil {
t.Fatalf("Unable to retrieve all images: %v", err)
}
allImages, err := images.ExtractImages(allPages)
if err != nil {
t.Fatalf("Unable to extract images: %v", err)
}
for _, image := range allImages {
tools.PrintResource(t, image)
tools.PrintResource(t, image.Properties)
}
}
func TestImagesCreateDestroyEmptyImage(t *testing.T) {
client, err := clients.NewImageServiceV2Client()
if err != nil {
t.Fatalf("Unable to create an image service client: %v", err)
}
image, err := CreateEmptyImage(t, client)
if err != nil {
t.Fatalf("Unable to create empty image: %v", err)
}
defer DeleteImage(t, client, image)
tools.PrintResource(t, image)
}

View File

@@ -0,0 +1,55 @@
// Package v2 contains common functions for creating imageservice resources
// for use in acceptance tests. See the `*_test.go` files for example usages.
package v2
import (
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
)
// CreateEmptyImage will create an image, but with no actual image data.
// An error will be returned if an image was unable to be created.
func CreateEmptyImage(t *testing.T, client *gophercloud.ServiceClient) (*images.Image, error) {
var image *images.Image
name := tools.RandomString("ACPTTEST", 16)
t.Logf("Attempting to create image: %s", name)
protected := false
visibility := images.ImageVisibilityPrivate
createOpts := &images.CreateOpts{
Name: name,
ContainerFormat: "bare",
DiskFormat: "qcow2",
MinDisk: 0,
MinRAM: 0,
Protected: &protected,
Visibility: &visibility,
Properties: map[string]string{
"architecture": "x86_64",
},
}
image, err := images.Create(client, createOpts).Extract()
if err != nil {
return image, err
}
t.Logf("Created image %s: %#v", name, image)
return image, nil
}
// DeleteImage deletes an image.
// A fatal error will occur if the image failed to delete. This works best when
// used as a deferred function.
func DeleteImage(t *testing.T, client *gophercloud.ServiceClient, image *images.Image) {
err := images.Delete(client, image.ID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete image %s: %v", image.ID, err)
}
t.Logf("Deleted image: %s", image.ID)
}

View File

@@ -0,0 +1,53 @@
// +build acceptance networking
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/networking/v2/apiversions"
)
func TestAPIVersionsList(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
allPages, err := apiversions.ListVersions(client).AllPages()
if err != nil {
t.Fatalf("Unable to list api versions: %v", err)
}
allAPIVersions, err := apiversions.ExtractAPIVersions(allPages)
if err != nil {
t.Fatalf("Unable to extract api versions: %v", err)
}
for _, apiVersion := range allAPIVersions {
tools.PrintResource(t, apiVersion)
}
}
func TestAPIResourcesList(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
allPages, err := apiversions.ListVersionResources(client, "v2.0").AllPages()
if err != nil {
t.Fatalf("Unable to list api version reosources: %v", err)
}
allVersionResources, err := apiversions.ExtractVersionResources(allPages)
if err != nil {
t.Fatalf("Unable to extract version resources: %v", err)
}
for _, versionResource := range allVersionResources {
tools.PrintResource(t, versionResource)
}
}

View File

@@ -0,0 +1,46 @@
// +build acceptance networking extensions
package v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/common/extensions"
)
func TestExtensionsList(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
allPages, err := extensions.List(client).AllPages()
if err != nil {
t.Fatalf("Unable to list extensions: %v", err)
}
allExtensions, err := extensions.ExtractExtensions(allPages)
if err != nil {
t.Fatalf("Unable to extract extensions: %v", err)
}
for _, extension := range allExtensions {
tools.PrintResource(t, extension)
}
}
func TestExtensionGet(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
extension, err := extensions.Get(client, "router").Extract()
if err != nil {
t.Fatalf("Unable to get extension port-security: %v", err)
}
tools.PrintResource(t, extension)
}

View File

@@ -0,0 +1,142 @@
package extensions
import (
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/external"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules"
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
)
// CreateExternalNetwork will create an external network. An error will be
// returned if the creation failed.
func CreateExternalNetwork(t *testing.T, client *gophercloud.ServiceClient) (*networks.Network, error) {
networkName := tools.RandomString("TESTACC-", 8)
t.Logf("Attempting to create external network: %s", networkName)
adminStateUp := true
isExternal := true
networkCreateOpts := networks.CreateOpts{
Name: networkName,
AdminStateUp: &adminStateUp,
}
createOpts := external.CreateOptsExt{
CreateOptsBuilder: networkCreateOpts,
External: &isExternal,
}
network, err := networks.Create(client, createOpts).Extract()
if err != nil {
return network, err
}
t.Logf("Created external network: %s", networkName)
return network, nil
}
// CreatePortWithSecurityGroup will create a port with a security group
// attached. An error will be returned if the port could not be created.
func CreatePortWithSecurityGroup(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID, secGroupID string) (*ports.Port, error) {
portName := tools.RandomString("TESTACC-", 8)
iFalse := false
t.Logf("Attempting to create port: %s", portName)
createOpts := ports.CreateOpts{
NetworkID: networkID,
Name: portName,
AdminStateUp: &iFalse,
FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}},
SecurityGroups: &[]string{secGroupID},
}
port, err := ports.Create(client, createOpts).Extract()
if err != nil {
return port, err
}
t.Logf("Successfully created port: %s", portName)
return port, nil
}
// CreateSecurityGroup will create a security group with a random name.
// An error will be returned if one was failed to be created.
func CreateSecurityGroup(t *testing.T, client *gophercloud.ServiceClient) (*groups.SecGroup, error) {
secGroupName := tools.RandomString("TESTACC-", 8)
t.Logf("Attempting to create security group: %s", secGroupName)
createOpts := groups.CreateOpts{
Name: secGroupName,
}
secGroup, err := groups.Create(client, createOpts).Extract()
if err != nil {
return secGroup, err
}
t.Logf("Created security group: %s", secGroup.ID)
return secGroup, nil
}
// CreateSecurityGroupRule will create a security group rule with a random name
// and random port between 80 and 99.
// An error will be returned if one was failed to be created.
func CreateSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, secGroupID string) (*rules.SecGroupRule, error) {
t.Logf("Attempting to create security group rule in group: %s", secGroupID)
fromPort := tools.RandomInt(80, 89)
toPort := tools.RandomInt(90, 99)
createOpts := rules.CreateOpts{
Direction: "ingress",
EtherType: "IPv4",
SecGroupID: secGroupID,
PortRangeMin: fromPort,
PortRangeMax: toPort,
Protocol: rules.ProtocolTCP,
}
rule, err := rules.Create(client, createOpts).Extract()
if err != nil {
return rule, err
}
t.Logf("Created security group rule: %s", rule.ID)
return rule, nil
}
// DeleteSecurityGroup will delete a security group of a specified ID.
// A fatal error will occur if the deletion failed. This works best as a
// deferred function
func DeleteSecurityGroup(t *testing.T, client *gophercloud.ServiceClient, secGroupID string) {
t.Logf("Attempting to delete security group: %s", secGroupID)
err := groups.Delete(client, secGroupID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete security group: %v", err)
}
}
// DeleteSecurityGroupRule will delete a security group rule of a specified ID.
// A fatal error will occur if the deletion failed. This works best as a
// deferred function
func DeleteSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, ruleID string) {
t.Logf("Attempting to delete security group rule: %s", ruleID)
err := rules.Delete(client, ruleID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete security group rule: %v", err)
}
}

View File

@@ -0,0 +1,212 @@
// +build acceptance networking fwaas
package fwaas
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
layer3 "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/layer3"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/routerinsertion"
)
func TestFirewallList(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
allPages, err := firewalls.List(client, nil).AllPages()
if err != nil {
t.Fatalf("Unable to list firewalls: %v", err)
}
allFirewalls, err := firewalls.ExtractFirewalls(allPages)
if err != nil {
t.Fatalf("Unable to extract firewalls: %v", err)
}
for _, firewall := range allFirewalls {
tools.PrintResource(t, firewall)
}
}
func TestFirewallCRUD(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
router, err := layer3.CreateExternalRouter(t, client)
if err != nil {
t.Fatalf("Unable to create router: %v", err)
}
defer layer3.DeleteRouter(t, client, router.ID)
rule, err := CreateRule(t, client)
if err != nil {
t.Fatalf("Unable to create rule: %v", err)
}
defer DeleteRule(t, client, rule.ID)
tools.PrintResource(t, rule)
policy, err := CreatePolicy(t, client, rule.ID)
if err != nil {
t.Fatalf("Unable to create policy: %v", err)
}
defer DeletePolicy(t, client, policy.ID)
tools.PrintResource(t, policy)
firewall, err := CreateFirewall(t, client, policy.ID)
if err != nil {
t.Fatalf("Unable to create firewall: %v", err)
}
defer DeleteFirewall(t, client, firewall.ID)
tools.PrintResource(t, firewall)
updateOpts := firewalls.UpdateOpts{
PolicyID: policy.ID,
Description: "Some firewall description",
}
_, err = firewalls.Update(client, firewall.ID, updateOpts).Extract()
if err != nil {
t.Fatalf("Unable to update firewall: %v", err)
}
newFirewall, err := firewalls.Get(client, firewall.ID).Extract()
if err != nil {
t.Fatalf("Unable to get firewall: %v", err)
}
tools.PrintResource(t, newFirewall)
}
func TestFirewallCRUDRouter(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
router, err := layer3.CreateExternalRouter(t, client)
if err != nil {
t.Fatalf("Unable to create router: %v", err)
}
defer layer3.DeleteRouter(t, client, router.ID)
rule, err := CreateRule(t, client)
if err != nil {
t.Fatalf("Unable to create rule: %v", err)
}
defer DeleteRule(t, client, rule.ID)
tools.PrintResource(t, rule)
policy, err := CreatePolicy(t, client, rule.ID)
if err != nil {
t.Fatalf("Unable to create policy: %v", err)
}
defer DeletePolicy(t, client, policy.ID)
tools.PrintResource(t, policy)
firewall, err := CreateFirewallOnRouter(t, client, policy.ID, router.ID)
if err != nil {
t.Fatalf("Unable to create firewall: %v", err)
}
defer DeleteFirewall(t, client, firewall.ID)
tools.PrintResource(t, firewall)
router2, err := layer3.CreateExternalRouter(t, client)
if err != nil {
t.Fatalf("Unable to create router: %v", err)
}
defer layer3.DeleteRouter(t, client, router2.ID)
firewallUpdateOpts := firewalls.UpdateOpts{
PolicyID: policy.ID,
Description: "Some firewall description",
}
updateOpts := routerinsertion.UpdateOptsExt{
firewallUpdateOpts,
[]string{router2.ID},
}
_, err = firewalls.Update(client, firewall.ID, updateOpts).Extract()
if err != nil {
t.Fatalf("Unable to update firewall: %v", err)
}
newFirewall, err := firewalls.Get(client, firewall.ID).Extract()
if err != nil {
t.Fatalf("Unable to get firewall: %v", err)
}
tools.PrintResource(t, newFirewall)
}
func TestFirewallCRUDRemoveRouter(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
router, err := layer3.CreateExternalRouter(t, client)
if err != nil {
t.Fatalf("Unable to create router: %v", err)
}
defer layer3.DeleteRouter(t, client, router.ID)
rule, err := CreateRule(t, client)
if err != nil {
t.Fatalf("Unable to create rule: %v", err)
}
defer DeleteRule(t, client, rule.ID)
tools.PrintResource(t, rule)
policy, err := CreatePolicy(t, client, rule.ID)
if err != nil {
t.Fatalf("Unable to create policy: %v", err)
}
defer DeletePolicy(t, client, policy.ID)
tools.PrintResource(t, policy)
firewall, err := CreateFirewallOnRouter(t, client, policy.ID, router.ID)
if err != nil {
t.Fatalf("Unable to create firewall: %v", err)
}
defer DeleteFirewall(t, client, firewall.ID)
tools.PrintResource(t, firewall)
firewallUpdateOpts := firewalls.UpdateOpts{
PolicyID: policy.ID,
Description: "Some firewall description",
}
updateOpts := routerinsertion.UpdateOptsExt{
firewallUpdateOpts,
[]string{},
}
_, err = firewalls.Update(client, firewall.ID, updateOpts).Extract()
if err != nil {
t.Fatalf("Unable to update firewall: %v", err)
}
newFirewall, err := firewalls.Get(client, firewall.ID).Extract()
if err != nil {
t.Fatalf("Unable to get firewall: %v", err)
}
tools.PrintResource(t, newFirewall)
}

View File

@@ -0,0 +1,203 @@
package fwaas
import (
"fmt"
"strconv"
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/routerinsertion"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules"
)
// CreateFirewall will create a Firewaill with a random name and a specified
// policy ID. An error will be returned if the firewall could not be created.
func CreateFirewall(t *testing.T, client *gophercloud.ServiceClient, policyID string) (*firewalls.Firewall, error) {
firewallName := tools.RandomString("TESTACC-", 8)
t.Logf("Attempting to create firewall %s", firewallName)
iTrue := true
createOpts := firewalls.CreateOpts{
Name: firewallName,
PolicyID: policyID,
AdminStateUp: &iTrue,
}
firewall, err := firewalls.Create(client, createOpts).Extract()
if err != nil {
return firewall, err
}
t.Logf("Waiting for firewall to become active.")
if err := WaitForFirewallState(client, firewall.ID, "ACTIVE", 60); err != nil {
return firewall, err
}
t.Logf("Successfully created firewall %s", firewallName)
return firewall, nil
}
// CreateFirewallOnRouter will create a Firewall with a random name and a
// specified policy ID attached to a specified Router. An error will be
// returned if the firewall could not be created.
func CreateFirewallOnRouter(t *testing.T, client *gophercloud.ServiceClient, policyID string, routerID string) (*firewalls.Firewall, error) {
firewallName := tools.RandomString("TESTACC-", 8)
t.Logf("Attempting to create firewall %s", firewallName)
firewallCreateOpts := firewalls.CreateOpts{
Name: firewallName,
PolicyID: policyID,
}
createOpts := routerinsertion.CreateOptsExt{
CreateOptsBuilder: firewallCreateOpts,
RouterIDs: []string{routerID},
}
firewall, err := firewalls.Create(client, createOpts).Extract()
if err != nil {
return firewall, err
}
t.Logf("Waiting for firewall to become active.")
if err := WaitForFirewallState(client, firewall.ID, "ACTIVE", 60); err != nil {
return firewall, err
}
t.Logf("Successfully created firewall %s", firewallName)
return firewall, nil
}
// CreatePolicy will create a Firewall Policy with a random name and given
// rule. An error will be returned if the rule could not be created.
func CreatePolicy(t *testing.T, client *gophercloud.ServiceClient, ruleID string) (*policies.Policy, error) {
policyName := tools.RandomString("TESTACC-", 8)
t.Logf("Attempting to create policy %s", policyName)
createOpts := policies.CreateOpts{
Name: policyName,
Rules: []string{
ruleID,
},
}
policy, err := policies.Create(client, createOpts).Extract()
if err != nil {
return policy, err
}
t.Logf("Successfully created policy %s", policyName)
return policy, nil
}
// CreateRule will create a Firewall Rule with a random source address and
//source port, destination address and port. An error will be returned if
// the rule could not be created.
func CreateRule(t *testing.T, client *gophercloud.ServiceClient) (*rules.Rule, error) {
ruleName := tools.RandomString("TESTACC-", 8)
sourceAddress := fmt.Sprintf("192.168.1.%d", tools.RandomInt(1, 100))
sourcePort := strconv.Itoa(tools.RandomInt(1, 100))
destinationAddress := fmt.Sprintf("192.168.2.%d", tools.RandomInt(1, 100))
destinationPort := strconv.Itoa(tools.RandomInt(1, 100))
t.Logf("Attempting to create rule %s with source %s:%s and destination %s:%s",
ruleName, sourceAddress, sourcePort, destinationAddress, destinationPort)
createOpts := rules.CreateOpts{
Name: ruleName,
Protocol: rules.ProtocolTCP,
Action: "allow",
SourceIPAddress: sourceAddress,
SourcePort: sourcePort,
DestinationIPAddress: destinationAddress,
DestinationPort: destinationPort,
}
rule, err := rules.Create(client, createOpts).Extract()
if err != nil {
return rule, err
}
t.Logf("Rule %s successfully created", ruleName)
return rule, nil
}
// DeleteFirewall will delete a firewall with a specified ID. A fatal error
// will occur if the delete was not successful. This works best when used as
// a deferred function.
func DeleteFirewall(t *testing.T, client *gophercloud.ServiceClient, firewallID string) {
t.Logf("Attempting to delete firewall: %s", firewallID)
err := firewalls.Delete(client, firewallID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete firewall %s: %v", firewallID, err)
}
t.Logf("Waiting for firewall to delete.")
if err := WaitForFirewallState(client, firewallID, "DELETED", 60); err != nil {
t.Logf("Unable to delete firewall: %s", firewallID)
}
t.Logf("Firewall deleted: %s", firewallID)
}
// DeletePolicy will delete a policy with a specified ID. A fatal error will
// occur if the delete was not successful. This works best when used as a
// deferred function.
func DeletePolicy(t *testing.T, client *gophercloud.ServiceClient, policyID string) {
t.Logf("Attempting to delete policy: %s", policyID)
err := policies.Delete(client, policyID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete policy %s: %v", policyID, err)
}
t.Logf("Deleted policy: %s", policyID)
}
// DeleteRule will delete a rule with a specified ID. A fatal error will occur
// if the delete was not successful. This works best when used as a deferred
// function.
func DeleteRule(t *testing.T, client *gophercloud.ServiceClient, ruleID string) {
t.Logf("Attempting to delete rule: %s", ruleID)
err := rules.Delete(client, ruleID).ExtractErr()
if err != nil {
t.Fatalf("Unable to delete rule %s: %v", ruleID, err)
}
t.Logf("Deleted rule: %s", ruleID)
}
// WaitForFirewallState will wait until a firewall reaches a given state.
func WaitForFirewallState(client *gophercloud.ServiceClient, firewallID, status string, secs int) error {
return gophercloud.WaitFor(secs, func() (bool, error) {
current, err := firewalls.Get(client, firewallID).Extract()
if err != nil {
if httpStatus, ok := err.(gophercloud.ErrDefault404); ok {
if httpStatus.Actual == 404 {
if status == "DELETED" {
return true, nil
}
}
}
return false, err
}
if current.Status == status {
return true, nil
}
return false, nil
})
}

View File

@@ -0,0 +1 @@
package fwaas

View File

@@ -0,0 +1,71 @@
// +build acceptance networking fwaas
package fwaas
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
)
func TestPolicyList(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
allPages, err := policies.List(client, nil).AllPages()
if err != nil {
t.Fatalf("Unable to list policies: %v", err)
}
allPolicies, err := policies.ExtractPolicies(allPages)
if err != nil {
t.Fatalf("Unable to extract policies: %v", err)
}
for _, policy := range allPolicies {
tools.PrintResource(t, policy)
}
}
func TestPolicyCRUD(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
rule, err := CreateRule(t, client)
if err != nil {
t.Fatalf("Unable to create rule: %v", err)
}
defer DeleteRule(t, client, rule.ID)
tools.PrintResource(t, rule)
policy, err := CreatePolicy(t, client, rule.ID)
if err != nil {
t.Fatalf("Unable to create policy: %v", err)
}
defer DeletePolicy(t, client, policy.ID)
tools.PrintResource(t, policy)
updateOpts := policies.UpdateOpts{
Description: "Some policy description",
}
_, err = policies.Update(client, policy.ID, updateOpts).Extract()
if err != nil {
t.Fatalf("Unable to update policy: %v", err)
}
newPolicy, err := policies.Get(client, policy.ID).Extract()
if err != nil {
t.Fatalf("Unable to get policy: %v", err)
}
tools.PrintResource(t, newPolicy)
}

View File

@@ -0,0 +1,64 @@
// +build acceptance networking fwaas
package fwaas
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules"
)
func TestRuleList(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
allPages, err := rules.List(client, nil).AllPages()
if err != nil {
t.Fatalf("Unable to list rules: %v", err)
}
allRules, err := rules.ExtractRules(allPages)
if err != nil {
t.Fatalf("Unable to extract rules: %v", err)
}
for _, rule := range allRules {
tools.PrintResource(t, rule)
}
}
func TestRuleCRUD(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
rule, err := CreateRule(t, client)
if err != nil {
t.Fatalf("Unable to create rule: %v", err)
}
defer DeleteRule(t, client, rule.ID)
tools.PrintResource(t, rule)
ruleDescription := "Some rule description"
updateOpts := rules.UpdateOpts{
Description: &ruleDescription,
}
_, err = rules.Update(client, rule.ID, updateOpts).Extract()
if err != nil {
t.Fatalf("Unable to update rule: %v", err)
}
newRule, err := rules.Get(client, rule.ID).Extract()
if err != nil {
t.Fatalf("Unable to get rule: %v", err)
}
tools.PrintResource(t, newRule)
}

View File

@@ -0,0 +1,100 @@
// +build acceptance networking layer3 floatingips
package layer3
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
)
func TestLayer3FloatingIPsList(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
listOpts := floatingips.ListOpts{
Status: "DOWN",
}
allPages, err := floatingips.List(client, listOpts).AllPages()
if err != nil {
t.Fatalf("Unable to list floating IPs: %v", err)
}
allFIPs, err := floatingips.ExtractFloatingIPs(allPages)
if err != nil {
t.Fatalf("Unable to extract floating IPs: %v", err)
}
for _, fip := range allFIPs {
tools.PrintResource(t, fip)
}
}
func TestLayer3FloatingIPsCreateDelete(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
t.Fatalf("Unable to get choices: %v", err)
}
netid, err := networks.IDFromName(client, choices.NetworkName)
if err != nil {
t.Fatalf("Unable to find network id: %v", err)
}
subnet, err := networking.CreateSubnet(t, client, netid)
if err != nil {
t.Fatalf("Unable to create subnet: %v", err)
}
defer networking.DeleteSubnet(t, client, subnet.ID)
router, err := CreateExternalRouter(t, client)
if err != nil {
t.Fatalf("Unable to create router: %v", err)
}
defer DeleteRouter(t, client, router.ID)
port, err := networking.CreatePort(t, client, netid, subnet.ID)
if err != nil {
t.Fatalf("Unable to create port: %v", err)
}
_, err = CreateRouterInterface(t, client, port.ID, router.ID)
if err != nil {
t.Fatalf("Unable to create router interface: %v", err)
}
defer DeleteRouterInterface(t, client, port.ID, router.ID)
fip, err := CreateFloatingIP(t, client, choices.ExternalNetworkID, port.ID)
if err != nil {
t.Fatalf("Unable to create floating IP: %v", err)
}
defer DeleteFloatingIP(t, client, fip.ID)
newFip, err := floatingips.Get(client, fip.ID).Extract()
if err != nil {
t.Fatalf("Unable to get floating ip: %v", err)
}
tools.PrintResource(t, newFip)
// Disassociate the floating IP
updateOpts := floatingips.UpdateOpts{
PortID: nil,
}
newFip, err = floatingips.Update(client, fip.ID, updateOpts).Extract()
if err != nil {
t.Fatalf("Unable to disassociate floating IP: %v", err)
}
}

View File

@@ -0,0 +1,250 @@
package layer3
import (
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
)
// CreateFloatingIP creates a floating IP on a given network and port. An error
// will be returned if the creation failed.
func CreateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, networkID, portID string) (*floatingips.FloatingIP, error) {
t.Logf("Attempting to create floating IP on port: %s", portID)
createOpts := &floatingips.CreateOpts{
FloatingNetworkID: networkID,
PortID: portID,
}
floatingIP, err := floatingips.Create(client, createOpts).Extract()
if err != nil {
return floatingIP, err
}
t.Logf("Created floating IP.")
return floatingIP, err
}
// CreateExternalRouter creates a router on the external network. This requires
// the OS_EXTGW_ID environment variable to be set. An error is returned if the
// creation failed.
func CreateExternalRouter(t *testing.T, client *gophercloud.ServiceClient) (*routers.Router, error) {
var router *routers.Router
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
return router, err
}
routerName := tools.RandomString("TESTACC-", 8)
t.Logf("Attempting to create external router: %s", routerName)
adminStateUp := true
enableSNAT := false
gatewayInfo := routers.GatewayInfo{
NetworkID: choices.ExternalNetworkID,
EnableSNAT: &enableSNAT,
}
createOpts := routers.CreateOpts{
Name: routerName,
AdminStateUp: &adminStateUp,
GatewayInfo: &gatewayInfo,
}
router, err = routers.Create(client, createOpts).Extract()
if err != nil {
return router, err
}
if err := WaitForRouterToCreate(client, router.ID, 60); err != nil {
return router, err
}
t.Logf("Created router: %s", routerName)
return router, nil
}
// CreateRouter creates a router on a specified Network ID. An error will be
// returned if the creation failed.
func CreateRouter(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*routers.Router, error) {
routerName := tools.RandomString("TESTACC-", 8)
t.Logf("Attempting to create router: %s", routerName)
adminStateUp := true
gatewayInfo := routers.GatewayInfo{
NetworkID: networkID,
}
createOpts := routers.CreateOpts{
Name: routerName,
AdminStateUp: &adminStateUp,
GatewayInfo: &gatewayInfo,
}
router, err := routers.Create(client, createOpts).Extract()
if err != nil {
return router, err
}
if err := WaitForRouterToCreate(client, router.ID, 60); err != nil {
return router, err
}
t.Logf("Created router: %s", routerName)
return router, nil
}
// CreateRouterInterface will attach a subnet to a router. An error will be
// returned if the operation fails.
func CreateRouterInterface(t *testing.T, client *gophercloud.ServiceClient, portID, routerID string) (*routers.InterfaceInfo, error) {
t.Logf("Attempting to add port %s to router %s", portID, routerID)
aiOpts := routers.AddInterfaceOpts{
PortID: portID,
}
iface, err := routers.AddInterface(client, routerID, aiOpts).Extract()
if err != nil {
return iface, err
}
if err := WaitForRouterInterfaceToAttach(client, portID, 60); err != nil {
return iface, err
}
t.Logf("Successfully added port %s to router %s", portID, routerID)
return iface, nil
}
// DeleteRouter deletes a router of a specified ID. A fatal error will occur
// if the deletion failed. This works best when used as a deferred function.
func DeleteRouter(t *testing.T, client *gophercloud.ServiceClient, routerID string) {
t.Logf("Attempting to delete router: %s", routerID)
err := routers.Delete(client, routerID).ExtractErr()
if err != nil {
t.Fatalf("Error deleting router: %v", err)
}
if err := WaitForRouterToDelete(client, routerID, 60); err != nil {
t.Fatalf("Error waiting for router to delete: %v", err)
}
t.Logf("Deleted router: %s", routerID)
}
// DeleteRouterInterface will detach a subnet to a router. A fatal error will
// occur if the deletion failed. This works best when used as a deferred
// function.
func DeleteRouterInterface(t *testing.T, client *gophercloud.ServiceClient, portID, routerID string) {
t.Logf("Attempting to detach port %s from router %s", portID, routerID)
riOpts := routers.RemoveInterfaceOpts{
PortID: portID,
}
_, err := routers.RemoveInterface(client, routerID, riOpts).Extract()
if err != nil {
t.Fatalf("Failed to detach port %s from router %s", portID, routerID)
}
if err := WaitForRouterInterfaceToDetach(client, portID, 60); err != nil {
t.Fatalf("Failed to wait for port %s to detach from router %s", portID, routerID)
}
t.Logf("Successfully detached port %s from router %s", portID, routerID)
}
// DeleteFloatingIP deletes a floatingIP of a specified ID. A fatal error will
// occur if the deletion failed. This works best when used as a deferred
// function.
func DeleteFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIPID string) {
t.Logf("Attempting to delete floating IP: %s", floatingIPID)
err := floatingips.Delete(client, floatingIPID).ExtractErr()
if err != nil {
t.Fatalf("Failed to delete floating IP: %v", err)
}
t.Logf("Deleted floating IP: %s", floatingIPID)
}
func WaitForRouterToCreate(client *gophercloud.ServiceClient, routerID string, secs int) error {
return gophercloud.WaitFor(secs, func() (bool, error) {
r, err := routers.Get(client, routerID).Extract()
if err != nil {
return false, err
}
if r.Status == "ACTIVE" {
return true, nil
}
return false, nil
})
}
func WaitForRouterToDelete(client *gophercloud.ServiceClient, routerID string, secs int) error {
return gophercloud.WaitFor(secs, func() (bool, error) {
_, err := routers.Get(client, routerID).Extract()
if err != nil {
if _, ok := err.(gophercloud.ErrDefault404); ok {
return true, nil
}
return false, err
}
return false, nil
})
}
func WaitForRouterInterfaceToAttach(client *gophercloud.ServiceClient, routerInterfaceID string, secs int) error {
return gophercloud.WaitFor(secs, func() (bool, error) {
r, err := ports.Get(client, routerInterfaceID).Extract()
if err != nil {
return false, err
}
if r.Status == "ACTIVE" {
return true, nil
}
return false, nil
})
}
func WaitForRouterInterfaceToDetach(client *gophercloud.ServiceClient, routerInterfaceID string, secs int) error {
return gophercloud.WaitFor(secs, func() (bool, error) {
r, err := ports.Get(client, routerInterfaceID).Extract()
if err != nil {
if _, ok := err.(gophercloud.ErrDefault404); ok {
return true, nil
}
if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok {
if errCode.Actual == 409 {
return false, nil
}
}
return false, err
}
if r.Status == "ACTIVE" {
return true, nil
}
return false, nil
})
}

View File

@@ -0,0 +1,119 @@
// +build acceptance networking layer3 router
package layer3
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
)
func TestLayer3RouterList(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
listOpts := routers.ListOpts{}
allPages, err := routers.List(client, listOpts).AllPages()
if err != nil {
t.Fatalf("Unable to list routers: %v", err)
}
allRouters, err := routers.ExtractRouters(allPages)
if err != nil {
t.Fatalf("Unable to extract routers: %v", err)
}
for _, router := range allRouters {
tools.PrintResource(t, router)
}
}
func TestLayer3ExternalRouterCreateDelete(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
router, err := CreateExternalRouter(t, client)
if err != nil {
t.Fatalf("Unable to create router: %v", err)
}
defer DeleteRouter(t, client, router.ID)
tools.PrintResource(t, router)
newName := tools.RandomString("TESTACC-", 8)
updateOpts := routers.UpdateOpts{
Name: newName,
}
_, err = routers.Update(client, router.ID, updateOpts).Extract()
if err != nil {
t.Fatalf("Unable to update router: %v", err)
}
newRouter, err := routers.Get(client, router.ID).Extract()
if err != nil {
t.Fatalf("Unable to get router: %v", err)
}
tools.PrintResource(t, newRouter)
}
func TestLayer3RouterInterface(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
choices, err := clients.AcceptanceTestChoicesFromEnv()
if err != nil {
t.Fatalf("Unable to get choices: %v", err)
}
netid, err := networks.IDFromName(client, choices.NetworkName)
if err != nil {
t.Fatalf("Unable to find network id: %v", err)
}
subnet, err := networking.CreateSubnet(t, client, netid)
if err != nil {
t.Fatalf("Unable to create subnet: %v", err)
}
defer networking.DeleteSubnet(t, client, subnet.ID)
tools.PrintResource(t, subnet)
router, err := CreateExternalRouter(t, client)
if err != nil {
t.Fatalf("Unable to create router: %v", err)
}
defer DeleteRouter(t, client, router.ID)
aiOpts := routers.AddInterfaceOpts{
SubnetID: subnet.ID,
}
iface, err := routers.AddInterface(client, router.ID, aiOpts).Extract()
if err != nil {
t.Fatalf("Failed to add interface to router: %v", err)
}
tools.PrintResource(t, router)
tools.PrintResource(t, iface)
riOpts := routers.RemoveInterfaceOpts{
SubnetID: subnet.ID,
}
_, err = routers.RemoveInterface(client, router.ID, riOpts).Extract()
if err != nil {
t.Fatalf("Failed to remove interface from router: %v", err)
}
}

View File

@@ -0,0 +1,160 @@
package lbaas
import (
"fmt"
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/members"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/pools"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/vips"
)
// CreateMember will create a load balancer member in a specified pool on a
// random port. An error will be returned if the member could not be created.
func CreateMember(t *testing.T, client *gophercloud.ServiceClient, poolID string) (*members.Member, error) {
protocolPort := tools.RandomInt(100, 1000)
address := tools.RandomInt(2, 200)
t.Logf("Attempting to create member in port %d", protocolPort)
createOpts := members.CreateOpts{
PoolID: poolID,
ProtocolPort: protocolPort,
Address: fmt.Sprintf("192.168.1.%d", address),
}
member, err := members.Create(client, createOpts).Extract()
if err != nil {
return member, err
}
t.Logf("Successfully created member %s", member.ID)
return member, nil
}
// CreateMonitor will create a monitor with a random name for a specific pool.
// An error will be returned if the monitor could not be created.
func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient) (*monitors.Monitor, error) {
t.Logf("Attempting to create monitor.")
createOpts := monitors.CreateOpts{
Type: monitors.TypePING,
Delay: 90,
Timeout: 60,
MaxRetries: 10,
AdminStateUp: gophercloud.Enabled,
}
monitor, err := monitors.Create(client, createOpts).Extract()
if err != nil {
return monitor, err
}
t.Logf("Successfully created monitor %s", monitor.ID)
return monitor, nil
}
// CreatePool will create a pool with a random name. An error will be returned
// if the pool could not be deleted.
func CreatePool(t *testing.T, client *gophercloud.ServiceClient, subnetID string) (*pools.Pool, error) {
poolName := tools.RandomString("TESTACCT-", 8)
t.Logf("Attempting to create pool %s", poolName)
createOpts := pools.CreateOpts{
Name: poolName,
SubnetID: subnetID,
Protocol: pools.ProtocolTCP,
LBMethod: pools.LBMethodRoundRobin,
}
pool, err := pools.Create(client, createOpts).Extract()
if err != nil {
return pool, err
}
t.Logf("Successfully created pool %s", poolName)
return pool, nil
}
// CreateVIP will create a vip with a random name and a random port in a
// specified subnet and pool. An error will be returned if the vip could
// not be created.
func CreateVIP(t *testing.T, client *gophercloud.ServiceClient, subnetID, poolID string) (*vips.VirtualIP, error) {
vipName := tools.RandomString("TESTACCT-", 8)
vipPort := tools.RandomInt(100, 10000)
t.Logf("Attempting to create VIP %s", vipName)
createOpts := vips.CreateOpts{
Name: vipName,
SubnetID: subnetID,
PoolID: poolID,
Protocol: "TCP",
ProtocolPort: vipPort,
}
vip, err := vips.Create(client, createOpts).Extract()
if err != nil {
return vip, err
}
t.Logf("Successfully created vip %s", vipName)
return vip, nil
}
// DeleteMember will delete a specified member. A fatal error will occur if
// the member could not be deleted. This works best when used as a deferred
// function.
func DeleteMember(t *testing.T, client *gophercloud.ServiceClient, memberID string) {
t.Logf("Attempting to delete member %s", memberID)
if err := members.Delete(client, memberID).ExtractErr(); err != nil {
t.Fatalf("Unable to delete member: %v", err)
}
t.Logf("Successfully deleted member %s", memberID)
}
// DeleteMonitor will delete a specified monitor. A fatal error will occur if
// the monitor could not be deleted. This works best when used as a deferred
// function.
func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, monitorID string) {
t.Logf("Attempting to delete monitor %s", monitorID)
if err := monitors.Delete(client, monitorID).ExtractErr(); err != nil {
t.Fatalf("Unable to delete monitor: %v", err)
}
t.Logf("Successfully deleted monitor %s", monitorID)
}
// DeletePool will delete a specified pool. A fatal error will occur if the
// pool could not be deleted. This works best when used as a deferred function.
func DeletePool(t *testing.T, client *gophercloud.ServiceClient, poolID string) {
t.Logf("Attempting to delete pool %s", poolID)
if err := pools.Delete(client, poolID).ExtractErr(); err != nil {
t.Fatalf("Unable to delete pool: %v", err)
}
t.Logf("Successfully deleted pool %s", poolID)
}
// DeleteVIP will delete a specified vip. A fatal error will occur if the vip
// could not be deleted. This works best when used as a deferred function.
func DeleteVIP(t *testing.T, client *gophercloud.ServiceClient, vipID string) {
t.Logf("Attempting to delete vip %s", vipID)
if err := vips.Delete(client, vipID).ExtractErr(); err != nil {
t.Fatalf("Unable to delete vip: %v", err)
}
t.Logf("Successfully deleted vip %s", vipID)
}

View File

@@ -0,0 +1,83 @@
// +build acceptance networking lbaas member
package lbaas
import (
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/clients"
networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/members"
)
func TestMembersList(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
allPages, err := members.List(client, members.ListOpts{}).AllPages()
if err != nil {
t.Fatalf("Unable to list members: %v", err)
}
allMembers, err := members.ExtractMembers(allPages)
if err != nil {
t.Fatalf("Unable to extract members: %v", err)
}
for _, member := range allMembers {
tools.PrintResource(t, member)
}
}
func TestMembersCRUD(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
network, err := networking.CreateNetwork(t, client)
if err != nil {
t.Fatalf("Unable to create network: %v", err)
}
defer networking.DeleteNetwork(t, client, network.ID)
subnet, err := networking.CreateSubnet(t, client, network.ID)
if err != nil {
t.Fatalf("Unable to create subnet: %v", err)
}
defer networking.DeleteSubnet(t, client, subnet.ID)
pool, err := CreatePool(t, client, subnet.ID)
if err != nil {
t.Fatalf("Unable to create pool: %v", err)
}
defer DeletePool(t, client, pool.ID)
member, err := CreateMember(t, client, pool.ID)
if err != nil {
t.Fatalf("Unable to create member: %v", err)
}
defer DeleteMember(t, client, member.ID)
tools.PrintResource(t, member)
updateOpts := members.UpdateOpts{
AdminStateUp: gophercloud.Enabled,
}
_, err = members.Update(client, member.ID, updateOpts).Extract()
if err != nil {
t.Fatalf("Unable to update member: %v")
}
newMember, err := members.Get(client, member.ID).Extract()
if err != nil {
t.Fatalf("Unable to get member: %v")
}
tools.PrintResource(t, newMember)
}

View File

@@ -0,0 +1,63 @@
// +build acceptance networking lbaas monitors
package lbaas
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
)
func TestMonitorsList(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
allPages, err := monitors.List(client, monitors.ListOpts{}).AllPages()
if err != nil {
t.Fatalf("Unable to list monitors: %v", err)
}
allMonitors, err := monitors.ExtractMonitors(allPages)
if err != nil {
t.Fatalf("Unable to extract monitors: %v", err)
}
for _, monitor := range allMonitors {
tools.PrintResource(t, monitor)
}
}
func TestMonitorsCRUD(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
monitor, err := CreateMonitor(t, client)
if err != nil {
t.Fatalf("Unable to create monitor: %v", err)
}
defer DeleteMonitor(t, client, monitor.ID)
tools.PrintResource(t, monitor)
updateOpts := monitors.UpdateOpts{
Delay: 999,
}
_, err = monitors.Update(client, monitor.ID, updateOpts).Extract()
if err != nil {
t.Fatalf("Unable to update monitor: %v")
}
newMonitor, err := monitors.Get(client, monitor.ID).Extract()
if err != nil {
t.Fatalf("Unable to get monitor: %v")
}
tools.PrintResource(t, newMonitor)
}

View File

@@ -0,0 +1 @@
package lbaas

View File

@@ -0,0 +1,118 @@
// +build acceptance networking lbaas pool
package lbaas
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/pools"
)
func TestPoolsList(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
allPages, err := pools.List(client, pools.ListOpts{}).AllPages()
if err != nil {
t.Fatalf("Unable to list pools: %v", err)
}
allPools, err := pools.ExtractPools(allPages)
if err != nil {
t.Fatalf("Unable to extract pools: %v", err)
}
for _, pool := range allPools {
tools.PrintResource(t, pool)
}
}
func TestPoolsCRUD(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
network, err := networking.CreateNetwork(t, client)
if err != nil {
t.Fatalf("Unable to create network: %v", err)
}
defer networking.DeleteNetwork(t, client, network.ID)
subnet, err := networking.CreateSubnet(t, client, network.ID)
if err != nil {
t.Fatalf("Unable to create subnet: %v", err)
}
defer networking.DeleteSubnet(t, client, subnet.ID)
pool, err := CreatePool(t, client, subnet.ID)
if err != nil {
t.Fatalf("Unable to create pool: %v", err)
}
defer DeletePool(t, client, pool.ID)
tools.PrintResource(t, pool)
updateOpts := pools.UpdateOpts{
LBMethod: pools.LBMethodLeastConnections,
}
_, err = pools.Update(client, pool.ID, updateOpts).Extract()
if err != nil {
t.Fatalf("Unable to update pool: %v")
}
newPool, err := pools.Get(client, pool.ID).Extract()
if err != nil {
t.Fatalf("Unable to get pool: %v")
}
tools.PrintResource(t, newPool)
}
func TestPoolsMonitors(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
network, err := networking.CreateNetwork(t, client)
if err != nil {
t.Fatalf("Unable to create network: %v", err)
}
defer networking.DeleteNetwork(t, client, network.ID)
subnet, err := networking.CreateSubnet(t, client, network.ID)
if err != nil {
t.Fatalf("Unable to create subnet: %v", err)
}
defer networking.DeleteSubnet(t, client, subnet.ID)
pool, err := CreatePool(t, client, subnet.ID)
if err != nil {
t.Fatalf("Unable to create pool: %v", err)
}
defer DeletePool(t, client, pool.ID)
monitor, err := CreateMonitor(t, client)
if err != nil {
t.Fatalf("Unable to create monitor: %v", err)
}
defer DeleteMonitor(t, client, monitor.ID)
t.Logf("Associating monitor %s with pool %s", monitor.ID, pool.ID)
if res := pools.AssociateMonitor(client, pool.ID, monitor.ID); res.Err != nil {
t.Fatalf("Unable to associate monitor to pool")
}
t.Logf("Disassociating monitor %s with pool %s", monitor.ID, pool.ID)
if res := pools.DisassociateMonitor(client, pool.ID, monitor.ID); res.Err != nil {
t.Fatalf("Unable to disassociate monitor from pool")
}
}

View File

@@ -0,0 +1,83 @@
// +build acceptance networking lbaas vip
package lbaas
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/vips"
)
func TestVIPsList(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
allPages, err := vips.List(client, vips.ListOpts{}).AllPages()
if err != nil {
t.Fatalf("Unable to list vips: %v", err)
}
allVIPs, err := vips.ExtractVIPs(allPages)
if err != nil {
t.Fatalf("Unable to extract vips: %v", err)
}
for _, vip := range allVIPs {
tools.PrintResource(t, vip)
}
}
func TestVIPsCRUD(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
network, err := networking.CreateNetwork(t, client)
if err != nil {
t.Fatalf("Unable to create network: %v", err)
}
defer networking.DeleteNetwork(t, client, network.ID)
subnet, err := networking.CreateSubnet(t, client, network.ID)
if err != nil {
t.Fatalf("Unable to create subnet: %v", err)
}
defer networking.DeleteSubnet(t, client, subnet.ID)
pool, err := CreatePool(t, client, subnet.ID)
if err != nil {
t.Fatalf("Unable to create pool: %v", err)
}
defer DeletePool(t, client, pool.ID)
vip, err := CreateVIP(t, client, subnet.ID, pool.ID)
if err != nil {
t.Fatalf("Unable to create vip: %v", err)
}
defer DeleteVIP(t, client, vip.ID)
tools.PrintResource(t, vip)
connLimit := 100
updateOpts := vips.UpdateOpts{
ConnLimit: &connLimit,
}
_, err = vips.Update(client, vip.ID, updateOpts).Extract()
if err != nil {
t.Fatalf("Unable to update vip: %v")
}
newVIP, err := vips.Get(client, vip.ID).Extract()
if err != nil {
t.Fatalf("Unable to get vip: %v")
}
tools.PrintResource(t, newVIP)
}

View File

@@ -0,0 +1,282 @@
package lbaas_v2
import (
"fmt"
"strings"
"testing"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
)
const loadbalancerActiveTimeoutSeconds = 300
const loadbalancerDeleteTimeoutSeconds = 300
// CreateListener will create a listener for a given load balancer on a random
// port with a random name. An error will be returned if the listener could not
// be created.
func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*listeners.Listener, error) {
listenerName := tools.RandomString("TESTACCT-", 8)
listenerPort := tools.RandomInt(1, 100)
t.Logf("Attempting to create listener %s on port %d", listenerName, listenerPort)
createOpts := listeners.CreateOpts{
Name: listenerName,
LoadbalancerID: lb.ID,
Protocol: "TCP",
ProtocolPort: listenerPort,
}
listener, err := listeners.Create(client, createOpts).Extract()
if err != nil {
return listener, err
}
t.Logf("Successfully created listener %s", listenerName)
if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
return listener, fmt.Errorf("Timed out waiting for loadbalancer to become active")
}
return listener, nil
}
// CreateLoadBalancer will create a load balancer with a random name on a given
// subnet. An error will be returned if the loadbalancer could not be created.
func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetID string) (*loadbalancers.LoadBalancer, error) {
lbName := tools.RandomString("TESTACCT-", 8)
t.Logf("Attempting to create loadbalancer %s on subnet %s", lbName, subnetID)
createOpts := loadbalancers.CreateOpts{
Name: lbName,
VipSubnetID: subnetID,
AdminStateUp: gophercloud.Enabled,
}
lb, err := loadbalancers.Create(client, createOpts).Extract()
if err != nil {
return lb, err
}
t.Logf("Successfully created loadbalancer %s on subnet %s", lbName, subnetID)
t.Logf("Waiting for loadbalancer %s to become active", lbName)
if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
return lb, err
}
t.Logf("LoadBalancer %s is active", lbName)
return lb, nil
}
// CreateMember will create a member with a random name, port, address, and
// weight. An error will be returned if the member could not be created.
func CreateMember(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer, pool *pools.Pool, subnetID, subnetCIDR string) (*pools.Member, error) {
memberName := tools.RandomString("TESTACCT-", 8)
memberPort := tools.RandomInt(100, 1000)
memberWeight := tools.RandomInt(1, 10)
cidrParts := strings.Split(subnetCIDR, "/")
subnetParts := strings.Split(cidrParts[0], ".")
memberAddress := fmt.Sprintf("%s.%s.%s.%d", subnetParts[0], subnetParts[1], subnetParts[2], tools.RandomInt(10, 100))
t.Logf("Attempting to create member %s", memberName)
createOpts := pools.CreateMemberOpts{
Name: memberName,
ProtocolPort: memberPort,
Weight: memberWeight,
Address: memberAddress,
SubnetID: subnetID,
}
t.Logf("Member create opts: %#v", createOpts)
member, err := pools.CreateMember(client, pool.ID, createOpts).Extract()
if err != nil {
return member, err
}
t.Logf("Successfully created member %s", memberName)
if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
return member, fmt.Errorf("Timed out waiting for loadbalancer to become active")
}
return member, nil
}
// CreateMonitor will create a monitor with a random name for a specific pool.
// An error will be returned if the monitor could not be created.
func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer, pool *pools.Pool) (*monitors.Monitor, error) {
monitorName := tools.RandomString("TESTACCT-", 8)
t.Logf("Attempting to create monitor %s", monitorName)
createOpts := monitors.CreateOpts{
PoolID: pool.ID,
Name: monitorName,
Delay: 10,
Timeout: 5,
MaxRetries: 5,
Type: "PING",
}
monitor, err := monitors.Create(client, createOpts).Extract()
if err != nil {
return monitor, err
}
t.Logf("Successfully created monitor: %s", monitorName)
if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
return monitor, fmt.Errorf("Timed out waiting for loadbalancer to become active")
}
return monitor, nil
}
// CreatePool will create a pool with a random name with a specified listener
// and loadbalancer. An error will be returned if the pool could not be
// created.
func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*pools.Pool, error) {
poolName := tools.RandomString("TESTACCT-", 8)
t.Logf("Attempting to create pool %s", poolName)
createOpts := pools.CreateOpts{
Name: poolName,
Protocol: pools.ProtocolTCP,
LoadbalancerID: lb.ID,
LBMethod: pools.LBMethodLeastConnections,
}
pool, err := pools.Create(client, createOpts).Extract()
if err != nil {
return pool, err
}
t.Logf("Successfully created pool %s", poolName)
if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
return pool, fmt.Errorf("Timed out waiting for loadbalancer to become active")
}
return pool, nil
}
// DeleteListener will delete a specified listener. A fatal error will occur if
// the listener could not be deleted. This works best when used as a deferred
// function.
func DeleteListener(t *testing.T, client *gophercloud.ServiceClient, lbID, listenerID string) {
t.Logf("Attempting to delete listener %s", listenerID)
if err := listeners.Delete(client, listenerID).ExtractErr(); err != nil {
t.Fatalf("Unable to delete listener: %v", err)
}
if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
t.Fatalf("Timed out waiting for loadbalancer to become active")
}
t.Logf("Successfully deleted listener %s", listenerID)
}
// DeleteMember will delete a specified member. A fatal error will occur if the
// member could not be deleted. This works best when used as a deferred
// function.
func DeleteMember(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID, memberID string) {
t.Logf("Attempting to delete member %s", memberID)
if err := pools.DeleteMember(client, poolID, memberID).ExtractErr(); err != nil {
t.Fatalf("Unable to delete member: %s", memberID)
}
if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
t.Fatalf("Timed out waiting for loadbalancer to become active")
}
t.Logf("Successfully deleted member %s", memberID)
}
// DeleteLoadBalancer will delete a specified loadbalancer. A fatal error will
// occur if the loadbalancer could not be deleted. This works best when used
// as a deferred function.
func DeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, lbID string) {
t.Logf("Attempting to delete loadbalancer %s", lbID)
if err := loadbalancers.Delete(client, lbID).ExtractErr(); err != nil {
t.Fatalf("Unable to delete loadbalancer: %v", err)
}
t.Logf("Waiting for loadbalancer %s to delete", lbID)
if err := WaitForLoadBalancerState(client, lbID, "DELETED", loadbalancerActiveTimeoutSeconds); err != nil {
t.Fatalf("Loadbalancer did not delete in time.")
}
t.Logf("Successfully deleted loadbalancer %s", lbID)
}
// DeleteMonitor will delete a specified monitor. A fatal error will occur if
// the monitor could not be deleted. This works best when used as a deferred
// function.
func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID, monitorID string) {
t.Logf("Attempting to delete monitor %s", monitorID)
if err := monitors.Delete(client, monitorID).ExtractErr(); err != nil {
t.Fatalf("Unable to delete monitor: %v", err)
}
if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
t.Fatalf("Timed out waiting for loadbalancer to become active")
}
t.Logf("Successfully deleted monitor %s", monitorID)
}
// DeletePool will delete a specified pool. A fatal error will occur if the
// pool could not be deleted. This works best when used as a deferred function.
func DeletePool(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID string) {
t.Logf("Attempting to delete pool %s", poolID)
if err := pools.Delete(client, poolID).ExtractErr(); err != nil {
t.Fatalf("Unable to delete pool: %v", err)
}
if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil {
t.Fatalf("Timed out waiting for loadbalancer to become active")
}
t.Logf("Successfully deleted pool %s", poolID)
}
// WaitForLoadBalancerState will wait until a loadbalancer reaches a given state.
func WaitForLoadBalancerState(client *gophercloud.ServiceClient, lbID, status string, secs int) error {
return gophercloud.WaitFor(secs, func() (bool, error) {
current, err := loadbalancers.Get(client, lbID).Extract()
if err != nil {
if httpStatus, ok := err.(gophercloud.ErrDefault404); ok {
if httpStatus.Actual == 404 {
if status == "DELETED" {
return true, nil
}
}
}
return false, err
}
if current.ProvisioningStatus == status {
return true, nil
}
return false, nil
})
}

View File

@@ -0,0 +1,32 @@
// +build acceptance networking lbaas_v2 listeners
package lbaas_v2
import (
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
)
func TestListenersList(t *testing.T) {
client, err := clients.NewNetworkV2Client()
if err != nil {
t.Fatalf("Unable to create a network client: %v", err)
}
allPages, err := listeners.List(client, nil).AllPages()
if err != nil {
t.Fatalf("Unable to list listeners: %v", err)
}
allListeners, err := listeners.ExtractListeners(allPages)
if err != nil {
t.Fatalf("Unable to extract listeners: %v", err)
}
for _, listener := range allListeners {
tools.PrintResource(t, listener)
}
}

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