mirror of https://github.com/docker/docs.git
Add dependencies for OpenStack driver
Signed-off-by: Guillaume Giamarchi <guillaume.giamarchi@gmail.com>
This commit is contained in:
parent
c2f1fea9dc
commit
1785869490
|
@ -118,6 +118,20 @@
|
|||
"ImportPath": "github.com/smartystreets/go-aws-auth",
|
||||
"Rev": "1f0db8c0ee6362470abe06a94e3385927ed72a4b"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/mitchellh/mapstructure",
|
||||
"Rev": "740c764bc6149d3f1806231418adb9f52c11bcbf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/racker/perigee",
|
||||
"Comment": "v0.0.0-18-g0c00cb0",
|
||||
"Rev": "0c00cb0a026b71034ebc8205263c77dad3577db5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rackspace/gophercloud",
|
||||
"Comment": "v1.0.0-232-g2e7ab37",
|
||||
"Rev": "2e7ab378257b8723e02cbceac7410be4db286436"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/tent/http-link-go",
|
||||
"Rev": "ac974c61c2f990f4115b119354b5e0b47550e888"
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Mitchell Hashimoto
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,46 @@
|
|||
# mapstructure
|
||||
|
||||
mapstructure is a Go library for decoding generic map values to structures
|
||||
and vice versa, while providing helpful error handling.
|
||||
|
||||
This library is most useful when decoding values from some data stream (JSON,
|
||||
Gob, etc.) where you don't _quite_ know the structure of the underlying data
|
||||
until you read a part of it. You can therefore read a `map[string]interface{}`
|
||||
and use this library to decode it into the proper underlying native Go
|
||||
structure.
|
||||
|
||||
## Installation
|
||||
|
||||
Standard `go get`:
|
||||
|
||||
```
|
||||
$ go get github.com/mitchellh/mapstructure
|
||||
```
|
||||
|
||||
## Usage & Example
|
||||
|
||||
For usage and examples see the [Godoc](http://godoc.org/github.com/mitchellh/mapstructure).
|
||||
|
||||
The `Decode` function has examples associated with it there.
|
||||
|
||||
## But Why?!
|
||||
|
||||
Go offers fantastic standard libraries for decoding formats such as JSON.
|
||||
The standard method is to have a struct pre-created, and populate that struct
|
||||
from the bytes of the encoded format. This is great, but the problem is if
|
||||
you have configuration or an encoding that changes slightly depending on
|
||||
specific fields. For example, consider this JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "person",
|
||||
"name": "Mitchell"
|
||||
}
|
||||
```
|
||||
|
||||
Perhaps we can't populate a specific structure without first reading
|
||||
the "type" field from the JSON. We could always do two passes over the
|
||||
decoding of the JSON (reading the "type" first, and the rest later).
|
||||
However, it is much simpler to just decode this into a `map[string]interface{}`
|
||||
structure, read the "type" key, then use something like this library
|
||||
to decode it into the proper structure.
|
84
Godeps/_workspace/src/github.com/mitchellh/mapstructure/decode_hooks.go
generated
vendored
Normal file
84
Godeps/_workspace/src/github.com/mitchellh/mapstructure/decode_hooks.go
generated
vendored
Normal file
|
@ -0,0 +1,84 @@
|
|||
package mapstructure
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ComposeDecodeHookFunc creates a single DecodeHookFunc that
|
||||
// automatically composes multiple DecodeHookFuncs.
|
||||
//
|
||||
// The composed funcs are called in order, with the result of the
|
||||
// previous transformation.
|
||||
func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
|
||||
return func(
|
||||
f reflect.Kind,
|
||||
t reflect.Kind,
|
||||
data interface{}) (interface{}, error) {
|
||||
var err error
|
||||
for _, f1 := range fs {
|
||||
data, err = f1(f, t, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Modify the from kind to be correct with the new data
|
||||
f = getKind(reflect.ValueOf(data))
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
// StringToSliceHookFunc returns a DecodeHookFunc that converts
|
||||
// string to []string by splitting on the given sep.
|
||||
func StringToSliceHookFunc(sep string) DecodeHookFunc {
|
||||
return func(
|
||||
f reflect.Kind,
|
||||
t reflect.Kind,
|
||||
data interface{}) (interface{}, error) {
|
||||
if f != reflect.String || t != reflect.Slice {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
raw := data.(string)
|
||||
if raw == "" {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
return strings.Split(raw, sep), nil
|
||||
}
|
||||
}
|
||||
|
||||
func WeaklyTypedHook(
|
||||
f reflect.Kind,
|
||||
t reflect.Kind,
|
||||
data interface{}) (interface{}, error) {
|
||||
dataVal := reflect.ValueOf(data)
|
||||
switch t {
|
||||
case reflect.String:
|
||||
switch f {
|
||||
case reflect.Bool:
|
||||
if dataVal.Bool() {
|
||||
return "1", nil
|
||||
} else {
|
||||
return "0", nil
|
||||
}
|
||||
case reflect.Float32:
|
||||
return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil
|
||||
case reflect.Int:
|
||||
return strconv.FormatInt(dataVal.Int(), 10), nil
|
||||
case reflect.Slice:
|
||||
dataType := dataVal.Type()
|
||||
elemKind := dataType.Elem().Kind()
|
||||
if elemKind == reflect.Uint8 {
|
||||
return string(dataVal.Interface().([]uint8)), nil
|
||||
}
|
||||
case reflect.Uint:
|
||||
return strconv.FormatUint(dataVal.Uint(), 10), nil
|
||||
}
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
191
Godeps/_workspace/src/github.com/mitchellh/mapstructure/decode_hooks_test.go
generated
vendored
Normal file
191
Godeps/_workspace/src/github.com/mitchellh/mapstructure/decode_hooks_test.go
generated
vendored
Normal file
|
@ -0,0 +1,191 @@
|
|||
package mapstructure
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestComposeDecodeHookFunc(t *testing.T) {
|
||||
f1 := func(
|
||||
f reflect.Kind,
|
||||
t reflect.Kind,
|
||||
data interface{}) (interface{}, error) {
|
||||
return data.(string) + "foo", nil
|
||||
}
|
||||
|
||||
f2 := func(
|
||||
f reflect.Kind,
|
||||
t reflect.Kind,
|
||||
data interface{}) (interface{}, error) {
|
||||
return data.(string) + "bar", nil
|
||||
}
|
||||
|
||||
f := ComposeDecodeHookFunc(f1, f2)
|
||||
|
||||
result, err := f(reflect.String, reflect.Slice, "")
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
}
|
||||
if result.(string) != "foobar" {
|
||||
t.Fatalf("bad: %#v", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestComposeDecodeHookFunc_err(t *testing.T) {
|
||||
f1 := func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) {
|
||||
return nil, errors.New("foo")
|
||||
}
|
||||
|
||||
f2 := func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) {
|
||||
panic("NOPE")
|
||||
}
|
||||
|
||||
f := ComposeDecodeHookFunc(f1, f2)
|
||||
|
||||
_, err := f(reflect.String, reflect.Slice, 42)
|
||||
if err.Error() != "foo" {
|
||||
t.Fatalf("bad: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestComposeDecodeHookFunc_kinds(t *testing.T) {
|
||||
var f2From reflect.Kind
|
||||
|
||||
f1 := func(
|
||||
f reflect.Kind,
|
||||
t reflect.Kind,
|
||||
data interface{}) (interface{}, error) {
|
||||
return int(42), nil
|
||||
}
|
||||
|
||||
f2 := func(
|
||||
f reflect.Kind,
|
||||
t reflect.Kind,
|
||||
data interface{}) (interface{}, error) {
|
||||
f2From = f
|
||||
return data, nil
|
||||
}
|
||||
|
||||
f := ComposeDecodeHookFunc(f1, f2)
|
||||
|
||||
_, err := f(reflect.String, reflect.Slice, "")
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
}
|
||||
if f2From != reflect.Int {
|
||||
t.Fatalf("bad: %#v", f2From)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringToSliceHookFunc(t *testing.T) {
|
||||
f := StringToSliceHookFunc(",")
|
||||
|
||||
cases := []struct {
|
||||
f, t reflect.Kind
|
||||
data interface{}
|
||||
result interface{}
|
||||
err bool
|
||||
}{
|
||||
{reflect.Slice, reflect.Slice, 42, 42, false},
|
||||
{reflect.String, reflect.String, 42, 42, false},
|
||||
{
|
||||
reflect.String,
|
||||
reflect.Slice,
|
||||
"foo,bar,baz",
|
||||
[]string{"foo", "bar", "baz"},
|
||||
false,
|
||||
},
|
||||
{
|
||||
reflect.String,
|
||||
reflect.Slice,
|
||||
"",
|
||||
[]string{},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
actual, err := f(tc.f, tc.t, tc.data)
|
||||
if tc.err != (err != nil) {
|
||||
t.Fatalf("case %d: expected err %#v", i, tc.err)
|
||||
}
|
||||
if !reflect.DeepEqual(actual, tc.result) {
|
||||
t.Fatalf(
|
||||
"case %d: expected %#v, got %#v",
|
||||
i, tc.result, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWeaklyTypedHook(t *testing.T) {
|
||||
var f DecodeHookFunc = WeaklyTypedHook
|
||||
|
||||
cases := []struct {
|
||||
f, t reflect.Kind
|
||||
data interface{}
|
||||
result interface{}
|
||||
err bool
|
||||
}{
|
||||
// TO STRING
|
||||
{
|
||||
reflect.Bool,
|
||||
reflect.String,
|
||||
false,
|
||||
"0",
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
reflect.Bool,
|
||||
reflect.String,
|
||||
true,
|
||||
"1",
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
reflect.Float32,
|
||||
reflect.String,
|
||||
float32(7),
|
||||
"7",
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
reflect.Int,
|
||||
reflect.String,
|
||||
int(7),
|
||||
"7",
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
reflect.Slice,
|
||||
reflect.String,
|
||||
[]uint8("foo"),
|
||||
"foo",
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
reflect.Uint,
|
||||
reflect.String,
|
||||
uint(7),
|
||||
"7",
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
actual, err := f(tc.f, tc.t, tc.data)
|
||||
if tc.err != (err != nil) {
|
||||
t.Fatalf("case %d: expected err %#v", i, tc.err)
|
||||
}
|
||||
if !reflect.DeepEqual(actual, tc.result) {
|
||||
t.Fatalf(
|
||||
"case %d: expected %#v, got %#v",
|
||||
i, tc.result, actual)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package mapstructure
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Error implements the error interface and can represents multiple
|
||||
// errors that occur in the course of a single decode.
|
||||
type Error struct {
|
||||
Errors []string
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
points := make([]string, len(e.Errors))
|
||||
for i, err := range e.Errors {
|
||||
points[i] = fmt.Sprintf("* %s", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"%d error(s) decoding:\n\n%s",
|
||||
len(e.Errors), strings.Join(points, "\n"))
|
||||
}
|
||||
|
||||
func appendErrors(errors []string, err error) []string {
|
||||
switch e := err.(type) {
|
||||
case *Error:
|
||||
return append(errors, e.Errors...)
|
||||
default:
|
||||
return append(errors, e.Error())
|
||||
}
|
||||
}
|
704
Godeps/_workspace/src/github.com/mitchellh/mapstructure/mapstructure.go
generated
vendored
Normal file
704
Godeps/_workspace/src/github.com/mitchellh/mapstructure/mapstructure.go
generated
vendored
Normal file
|
@ -0,0 +1,704 @@
|
|||
// The mapstructure package exposes functionality to convert an
|
||||
// abitrary map[string]interface{} into a native Go structure.
|
||||
//
|
||||
// The Go structure can be arbitrarily complex, containing slices,
|
||||
// other structs, etc. and the decoder will properly decode nested
|
||||
// maps and so on into the proper structures in the native Go struct.
|
||||
// See the examples to see what the decoder is capable of.
|
||||
package mapstructure
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// DecodeHookFunc is the callback function that can be used for
|
||||
// data transformations. See "DecodeHook" in the DecoderConfig
|
||||
// struct.
|
||||
type DecodeHookFunc func(
|
||||
from reflect.Kind,
|
||||
to reflect.Kind,
|
||||
data interface{}) (interface{}, error)
|
||||
|
||||
// DecoderConfig is the configuration that is used to create a new decoder
|
||||
// and allows customization of various aspects of decoding.
|
||||
type DecoderConfig struct {
|
||||
// DecodeHook, if set, will be called before any decoding and any
|
||||
// type conversion (if WeaklyTypedInput is on). This lets you modify
|
||||
// the values before they're set down onto the resulting struct.
|
||||
//
|
||||
// If an error is returned, the entire decode will fail with that
|
||||
// error.
|
||||
DecodeHook DecodeHookFunc
|
||||
|
||||
// If ErrorUnused is true, then it is an error for there to exist
|
||||
// keys in the original map that were unused in the decoding process
|
||||
// (extra keys).
|
||||
ErrorUnused bool
|
||||
|
||||
// If WeaklyTypedInput is true, the decoder will make the following
|
||||
// "weak" conversions:
|
||||
//
|
||||
// - bools to string (true = "1", false = "0")
|
||||
// - numbers to string (base 10)
|
||||
// - bools to int/uint (true = 1, false = 0)
|
||||
// - strings to int/uint (base implied by prefix)
|
||||
// - int to bool (true if value != 0)
|
||||
// - string to bool (accepts: 1, t, T, TRUE, true, True, 0, f, F,
|
||||
// FALSE, false, False. Anything else is an error)
|
||||
// - empty array = empty map and vice versa
|
||||
//
|
||||
WeaklyTypedInput bool
|
||||
|
||||
// Metadata is the struct that will contain extra metadata about
|
||||
// the decoding. If this is nil, then no metadata will be tracked.
|
||||
Metadata *Metadata
|
||||
|
||||
// Result is a pointer to the struct that will contain the decoded
|
||||
// value.
|
||||
Result interface{}
|
||||
|
||||
// The tag name that mapstructure reads for field names. This
|
||||
// defaults to "mapstructure"
|
||||
TagName string
|
||||
}
|
||||
|
||||
// A Decoder takes a raw interface value and turns it into structured
|
||||
// data, keeping track of rich error information along the way in case
|
||||
// anything goes wrong. Unlike the basic top-level Decode method, you can
|
||||
// more finely control how the Decoder behaves using the DecoderConfig
|
||||
// structure. The top-level Decode method is just a convenience that sets
|
||||
// up the most basic Decoder.
|
||||
type Decoder struct {
|
||||
config *DecoderConfig
|
||||
}
|
||||
|
||||
// Metadata contains information about decoding a structure that
|
||||
// is tedious or difficult to get otherwise.
|
||||
type Metadata struct {
|
||||
// Keys are the keys of the structure which were successfully decoded
|
||||
Keys []string
|
||||
|
||||
// Unused is a slice of keys that were found in the raw value but
|
||||
// weren't decoded since there was no matching field in the result interface
|
||||
Unused []string
|
||||
}
|
||||
|
||||
// Decode takes a map and uses reflection to convert it into the
|
||||
// given Go native structure. val must be a pointer to a struct.
|
||||
func Decode(m interface{}, rawVal interface{}) error {
|
||||
config := &DecoderConfig{
|
||||
Metadata: nil,
|
||||
Result: rawVal,
|
||||
}
|
||||
|
||||
decoder, err := NewDecoder(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return decoder.Decode(m)
|
||||
}
|
||||
|
||||
// WeakDecode is the same as Decode but is shorthand to enable
|
||||
// WeaklyTypedInput. See DecoderConfig for more info.
|
||||
func WeakDecode(input, output interface{}) error {
|
||||
config := &DecoderConfig{
|
||||
Metadata: nil,
|
||||
Result: output,
|
||||
WeaklyTypedInput: true,
|
||||
}
|
||||
|
||||
decoder, err := NewDecoder(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return decoder.Decode(input)
|
||||
}
|
||||
|
||||
// NewDecoder returns a new decoder for the given configuration. Once
|
||||
// a decoder has been returned, the same configuration must not be used
|
||||
// again.
|
||||
func NewDecoder(config *DecoderConfig) (*Decoder, error) {
|
||||
val := reflect.ValueOf(config.Result)
|
||||
if val.Kind() != reflect.Ptr {
|
||||
return nil, errors.New("result must be a pointer")
|
||||
}
|
||||
|
||||
val = val.Elem()
|
||||
if !val.CanAddr() {
|
||||
return nil, errors.New("result must be addressable (a pointer)")
|
||||
}
|
||||
|
||||
if config.Metadata != nil {
|
||||
if config.Metadata.Keys == nil {
|
||||
config.Metadata.Keys = make([]string, 0)
|
||||
}
|
||||
|
||||
if config.Metadata.Unused == nil {
|
||||
config.Metadata.Unused = make([]string, 0)
|
||||
}
|
||||
}
|
||||
|
||||
if config.TagName == "" {
|
||||
config.TagName = "mapstructure"
|
||||
}
|
||||
|
||||
result := &Decoder{
|
||||
config: config,
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Decode decodes the given raw interface to the target pointer specified
|
||||
// by the configuration.
|
||||
func (d *Decoder) Decode(raw interface{}) error {
|
||||
return d.decode("", raw, reflect.ValueOf(d.config.Result).Elem())
|
||||
}
|
||||
|
||||
// Decodes an unknown data type into a specific reflection value.
|
||||
func (d *Decoder) decode(name string, data interface{}, val reflect.Value) error {
|
||||
if data == nil {
|
||||
// If the data is nil, then we don't set anything.
|
||||
return nil
|
||||
}
|
||||
|
||||
dataVal := reflect.ValueOf(data)
|
||||
if !dataVal.IsValid() {
|
||||
// If the data value is invalid, then we just set the value
|
||||
// to be the zero value.
|
||||
val.Set(reflect.Zero(val.Type()))
|
||||
return nil
|
||||
}
|
||||
|
||||
if d.config.DecodeHook != nil {
|
||||
// We have a DecodeHook, so let's pre-process the data.
|
||||
var err error
|
||||
data, err = d.config.DecodeHook(getKind(dataVal), getKind(val), data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
dataKind := getKind(val)
|
||||
switch dataKind {
|
||||
case reflect.Bool:
|
||||
err = d.decodeBool(name, data, val)
|
||||
case reflect.Interface:
|
||||
err = d.decodeBasic(name, data, val)
|
||||
case reflect.String:
|
||||
err = d.decodeString(name, data, val)
|
||||
case reflect.Int:
|
||||
err = d.decodeInt(name, data, val)
|
||||
case reflect.Uint:
|
||||
err = d.decodeUint(name, data, val)
|
||||
case reflect.Float32:
|
||||
err = d.decodeFloat(name, data, val)
|
||||
case reflect.Struct:
|
||||
err = d.decodeStruct(name, data, val)
|
||||
case reflect.Map:
|
||||
err = d.decodeMap(name, data, val)
|
||||
case reflect.Ptr:
|
||||
err = d.decodePtr(name, data, val)
|
||||
case reflect.Slice:
|
||||
err = d.decodeSlice(name, data, val)
|
||||
default:
|
||||
// If we reached this point then we weren't able to decode it
|
||||
return fmt.Errorf("%s: unsupported type: %s", name, dataKind)
|
||||
}
|
||||
|
||||
// If we reached here, then we successfully decoded SOMETHING, so
|
||||
// mark the key as used if we're tracking metadata.
|
||||
if d.config.Metadata != nil && name != "" {
|
||||
d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// This decodes a basic type (bool, int, string, etc.) and sets the
|
||||
// value to "data" of that type.
|
||||
func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value) error {
|
||||
dataVal := reflect.ValueOf(data)
|
||||
dataValType := dataVal.Type()
|
||||
if !dataValType.AssignableTo(val.Type()) {
|
||||
return fmt.Errorf(
|
||||
"'%s' expected type '%s', got '%s'",
|
||||
name, val.Type(), dataValType)
|
||||
}
|
||||
|
||||
val.Set(dataVal)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) decodeString(name string, data interface{}, val reflect.Value) error {
|
||||
dataVal := reflect.ValueOf(data)
|
||||
dataKind := getKind(dataVal)
|
||||
|
||||
converted := true
|
||||
switch {
|
||||
case dataKind == reflect.String:
|
||||
val.SetString(dataVal.String())
|
||||
case dataKind == reflect.Bool && d.config.WeaklyTypedInput:
|
||||
if dataVal.Bool() {
|
||||
val.SetString("1")
|
||||
} else {
|
||||
val.SetString("0")
|
||||
}
|
||||
case dataKind == reflect.Int && d.config.WeaklyTypedInput:
|
||||
val.SetString(strconv.FormatInt(dataVal.Int(), 10))
|
||||
case dataKind == reflect.Uint && d.config.WeaklyTypedInput:
|
||||
val.SetString(strconv.FormatUint(dataVal.Uint(), 10))
|
||||
case dataKind == reflect.Float32 && d.config.WeaklyTypedInput:
|
||||
val.SetString(strconv.FormatFloat(dataVal.Float(), 'f', -1, 64))
|
||||
case dataKind == reflect.Slice && d.config.WeaklyTypedInput:
|
||||
dataType := dataVal.Type()
|
||||
elemKind := dataType.Elem().Kind()
|
||||
switch {
|
||||
case elemKind == reflect.Uint8:
|
||||
val.SetString(string(dataVal.Interface().([]uint8)))
|
||||
default:
|
||||
converted = false
|
||||
}
|
||||
default:
|
||||
converted = false
|
||||
}
|
||||
|
||||
if !converted {
|
||||
return fmt.Errorf(
|
||||
"'%s' expected type '%s', got unconvertible type '%s'",
|
||||
name, val.Type(), dataVal.Type())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) error {
|
||||
dataVal := reflect.ValueOf(data)
|
||||
dataKind := getKind(dataVal)
|
||||
|
||||
switch {
|
||||
case dataKind == reflect.Int:
|
||||
val.SetInt(dataVal.Int())
|
||||
case dataKind == reflect.Uint:
|
||||
val.SetInt(int64(dataVal.Uint()))
|
||||
case dataKind == reflect.Float32:
|
||||
val.SetInt(int64(dataVal.Float()))
|
||||
case dataKind == reflect.Bool && d.config.WeaklyTypedInput:
|
||||
if dataVal.Bool() {
|
||||
val.SetInt(1)
|
||||
} else {
|
||||
val.SetInt(0)
|
||||
}
|
||||
case dataKind == reflect.String && d.config.WeaklyTypedInput:
|
||||
i, err := strconv.ParseInt(dataVal.String(), 0, val.Type().Bits())
|
||||
if err == nil {
|
||||
val.SetInt(i)
|
||||
} else {
|
||||
return fmt.Errorf("cannot parse '%s' as int: %s", name, err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"'%s' expected type '%s', got unconvertible type '%s'",
|
||||
name, val.Type(), dataVal.Type())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) error {
|
||||
dataVal := reflect.ValueOf(data)
|
||||
dataKind := getKind(dataVal)
|
||||
|
||||
switch {
|
||||
case dataKind == reflect.Int:
|
||||
val.SetUint(uint64(dataVal.Int()))
|
||||
case dataKind == reflect.Uint:
|
||||
val.SetUint(dataVal.Uint())
|
||||
case dataKind == reflect.Float32:
|
||||
val.SetUint(uint64(dataVal.Float()))
|
||||
case dataKind == reflect.Bool && d.config.WeaklyTypedInput:
|
||||
if dataVal.Bool() {
|
||||
val.SetUint(1)
|
||||
} else {
|
||||
val.SetUint(0)
|
||||
}
|
||||
case dataKind == reflect.String && d.config.WeaklyTypedInput:
|
||||
i, err := strconv.ParseUint(dataVal.String(), 0, val.Type().Bits())
|
||||
if err == nil {
|
||||
val.SetUint(i)
|
||||
} else {
|
||||
return fmt.Errorf("cannot parse '%s' as uint: %s", name, err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"'%s' expected type '%s', got unconvertible type '%s'",
|
||||
name, val.Type(), dataVal.Type())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) error {
|
||||
dataVal := reflect.ValueOf(data)
|
||||
dataKind := getKind(dataVal)
|
||||
|
||||
switch {
|
||||
case dataKind == reflect.Bool:
|
||||
val.SetBool(dataVal.Bool())
|
||||
case dataKind == reflect.Int && d.config.WeaklyTypedInput:
|
||||
val.SetBool(dataVal.Int() != 0)
|
||||
case dataKind == reflect.Uint && d.config.WeaklyTypedInput:
|
||||
val.SetBool(dataVal.Uint() != 0)
|
||||
case dataKind == reflect.Float32 && d.config.WeaklyTypedInput:
|
||||
val.SetBool(dataVal.Float() != 0)
|
||||
case dataKind == reflect.String && d.config.WeaklyTypedInput:
|
||||
b, err := strconv.ParseBool(dataVal.String())
|
||||
if err == nil {
|
||||
val.SetBool(b)
|
||||
} else if dataVal.String() == "" {
|
||||
val.SetBool(false)
|
||||
} else {
|
||||
return fmt.Errorf("cannot parse '%s' as bool: %s", name, err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"'%s' expected type '%s', got unconvertible type '%s'",
|
||||
name, val.Type(), dataVal.Type())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value) error {
|
||||
dataVal := reflect.ValueOf(data)
|
||||
dataKind := getKind(dataVal)
|
||||
|
||||
switch {
|
||||
case dataKind == reflect.Int:
|
||||
val.SetFloat(float64(dataVal.Int()))
|
||||
case dataKind == reflect.Uint:
|
||||
val.SetFloat(float64(dataVal.Uint()))
|
||||
case dataKind == reflect.Float32:
|
||||
val.SetFloat(float64(dataVal.Float()))
|
||||
case dataKind == reflect.Bool && d.config.WeaklyTypedInput:
|
||||
if dataVal.Bool() {
|
||||
val.SetFloat(1)
|
||||
} else {
|
||||
val.SetFloat(0)
|
||||
}
|
||||
case dataKind == reflect.String && d.config.WeaklyTypedInput:
|
||||
f, err := strconv.ParseFloat(dataVal.String(), val.Type().Bits())
|
||||
if err == nil {
|
||||
val.SetFloat(f)
|
||||
} else {
|
||||
return fmt.Errorf("cannot parse '%s' as float: %s", name, err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"'%s' expected type '%s', got unconvertible type '%s'",
|
||||
name, val.Type(), dataVal.Type())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) error {
|
||||
valType := val.Type()
|
||||
valKeyType := valType.Key()
|
||||
valElemType := valType.Elem()
|
||||
|
||||
// Make a new map to hold our result
|
||||
mapType := reflect.MapOf(valKeyType, valElemType)
|
||||
valMap := reflect.MakeMap(mapType)
|
||||
|
||||
// Check input type
|
||||
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||
if dataVal.Kind() != reflect.Map {
|
||||
// Accept empty array/slice instead of an empty map in weakly typed mode
|
||||
if d.config.WeaklyTypedInput &&
|
||||
(dataVal.Kind() == reflect.Slice || dataVal.Kind() == reflect.Array) &&
|
||||
dataVal.Len() == 0 {
|
||||
val.Set(valMap)
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind())
|
||||
}
|
||||
}
|
||||
|
||||
// Accumulate errors
|
||||
errors := make([]string, 0)
|
||||
|
||||
for _, k := range dataVal.MapKeys() {
|
||||
fieldName := fmt.Sprintf("%s[%s]", name, k)
|
||||
|
||||
// First decode the key into the proper type
|
||||
currentKey := reflect.Indirect(reflect.New(valKeyType))
|
||||
if err := d.decode(fieldName, k.Interface(), currentKey); err != nil {
|
||||
errors = appendErrors(errors, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Next decode the data into the proper type
|
||||
v := dataVal.MapIndex(k).Interface()
|
||||
currentVal := reflect.Indirect(reflect.New(valElemType))
|
||||
if err := d.decode(fieldName, v, currentVal); err != nil {
|
||||
errors = appendErrors(errors, err)
|
||||
continue
|
||||
}
|
||||
|
||||
valMap.SetMapIndex(currentKey, currentVal)
|
||||
}
|
||||
|
||||
// Set the built up map to the value
|
||||
val.Set(valMap)
|
||||
|
||||
// If we had errors, return those
|
||||
if len(errors) > 0 {
|
||||
return &Error{errors}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) error {
|
||||
// Create an element of the concrete (non pointer) type and decode
|
||||
// into that. Then set the value of the pointer to this type.
|
||||
valType := val.Type()
|
||||
valElemType := valType.Elem()
|
||||
realVal := reflect.New(valElemType)
|
||||
if err := d.decode(name, data, reflect.Indirect(realVal)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
val.Set(realVal)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value) error {
|
||||
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||
dataValKind := dataVal.Kind()
|
||||
valType := val.Type()
|
||||
valElemType := valType.Elem()
|
||||
sliceType := reflect.SliceOf(valElemType)
|
||||
|
||||
// Check input type
|
||||
if dataValKind != reflect.Array && dataValKind != reflect.Slice {
|
||||
// Accept empty map instead of array/slice in weakly typed mode
|
||||
if d.config.WeaklyTypedInput && dataVal.Kind() == reflect.Map && dataVal.Len() == 0 {
|
||||
val.Set(reflect.MakeSlice(sliceType, 0, 0))
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf(
|
||||
"'%s': source data must be an array or slice, got %s", name, dataValKind)
|
||||
}
|
||||
}
|
||||
|
||||
// Make a new slice to hold our result, same size as the original data.
|
||||
valSlice := reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len())
|
||||
|
||||
// Accumulate any errors
|
||||
errors := make([]string, 0)
|
||||
|
||||
for i := 0; i < dataVal.Len(); i++ {
|
||||
currentData := dataVal.Index(i).Interface()
|
||||
currentField := valSlice.Index(i)
|
||||
|
||||
fieldName := fmt.Sprintf("%s[%d]", name, i)
|
||||
if err := d.decode(fieldName, currentData, currentField); err != nil {
|
||||
errors = appendErrors(errors, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, set the value to the slice we built up
|
||||
val.Set(valSlice)
|
||||
|
||||
// If there were errors, we return those
|
||||
if len(errors) > 0 {
|
||||
return &Error{errors}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) error {
|
||||
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||
dataValKind := dataVal.Kind()
|
||||
if dataValKind != reflect.Map {
|
||||
return fmt.Errorf("'%s' expected a map, got '%s'", name, dataValKind)
|
||||
}
|
||||
|
||||
dataValType := dataVal.Type()
|
||||
if kind := dataValType.Key().Kind(); kind != reflect.String && kind != reflect.Interface {
|
||||
return fmt.Errorf(
|
||||
"'%s' needs a map with string keys, has '%s' keys",
|
||||
name, dataValType.Key().Kind())
|
||||
}
|
||||
|
||||
dataValKeys := make(map[reflect.Value]struct{})
|
||||
dataValKeysUnused := make(map[interface{}]struct{})
|
||||
for _, dataValKey := range dataVal.MapKeys() {
|
||||
dataValKeys[dataValKey] = struct{}{}
|
||||
dataValKeysUnused[dataValKey.Interface()] = struct{}{}
|
||||
}
|
||||
|
||||
errors := make([]string, 0)
|
||||
|
||||
// This slice will keep track of all the structs we'll be decoding.
|
||||
// There can be more than one struct if there are embedded structs
|
||||
// that are squashed.
|
||||
structs := make([]reflect.Value, 1, 5)
|
||||
structs[0] = val
|
||||
|
||||
// Compile the list of all the fields that we're going to be decoding
|
||||
// from all the structs.
|
||||
fields := make(map[*reflect.StructField]reflect.Value)
|
||||
for len(structs) > 0 {
|
||||
structVal := structs[0]
|
||||
structs = structs[1:]
|
||||
|
||||
structType := structVal.Type()
|
||||
for i := 0; i < structType.NumField(); i++ {
|
||||
fieldType := structType.Field(i)
|
||||
|
||||
if fieldType.Anonymous {
|
||||
fieldKind := fieldType.Type.Kind()
|
||||
if fieldKind != reflect.Struct {
|
||||
errors = appendErrors(errors,
|
||||
fmt.Errorf("%s: unsupported type: %s", fieldType.Name, fieldKind))
|
||||
continue
|
||||
}
|
||||
|
||||
// We have an embedded field. We "squash" the fields down
|
||||
// if specified in the tag.
|
||||
squash := false
|
||||
tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",")
|
||||
for _, tag := range tagParts[1:] {
|
||||
if tag == "squash" {
|
||||
squash = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if squash {
|
||||
structs = append(structs, val.FieldByName(fieldType.Name))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Normal struct field, store it away
|
||||
fields[&fieldType] = structVal.Field(i)
|
||||
}
|
||||
}
|
||||
|
||||
for fieldType, field := range fields {
|
||||
fieldName := fieldType.Name
|
||||
|
||||
tagValue := fieldType.Tag.Get(d.config.TagName)
|
||||
tagValue = strings.SplitN(tagValue, ",", 2)[0]
|
||||
if tagValue != "" {
|
||||
fieldName = tagValue
|
||||
}
|
||||
|
||||
rawMapKey := reflect.ValueOf(fieldName)
|
||||
rawMapVal := dataVal.MapIndex(rawMapKey)
|
||||
if !rawMapVal.IsValid() {
|
||||
// Do a slower search by iterating over each key and
|
||||
// doing case-insensitive search.
|
||||
for dataValKey, _ := range dataValKeys {
|
||||
mK, ok := dataValKey.Interface().(string)
|
||||
if !ok {
|
||||
// Not a string key
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.EqualFold(mK, fieldName) {
|
||||
rawMapKey = dataValKey
|
||||
rawMapVal = dataVal.MapIndex(dataValKey)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !rawMapVal.IsValid() {
|
||||
// There was no matching key in the map for the value in
|
||||
// the struct. Just ignore.
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the key we're using from the unused map so we stop tracking
|
||||
delete(dataValKeysUnused, rawMapKey.Interface())
|
||||
|
||||
if !field.IsValid() {
|
||||
// This should never happen
|
||||
panic("field is not valid")
|
||||
}
|
||||
|
||||
// If we can't set the field, then it is unexported or something,
|
||||
// and we just continue onwards.
|
||||
if !field.CanSet() {
|
||||
continue
|
||||
}
|
||||
|
||||
// If the name is empty string, then we're at the root, and we
|
||||
// don't dot-join the fields.
|
||||
if name != "" {
|
||||
fieldName = fmt.Sprintf("%s.%s", name, fieldName)
|
||||
}
|
||||
|
||||
if err := d.decode(fieldName, rawMapVal.Interface(), field); err != nil {
|
||||
errors = appendErrors(errors, err)
|
||||
}
|
||||
}
|
||||
|
||||
if d.config.ErrorUnused && len(dataValKeysUnused) > 0 {
|
||||
keys := make([]string, 0, len(dataValKeysUnused))
|
||||
for rawKey, _ := range dataValKeysUnused {
|
||||
keys = append(keys, rawKey.(string))
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
err := fmt.Errorf("'%s' has invalid keys: %s", name, strings.Join(keys, ", "))
|
||||
errors = appendErrors(errors, err)
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return &Error{errors}
|
||||
}
|
||||
|
||||
// Add the unused keys to the list of unused keys if we're tracking metadata
|
||||
if d.config.Metadata != nil {
|
||||
for rawKey, _ := range dataValKeysUnused {
|
||||
key := rawKey.(string)
|
||||
if name != "" {
|
||||
key = fmt.Sprintf("%s.%s", name, key)
|
||||
}
|
||||
|
||||
d.config.Metadata.Unused = append(d.config.Metadata.Unused, key)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getKind(val reflect.Value) reflect.Kind {
|
||||
kind := val.Kind()
|
||||
|
||||
switch {
|
||||
case kind >= reflect.Int && kind <= reflect.Int64:
|
||||
return reflect.Int
|
||||
case kind >= reflect.Uint && kind <= reflect.Uint64:
|
||||
return reflect.Uint
|
||||
case kind >= reflect.Float32 && kind <= reflect.Float64:
|
||||
return reflect.Float32
|
||||
default:
|
||||
return kind
|
||||
}
|
||||
}
|
243
Godeps/_workspace/src/github.com/mitchellh/mapstructure/mapstructure_benchmark_test.go
generated
vendored
Normal file
243
Godeps/_workspace/src/github.com/mitchellh/mapstructure/mapstructure_benchmark_test.go
generated
vendored
Normal file
|
@ -0,0 +1,243 @@
|
|||
package mapstructure
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Benchmark_Decode(b *testing.B) {
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
Emails []string
|
||||
Extra map[string]string
|
||||
}
|
||||
|
||||
input := map[string]interface{}{
|
||||
"name": "Mitchell",
|
||||
"age": 91,
|
||||
"emails": []string{"one", "two", "three"},
|
||||
"extra": map[string]string{
|
||||
"twitter": "mitchellh",
|
||||
},
|
||||
}
|
||||
|
||||
var result Person
|
||||
for i := 0; i < b.N; i++ {
|
||||
Decode(input, &result)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_DecodeBasic(b *testing.B) {
|
||||
input := map[string]interface{}{
|
||||
"vstring": "foo",
|
||||
"vint": 42,
|
||||
"Vuint": 42,
|
||||
"vbool": true,
|
||||
"Vfloat": 42.42,
|
||||
"vsilent": true,
|
||||
"vdata": 42,
|
||||
}
|
||||
|
||||
var result Basic
|
||||
for i := 0; i < b.N; i++ {
|
||||
Decode(input, &result)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_DecodeEmbedded(b *testing.B) {
|
||||
input := map[string]interface{}{
|
||||
"vstring": "foo",
|
||||
"Basic": map[string]interface{}{
|
||||
"vstring": "innerfoo",
|
||||
},
|
||||
"vunique": "bar",
|
||||
}
|
||||
|
||||
var result Embedded
|
||||
for i := 0; i < b.N; i++ {
|
||||
Decode(input, &result)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_DecodeTypeConversion(b *testing.B) {
|
||||
input := map[string]interface{}{
|
||||
"IntToFloat": 42,
|
||||
"IntToUint": 42,
|
||||
"IntToBool": 1,
|
||||
"IntToString": 42,
|
||||
"UintToInt": 42,
|
||||
"UintToFloat": 42,
|
||||
"UintToBool": 42,
|
||||
"UintToString": 42,
|
||||
"BoolToInt": true,
|
||||
"BoolToUint": true,
|
||||
"BoolToFloat": true,
|
||||
"BoolToString": true,
|
||||
"FloatToInt": 42.42,
|
||||
"FloatToUint": 42.42,
|
||||
"FloatToBool": 42.42,
|
||||
"FloatToString": 42.42,
|
||||
"StringToInt": "42",
|
||||
"StringToUint": "42",
|
||||
"StringToBool": "1",
|
||||
"StringToFloat": "42.42",
|
||||
"SliceToMap": []interface{}{},
|
||||
"MapToSlice": map[string]interface{}{},
|
||||
}
|
||||
|
||||
var resultStrict TypeConversionResult
|
||||
for i := 0; i < b.N; i++ {
|
||||
Decode(input, &resultStrict)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_DecodeMap(b *testing.B) {
|
||||
input := map[string]interface{}{
|
||||
"vfoo": "foo",
|
||||
"vother": map[interface{}]interface{}{
|
||||
"foo": "foo",
|
||||
"bar": "bar",
|
||||
},
|
||||
}
|
||||
|
||||
var result Map
|
||||
for i := 0; i < b.N; i++ {
|
||||
Decode(input, &result)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_DecodeMapOfStruct(b *testing.B) {
|
||||
input := map[string]interface{}{
|
||||
"value": map[string]interface{}{
|
||||
"foo": map[string]string{"vstring": "one"},
|
||||
"bar": map[string]string{"vstring": "two"},
|
||||
},
|
||||
}
|
||||
|
||||
var result MapOfStruct
|
||||
for i := 0; i < b.N; i++ {
|
||||
Decode(input, &result)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_DecodeSlice(b *testing.B) {
|
||||
input := map[string]interface{}{
|
||||
"vfoo": "foo",
|
||||
"vbar": []string{"foo", "bar", "baz"},
|
||||
}
|
||||
|
||||
var result Slice
|
||||
for i := 0; i < b.N; i++ {
|
||||
Decode(input, &result)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_DecodeSliceOfStruct(b *testing.B) {
|
||||
input := map[string]interface{}{
|
||||
"value": []map[string]interface{}{
|
||||
{"vstring": "one"},
|
||||
{"vstring": "two"},
|
||||
},
|
||||
}
|
||||
|
||||
var result SliceOfStruct
|
||||
for i := 0; i < b.N; i++ {
|
||||
Decode(input, &result)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_DecodeWeaklyTypedInput(b *testing.B) {
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
Emails []string
|
||||
}
|
||||
|
||||
// This input can come from anywhere, but typically comes from
|
||||
// something like decoding JSON, generated by a weakly typed language
|
||||
// such as PHP.
|
||||
input := map[string]interface{}{
|
||||
"name": 123, // number => string
|
||||
"age": "42", // string => number
|
||||
"emails": map[string]interface{}{}, // empty map => empty array
|
||||
}
|
||||
|
||||
var result Person
|
||||
config := &DecoderConfig{
|
||||
WeaklyTypedInput: true,
|
||||
Result: &result,
|
||||
}
|
||||
|
||||
decoder, err := NewDecoder(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
decoder.Decode(input)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_DecodeMetadata(b *testing.B) {
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
input := map[string]interface{}{
|
||||
"name": "Mitchell",
|
||||
"age": 91,
|
||||
"email": "foo@bar.com",
|
||||
}
|
||||
|
||||
var md Metadata
|
||||
var result Person
|
||||
config := &DecoderConfig{
|
||||
Metadata: &md,
|
||||
Result: &result,
|
||||
}
|
||||
|
||||
decoder, err := NewDecoder(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
decoder.Decode(input)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_DecodeMetadataEmbedded(b *testing.B) {
|
||||
input := map[string]interface{}{
|
||||
"vstring": "foo",
|
||||
"vunique": "bar",
|
||||
}
|
||||
|
||||
var md Metadata
|
||||
var result EmbeddedSquash
|
||||
config := &DecoderConfig{
|
||||
Metadata: &md,
|
||||
Result: &result,
|
||||
}
|
||||
|
||||
decoder, err := NewDecoder(config)
|
||||
if err != nil {
|
||||
b.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
decoder.Decode(input)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_DecodeTagged(b *testing.B) {
|
||||
input := map[string]interface{}{
|
||||
"foo": "bar",
|
||||
"bar": "value",
|
||||
}
|
||||
|
||||
var result Tagged
|
||||
for i := 0; i < b.N; i++ {
|
||||
Decode(input, &result)
|
||||
}
|
||||
}
|
47
Godeps/_workspace/src/github.com/mitchellh/mapstructure/mapstructure_bugs_test.go
generated
vendored
Normal file
47
Godeps/_workspace/src/github.com/mitchellh/mapstructure/mapstructure_bugs_test.go
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
package mapstructure
|
||||
|
||||
import "testing"
|
||||
|
||||
// GH-1
|
||||
func TestDecode_NilValue(t *testing.T) {
|
||||
input := map[string]interface{}{
|
||||
"vfoo": nil,
|
||||
"vother": nil,
|
||||
}
|
||||
|
||||
var result Map
|
||||
err := Decode(input, &result)
|
||||
if err != nil {
|
||||
t.Fatalf("should not error: %s", err)
|
||||
}
|
||||
|
||||
if result.Vfoo != "" {
|
||||
t.Fatalf("value should be default: %s", result.Vfoo)
|
||||
}
|
||||
|
||||
if result.Vother != nil {
|
||||
t.Fatalf("Vother should be nil: %s", result.Vother)
|
||||
}
|
||||
}
|
||||
|
||||
// GH-10
|
||||
func TestDecode_mapInterfaceInterface(t *testing.T) {
|
||||
input := map[interface{}]interface{}{
|
||||
"vfoo": nil,
|
||||
"vother": nil,
|
||||
}
|
||||
|
||||
var result Map
|
||||
err := Decode(input, &result)
|
||||
if err != nil {
|
||||
t.Fatalf("should not error: %s", err)
|
||||
}
|
||||
|
||||
if result.Vfoo != "" {
|
||||
t.Fatalf("value should be default: %s", result.Vfoo)
|
||||
}
|
||||
|
||||
if result.Vother != nil {
|
||||
t.Fatalf("Vother should be nil: %s", result.Vother)
|
||||
}
|
||||
}
|
169
Godeps/_workspace/src/github.com/mitchellh/mapstructure/mapstructure_examples_test.go
generated
vendored
Normal file
169
Godeps/_workspace/src/github.com/mitchellh/mapstructure/mapstructure_examples_test.go
generated
vendored
Normal file
|
@ -0,0 +1,169 @@
|
|||
package mapstructure
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func ExampleDecode() {
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
Emails []string
|
||||
Extra map[string]string
|
||||
}
|
||||
|
||||
// This input can come from anywhere, but typically comes from
|
||||
// something like decoding JSON where we're not quite sure of the
|
||||
// struct initially.
|
||||
input := map[string]interface{}{
|
||||
"name": "Mitchell",
|
||||
"age": 91,
|
||||
"emails": []string{"one", "two", "three"},
|
||||
"extra": map[string]string{
|
||||
"twitter": "mitchellh",
|
||||
},
|
||||
}
|
||||
|
||||
var result Person
|
||||
err := Decode(input, &result)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("%#v", result)
|
||||
// Output:
|
||||
// mapstructure.Person{Name:"Mitchell", Age:91, Emails:[]string{"one", "two", "three"}, Extra:map[string]string{"twitter":"mitchellh"}}
|
||||
}
|
||||
|
||||
func ExampleDecode_errors() {
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
Emails []string
|
||||
Extra map[string]string
|
||||
}
|
||||
|
||||
// This input can come from anywhere, but typically comes from
|
||||
// something like decoding JSON where we're not quite sure of the
|
||||
// struct initially.
|
||||
input := map[string]interface{}{
|
||||
"name": 123,
|
||||
"age": "bad value",
|
||||
"emails": []int{1, 2, 3},
|
||||
}
|
||||
|
||||
var result Person
|
||||
err := Decode(input, &result)
|
||||
if err == nil {
|
||||
panic("should have an error")
|
||||
}
|
||||
|
||||
fmt.Println(err.Error())
|
||||
// Output:
|
||||
// 5 error(s) decoding:
|
||||
//
|
||||
// * 'Name' expected type 'string', got unconvertible type 'int'
|
||||
// * 'Age' expected type 'int', got unconvertible type 'string'
|
||||
// * 'Emails[0]' expected type 'string', got unconvertible type 'int'
|
||||
// * 'Emails[1]' expected type 'string', got unconvertible type 'int'
|
||||
// * 'Emails[2]' expected type 'string', got unconvertible type 'int'
|
||||
}
|
||||
|
||||
func ExampleDecode_metadata() {
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
// This input can come from anywhere, but typically comes from
|
||||
// something like decoding JSON where we're not quite sure of the
|
||||
// struct initially.
|
||||
input := map[string]interface{}{
|
||||
"name": "Mitchell",
|
||||
"age": 91,
|
||||
"email": "foo@bar.com",
|
||||
}
|
||||
|
||||
// For metadata, we make a more advanced DecoderConfig so we can
|
||||
// more finely configure the decoder that is used. In this case, we
|
||||
// just tell the decoder we want to track metadata.
|
||||
var md Metadata
|
||||
var result Person
|
||||
config := &DecoderConfig{
|
||||
Metadata: &md,
|
||||
Result: &result,
|
||||
}
|
||||
|
||||
decoder, err := NewDecoder(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := decoder.Decode(input); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("Unused keys: %#v", md.Unused)
|
||||
// Output:
|
||||
// Unused keys: []string{"email"}
|
||||
}
|
||||
|
||||
func ExampleDecode_weaklyTypedInput() {
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
Emails []string
|
||||
}
|
||||
|
||||
// This input can come from anywhere, but typically comes from
|
||||
// something like decoding JSON, generated by a weakly typed language
|
||||
// such as PHP.
|
||||
input := map[string]interface{}{
|
||||
"name": 123, // number => string
|
||||
"age": "42", // string => number
|
||||
"emails": map[string]interface{}{}, // empty map => empty array
|
||||
}
|
||||
|
||||
var result Person
|
||||
config := &DecoderConfig{
|
||||
WeaklyTypedInput: true,
|
||||
Result: &result,
|
||||
}
|
||||
|
||||
decoder, err := NewDecoder(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = decoder.Decode(input)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("%#v", result)
|
||||
// Output: mapstructure.Person{Name:"123", Age:42, Emails:[]string{}}
|
||||
}
|
||||
|
||||
func ExampleDecode_tags() {
|
||||
// Note that the mapstructure tags defined in the struct type
|
||||
// can indicate which fields the values are mapped to.
|
||||
type Person struct {
|
||||
Name string `mapstructure:"person_name"`
|
||||
Age int `mapstructure:"person_age"`
|
||||
}
|
||||
|
||||
input := map[string]interface{}{
|
||||
"person_name": "Mitchell",
|
||||
"person_age": 91,
|
||||
}
|
||||
|
||||
var result Person
|
||||
err := Decode(input, &result)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("%#v", result)
|
||||
// Output:
|
||||
// mapstructure.Person{Name:"Mitchell", Age:91}
|
||||
}
|
828
Godeps/_workspace/src/github.com/mitchellh/mapstructure/mapstructure_test.go
generated
vendored
Normal file
828
Godeps/_workspace/src/github.com/mitchellh/mapstructure/mapstructure_test.go
generated
vendored
Normal file
|
@ -0,0 +1,828 @@
|
|||
package mapstructure
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type Basic struct {
|
||||
Vstring string
|
||||
Vint int
|
||||
Vuint uint
|
||||
Vbool bool
|
||||
Vfloat float64
|
||||
Vextra string
|
||||
vsilent bool
|
||||
Vdata interface{}
|
||||
}
|
||||
|
||||
type Embedded struct {
|
||||
Basic
|
||||
Vunique string
|
||||
}
|
||||
|
||||
type EmbeddedPointer struct {
|
||||
*Basic
|
||||
Vunique string
|
||||
}
|
||||
|
||||
type EmbeddedSquash struct {
|
||||
Basic `mapstructure:",squash"`
|
||||
Vunique string
|
||||
}
|
||||
|
||||
type Map struct {
|
||||
Vfoo string
|
||||
Vother map[string]string
|
||||
}
|
||||
|
||||
type MapOfStruct struct {
|
||||
Value map[string]Basic
|
||||
}
|
||||
|
||||
type Nested struct {
|
||||
Vfoo string
|
||||
Vbar Basic
|
||||
}
|
||||
|
||||
type NestedPointer struct {
|
||||
Vfoo string
|
||||
Vbar *Basic
|
||||
}
|
||||
|
||||
type Slice struct {
|
||||
Vfoo string
|
||||
Vbar []string
|
||||
}
|
||||
|
||||
type SliceOfStruct struct {
|
||||
Value []Basic
|
||||
}
|
||||
|
||||
type Tagged struct {
|
||||
Extra string `mapstructure:"bar,what,what"`
|
||||
Value string `mapstructure:"foo"`
|
||||
}
|
||||
|
||||
type TypeConversionResult struct {
|
||||
IntToFloat float32
|
||||
IntToUint uint
|
||||
IntToBool bool
|
||||
IntToString string
|
||||
UintToInt int
|
||||
UintToFloat float32
|
||||
UintToBool bool
|
||||
UintToString string
|
||||
BoolToInt int
|
||||
BoolToUint uint
|
||||
BoolToFloat float32
|
||||
BoolToString string
|
||||
FloatToInt int
|
||||
FloatToUint uint
|
||||
FloatToBool bool
|
||||
FloatToString string
|
||||
SliceUint8ToString string
|
||||
StringToInt int
|
||||
StringToUint uint
|
||||
StringToBool bool
|
||||
StringToFloat float32
|
||||
SliceToMap map[string]interface{}
|
||||
MapToSlice []interface{}
|
||||
}
|
||||
|
||||
func TestBasicTypes(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input := map[string]interface{}{
|
||||
"vstring": "foo",
|
||||
"vint": 42,
|
||||
"Vuint": 42,
|
||||
"vbool": true,
|
||||
"Vfloat": 42.42,
|
||||
"vsilent": true,
|
||||
"vdata": 42,
|
||||
}
|
||||
|
||||
var result Basic
|
||||
err := Decode(input, &result)
|
||||
if err != nil {
|
||||
t.Errorf("got an err: %s", err.Error())
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if result.Vstring != "foo" {
|
||||
t.Errorf("vstring value should be 'foo': %#v", result.Vstring)
|
||||
}
|
||||
|
||||
if result.Vint != 42 {
|
||||
t.Errorf("vint value should be 42: %#v", result.Vint)
|
||||
}
|
||||
|
||||
if result.Vuint != 42 {
|
||||
t.Errorf("vuint value should be 42: %#v", result.Vuint)
|
||||
}
|
||||
|
||||
if result.Vbool != true {
|
||||
t.Errorf("vbool value should be true: %#v", result.Vbool)
|
||||
}
|
||||
|
||||
if result.Vfloat != 42.42 {
|
||||
t.Errorf("vfloat value should be 42.42: %#v", result.Vfloat)
|
||||
}
|
||||
|
||||
if result.Vextra != "" {
|
||||
t.Errorf("vextra value should be empty: %#v", result.Vextra)
|
||||
}
|
||||
|
||||
if result.vsilent != false {
|
||||
t.Error("vsilent should not be set, it is unexported")
|
||||
}
|
||||
|
||||
if result.Vdata != 42 {
|
||||
t.Error("vdata should be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasic_IntWithFloat(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input := map[string]interface{}{
|
||||
"vint": float64(42),
|
||||
}
|
||||
|
||||
var result Basic
|
||||
err := Decode(input, &result)
|
||||
if err != nil {
|
||||
t.Fatalf("got an err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecode_Embedded(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input := map[string]interface{}{
|
||||
"vstring": "foo",
|
||||
"Basic": map[string]interface{}{
|
||||
"vstring": "innerfoo",
|
||||
},
|
||||
"vunique": "bar",
|
||||
}
|
||||
|
||||
var result Embedded
|
||||
err := Decode(input, &result)
|
||||
if err != nil {
|
||||
t.Fatalf("got an err: %s", err.Error())
|
||||
}
|
||||
|
||||
if result.Vstring != "innerfoo" {
|
||||
t.Errorf("vstring value should be 'innerfoo': %#v", result.Vstring)
|
||||
}
|
||||
|
||||
if result.Vunique != "bar" {
|
||||
t.Errorf("vunique value should be 'bar': %#v", result.Vunique)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecode_EmbeddedPointer(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input := map[string]interface{}{
|
||||
"vstring": "foo",
|
||||
"Basic": map[string]interface{}{
|
||||
"vstring": "innerfoo",
|
||||
},
|
||||
"vunique": "bar",
|
||||
}
|
||||
|
||||
var result EmbeddedPointer
|
||||
err := Decode(input, &result)
|
||||
if err == nil {
|
||||
t.Fatal("should get error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecode_EmbeddedSquash(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input := map[string]interface{}{
|
||||
"vstring": "foo",
|
||||
"vunique": "bar",
|
||||
}
|
||||
|
||||
var result EmbeddedSquash
|
||||
err := Decode(input, &result)
|
||||
if err != nil {
|
||||
t.Fatalf("got an err: %s", err.Error())
|
||||
}
|
||||
|
||||
if result.Vstring != "foo" {
|
||||
t.Errorf("vstring value should be 'foo': %#v", result.Vstring)
|
||||
}
|
||||
|
||||
if result.Vunique != "bar" {
|
||||
t.Errorf("vunique value should be 'bar': %#v", result.Vunique)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecode_DecodeHook(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input := map[string]interface{}{
|
||||
"vint": "WHAT",
|
||||
}
|
||||
|
||||
decodeHook := func(from reflect.Kind, to reflect.Kind, v interface{}) (interface{}, error) {
|
||||
if from == reflect.String && to != reflect.String {
|
||||
return 5, nil
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
var result Basic
|
||||
config := &DecoderConfig{
|
||||
DecodeHook: decodeHook,
|
||||
Result: &result,
|
||||
}
|
||||
|
||||
decoder, err := NewDecoder(config)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
err = decoder.Decode(input)
|
||||
if err != nil {
|
||||
t.Fatalf("got an err: %s", err)
|
||||
}
|
||||
|
||||
if result.Vint != 5 {
|
||||
t.Errorf("vint should be 5: %#v", result.Vint)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecode_Nil(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var input interface{} = nil
|
||||
result := Basic{
|
||||
Vstring: "foo",
|
||||
}
|
||||
|
||||
err := Decode(input, &result)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if result.Vstring != "foo" {
|
||||
t.Fatalf("bad: %#v", result.Vstring)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecode_NonStruct(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input := map[string]interface{}{
|
||||
"foo": "bar",
|
||||
"bar": "baz",
|
||||
}
|
||||
|
||||
var result map[string]string
|
||||
err := Decode(input, &result)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if result["foo"] != "bar" {
|
||||
t.Fatal("foo is not bar")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecode_TypeConversion(t *testing.T) {
|
||||
input := map[string]interface{}{
|
||||
"IntToFloat": 42,
|
||||
"IntToUint": 42,
|
||||
"IntToBool": 1,
|
||||
"IntToString": 42,
|
||||
"UintToInt": 42,
|
||||
"UintToFloat": 42,
|
||||
"UintToBool": 42,
|
||||
"UintToString": 42,
|
||||
"BoolToInt": true,
|
||||
"BoolToUint": true,
|
||||
"BoolToFloat": true,
|
||||
"BoolToString": true,
|
||||
"FloatToInt": 42.42,
|
||||
"FloatToUint": 42.42,
|
||||
"FloatToBool": 42.42,
|
||||
"FloatToString": 42.42,
|
||||
"SliceUint8ToString": []uint8("foo"),
|
||||
"StringToInt": "42",
|
||||
"StringToUint": "42",
|
||||
"StringToBool": "1",
|
||||
"StringToFloat": "42.42",
|
||||
"SliceToMap": []interface{}{},
|
||||
"MapToSlice": map[string]interface{}{},
|
||||
}
|
||||
|
||||
expectedResultStrict := TypeConversionResult{
|
||||
IntToFloat: 42.0,
|
||||
IntToUint: 42,
|
||||
UintToInt: 42,
|
||||
UintToFloat: 42,
|
||||
BoolToInt: 0,
|
||||
BoolToUint: 0,
|
||||
BoolToFloat: 0,
|
||||
FloatToInt: 42,
|
||||
FloatToUint: 42,
|
||||
}
|
||||
|
||||
expectedResultWeak := TypeConversionResult{
|
||||
IntToFloat: 42.0,
|
||||
IntToUint: 42,
|
||||
IntToBool: true,
|
||||
IntToString: "42",
|
||||
UintToInt: 42,
|
||||
UintToFloat: 42,
|
||||
UintToBool: true,
|
||||
UintToString: "42",
|
||||
BoolToInt: 1,
|
||||
BoolToUint: 1,
|
||||
BoolToFloat: 1,
|
||||
BoolToString: "1",
|
||||
FloatToInt: 42,
|
||||
FloatToUint: 42,
|
||||
FloatToBool: true,
|
||||
FloatToString: "42.42",
|
||||
SliceUint8ToString: "foo",
|
||||
StringToInt: 42,
|
||||
StringToUint: 42,
|
||||
StringToBool: true,
|
||||
StringToFloat: 42.42,
|
||||
SliceToMap: map[string]interface{}{},
|
||||
MapToSlice: []interface{}{},
|
||||
}
|
||||
|
||||
// Test strict type conversion
|
||||
var resultStrict TypeConversionResult
|
||||
err := Decode(input, &resultStrict)
|
||||
if err == nil {
|
||||
t.Errorf("should return an error")
|
||||
}
|
||||
if !reflect.DeepEqual(resultStrict, expectedResultStrict) {
|
||||
t.Errorf("expected %v, got: %v", expectedResultStrict, resultStrict)
|
||||
}
|
||||
|
||||
// Test weak type conversion
|
||||
var decoder *Decoder
|
||||
var resultWeak TypeConversionResult
|
||||
|
||||
config := &DecoderConfig{
|
||||
WeaklyTypedInput: true,
|
||||
Result: &resultWeak,
|
||||
}
|
||||
|
||||
decoder, err = NewDecoder(config)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
err = decoder.Decode(input)
|
||||
if err != nil {
|
||||
t.Fatalf("got an err: %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(resultWeak, expectedResultWeak) {
|
||||
t.Errorf("expected \n%#v, got: \n%#v", expectedResultWeak, resultWeak)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoder_ErrorUnused(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input := map[string]interface{}{
|
||||
"vstring": "hello",
|
||||
"foo": "bar",
|
||||
}
|
||||
|
||||
var result Basic
|
||||
config := &DecoderConfig{
|
||||
ErrorUnused: true,
|
||||
Result: &result,
|
||||
}
|
||||
|
||||
decoder, err := NewDecoder(config)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
err = decoder.Decode(input)
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input := map[string]interface{}{
|
||||
"vfoo": "foo",
|
||||
"vother": map[interface{}]interface{}{
|
||||
"foo": "foo",
|
||||
"bar": "bar",
|
||||
},
|
||||
}
|
||||
|
||||
var result Map
|
||||
err := Decode(input, &result)
|
||||
if err != nil {
|
||||
t.Fatalf("got an error: %s", err)
|
||||
}
|
||||
|
||||
if result.Vfoo != "foo" {
|
||||
t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo)
|
||||
}
|
||||
|
||||
if result.Vother == nil {
|
||||
t.Fatal("vother should not be nil")
|
||||
}
|
||||
|
||||
if len(result.Vother) != 2 {
|
||||
t.Error("vother should have two items")
|
||||
}
|
||||
|
||||
if result.Vother["foo"] != "foo" {
|
||||
t.Errorf("'foo' key should be foo, got: %#v", result.Vother["foo"])
|
||||
}
|
||||
|
||||
if result.Vother["bar"] != "bar" {
|
||||
t.Errorf("'bar' key should be bar, got: %#v", result.Vother["bar"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapOfStruct(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input := map[string]interface{}{
|
||||
"value": map[string]interface{}{
|
||||
"foo": map[string]string{"vstring": "one"},
|
||||
"bar": map[string]string{"vstring": "two"},
|
||||
},
|
||||
}
|
||||
|
||||
var result MapOfStruct
|
||||
err := Decode(input, &result)
|
||||
if err != nil {
|
||||
t.Fatalf("got an err: %s", err)
|
||||
}
|
||||
|
||||
if result.Value == nil {
|
||||
t.Fatal("value should not be nil")
|
||||
}
|
||||
|
||||
if len(result.Value) != 2 {
|
||||
t.Error("value should have two items")
|
||||
}
|
||||
|
||||
if result.Value["foo"].Vstring != "one" {
|
||||
t.Errorf("foo value should be 'one', got: %s", result.Value["foo"].Vstring)
|
||||
}
|
||||
|
||||
if result.Value["bar"].Vstring != "two" {
|
||||
t.Errorf("bar value should be 'two', got: %s", result.Value["bar"].Vstring)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNestedType(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input := map[string]interface{}{
|
||||
"vfoo": "foo",
|
||||
"vbar": map[string]interface{}{
|
||||
"vstring": "foo",
|
||||
"vint": 42,
|
||||
"vbool": true,
|
||||
},
|
||||
}
|
||||
|
||||
var result Nested
|
||||
err := Decode(input, &result)
|
||||
if err != nil {
|
||||
t.Fatalf("got an err: %s", err.Error())
|
||||
}
|
||||
|
||||
if result.Vfoo != "foo" {
|
||||
t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo)
|
||||
}
|
||||
|
||||
if result.Vbar.Vstring != "foo" {
|
||||
t.Errorf("vstring value should be 'foo': %#v", result.Vbar.Vstring)
|
||||
}
|
||||
|
||||
if result.Vbar.Vint != 42 {
|
||||
t.Errorf("vint value should be 42: %#v", result.Vbar.Vint)
|
||||
}
|
||||
|
||||
if result.Vbar.Vbool != true {
|
||||
t.Errorf("vbool value should be true: %#v", result.Vbar.Vbool)
|
||||
}
|
||||
|
||||
if result.Vbar.Vextra != "" {
|
||||
t.Errorf("vextra value should be empty: %#v", result.Vbar.Vextra)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNestedTypePointer(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input := map[string]interface{}{
|
||||
"vfoo": "foo",
|
||||
"vbar": &map[string]interface{}{
|
||||
"vstring": "foo",
|
||||
"vint": 42,
|
||||
"vbool": true,
|
||||
},
|
||||
}
|
||||
|
||||
var result NestedPointer
|
||||
err := Decode(input, &result)
|
||||
if err != nil {
|
||||
t.Fatalf("got an err: %s", err.Error())
|
||||
}
|
||||
|
||||
if result.Vfoo != "foo" {
|
||||
t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo)
|
||||
}
|
||||
|
||||
if result.Vbar.Vstring != "foo" {
|
||||
t.Errorf("vstring value should be 'foo': %#v", result.Vbar.Vstring)
|
||||
}
|
||||
|
||||
if result.Vbar.Vint != 42 {
|
||||
t.Errorf("vint value should be 42: %#v", result.Vbar.Vint)
|
||||
}
|
||||
|
||||
if result.Vbar.Vbool != true {
|
||||
t.Errorf("vbool value should be true: %#v", result.Vbar.Vbool)
|
||||
}
|
||||
|
||||
if result.Vbar.Vextra != "" {
|
||||
t.Errorf("vextra value should be empty: %#v", result.Vbar.Vextra)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSlice(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
inputStringSlice := map[string]interface{}{
|
||||
"vfoo": "foo",
|
||||
"vbar": []string{"foo", "bar", "baz"},
|
||||
}
|
||||
|
||||
inputStringSlicePointer := map[string]interface{}{
|
||||
"vfoo": "foo",
|
||||
"vbar": &[]string{"foo", "bar", "baz"},
|
||||
}
|
||||
|
||||
outputStringSlice := &Slice{
|
||||
"foo",
|
||||
[]string{"foo", "bar", "baz"},
|
||||
}
|
||||
|
||||
testSliceInput(t, inputStringSlice, outputStringSlice)
|
||||
testSliceInput(t, inputStringSlicePointer, outputStringSlice)
|
||||
}
|
||||
|
||||
func TestInvalidSlice(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input := map[string]interface{}{
|
||||
"vfoo": "foo",
|
||||
"vbar": 42,
|
||||
}
|
||||
|
||||
result := Slice{}
|
||||
err := Decode(input, &result)
|
||||
if err == nil {
|
||||
t.Errorf("expected failure")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSliceOfStruct(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input := map[string]interface{}{
|
||||
"value": []map[string]interface{}{
|
||||
{"vstring": "one"},
|
||||
{"vstring": "two"},
|
||||
},
|
||||
}
|
||||
|
||||
var result SliceOfStruct
|
||||
err := Decode(input, &result)
|
||||
if err != nil {
|
||||
t.Fatalf("got unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if len(result.Value) != 2 {
|
||||
t.Fatalf("expected two values, got %d", len(result.Value))
|
||||
}
|
||||
|
||||
if result.Value[0].Vstring != "one" {
|
||||
t.Errorf("first value should be 'one', got: %s", result.Value[0].Vstring)
|
||||
}
|
||||
|
||||
if result.Value[1].Vstring != "two" {
|
||||
t.Errorf("second value should be 'two', got: %s", result.Value[1].Vstring)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidType(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input := map[string]interface{}{
|
||||
"vstring": 42,
|
||||
}
|
||||
|
||||
var result Basic
|
||||
err := Decode(input, &result)
|
||||
if err == nil {
|
||||
t.Fatal("error should exist")
|
||||
}
|
||||
|
||||
derr, ok := err.(*Error)
|
||||
if !ok {
|
||||
t.Fatalf("error should be kind of Error, instead: %#v", err)
|
||||
}
|
||||
|
||||
if derr.Errors[0] != "'Vstring' expected type 'string', got unconvertible type 'int'" {
|
||||
t.Errorf("got unexpected error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMetadata(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input := map[string]interface{}{
|
||||
"vfoo": "foo",
|
||||
"vbar": map[string]interface{}{
|
||||
"vstring": "foo",
|
||||
"Vuint": 42,
|
||||
"foo": "bar",
|
||||
},
|
||||
"bar": "nil",
|
||||
}
|
||||
|
||||
var md Metadata
|
||||
var result Nested
|
||||
config := &DecoderConfig{
|
||||
Metadata: &md,
|
||||
Result: &result,
|
||||
}
|
||||
|
||||
decoder, err := NewDecoder(config)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
err = decoder.Decode(input)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err.Error())
|
||||
}
|
||||
|
||||
expectedKeys := []string{"Vfoo", "Vbar.Vstring", "Vbar.Vuint", "Vbar"}
|
||||
if !reflect.DeepEqual(md.Keys, expectedKeys) {
|
||||
t.Fatalf("bad keys: %#v", md.Keys)
|
||||
}
|
||||
|
||||
expectedUnused := []string{"Vbar.foo", "bar"}
|
||||
if !reflect.DeepEqual(md.Unused, expectedUnused) {
|
||||
t.Fatalf("bad unused: %#v", md.Unused)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMetadata_Embedded(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input := map[string]interface{}{
|
||||
"vstring": "foo",
|
||||
"vunique": "bar",
|
||||
}
|
||||
|
||||
var md Metadata
|
||||
var result EmbeddedSquash
|
||||
config := &DecoderConfig{
|
||||
Metadata: &md,
|
||||
Result: &result,
|
||||
}
|
||||
|
||||
decoder, err := NewDecoder(config)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
err = decoder.Decode(input)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err.Error())
|
||||
}
|
||||
|
||||
expectedKeys := []string{"Vstring", "Vunique"}
|
||||
|
||||
sort.Strings(md.Keys)
|
||||
if !reflect.DeepEqual(md.Keys, expectedKeys) {
|
||||
t.Fatalf("bad keys: %#v", md.Keys)
|
||||
}
|
||||
|
||||
expectedUnused := []string{}
|
||||
if !reflect.DeepEqual(md.Unused, expectedUnused) {
|
||||
t.Fatalf("bad unused: %#v", md.Unused)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonPtrValue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
err := Decode(map[string]interface{}{}, Basic{})
|
||||
if err == nil {
|
||||
t.Fatal("error should exist")
|
||||
}
|
||||
|
||||
if err.Error() != "result must be a pointer" {
|
||||
t.Errorf("got unexpected error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTagged(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input := map[string]interface{}{
|
||||
"foo": "bar",
|
||||
"bar": "value",
|
||||
}
|
||||
|
||||
var result Tagged
|
||||
err := Decode(input, &result)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if result.Value != "bar" {
|
||||
t.Errorf("value should be 'bar', got: %#v", result.Value)
|
||||
}
|
||||
|
||||
if result.Extra != "value" {
|
||||
t.Errorf("extra should be 'value', got: %#v", result.Extra)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWeakDecode(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input := map[string]interface{}{
|
||||
"foo": "4",
|
||||
"bar": "value",
|
||||
}
|
||||
|
||||
var result struct {
|
||||
Foo int
|
||||
Bar string
|
||||
}
|
||||
|
||||
if err := WeakDecode(input, &result); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if result.Foo != 4 {
|
||||
t.Fatalf("bad: %#v", result)
|
||||
}
|
||||
if result.Bar != "value" {
|
||||
t.Fatalf("bad: %#v", result)
|
||||
}
|
||||
}
|
||||
|
||||
func testSliceInput(t *testing.T, input map[string]interface{}, expected *Slice) {
|
||||
var result Slice
|
||||
err := Decode(input, &result)
|
||||
if err != nil {
|
||||
t.Fatalf("got error: %s", err)
|
||||
}
|
||||
|
||||
if result.Vfoo != expected.Vfoo {
|
||||
t.Errorf("Vfoo expected '%s', got '%s'", expected.Vfoo, result.Vfoo)
|
||||
}
|
||||
|
||||
if result.Vbar == nil {
|
||||
t.Fatalf("Vbar a slice, got '%#v'", result.Vbar)
|
||||
}
|
||||
|
||||
if len(result.Vbar) != len(expected.Vbar) {
|
||||
t.Errorf("Vbar length should be %d, got %d", len(expected.Vbar), len(result.Vbar))
|
||||
}
|
||||
|
||||
for i, v := range result.Vbar {
|
||||
if v != expected.Vbar[i] {
|
||||
t.Errorf(
|
||||
"Vbar[%d] should be '%#v', got '%#v'",
|
||||
i, expected.Vbar[i], v)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
bin/*
|
||||
pkg/*
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
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
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
|
@ -0,0 +1,120 @@
|
|||
# perigee
|
||||
|
||||
Perigee provides a REST client that, while it should be generic enough to use with most any RESTful API, is nonetheless optimized to the needs of the OpenStack APIs.
|
||||
Perigee grew out of the need to refactor out common API access code from the [gorax](http://github.com/racker/gorax) project.
|
||||
|
||||
Several things influenced the name of the project.
|
||||
Numerous elements of the OpenStack ecosystem are named after astronomical artifacts.
|
||||
Additionally, perigee occurs when two orbiting bodies are closest to each other.
|
||||
Perigee seemed appropriate for something aiming to bring OpenStack and other RESTful services closer to the end-user.
|
||||
|
||||
**This library is still in the very early stages of development. Unless you want to contribute, it probably isn't what you want**
|
||||
|
||||
## Installation and Testing
|
||||
|
||||
To install:
|
||||
|
||||
```bash
|
||||
go get github.com/racker/perigee
|
||||
```
|
||||
|
||||
To run unit tests:
|
||||
|
||||
```bash
|
||||
go test github.com/racker/perigee
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
The following guidelines are preliminary, as this project is just starting out.
|
||||
However, this should serve as a working first-draft.
|
||||
|
||||
### Branching
|
||||
|
||||
The master branch must always be a valid build.
|
||||
The `go get` command will not work otherwise.
|
||||
Therefore, development must occur on a different branch.
|
||||
|
||||
When creating a feature branch, do so off the master branch:
|
||||
|
||||
```bash
|
||||
git checkout master
|
||||
git pull
|
||||
git checkout -b featureBranch
|
||||
git checkout -b featureBranch-wip # optional
|
||||
```
|
||||
|
||||
Perform all your editing and testing in the WIP-branch.
|
||||
Feel free to make as many commits as you see fit.
|
||||
You may even open "WIP" pull requests from your feature branch to seek feedback.
|
||||
WIP pull requests will **never** be merged, however.
|
||||
|
||||
To get code merged, you'll need to "squash" your changes into one or more clean commits in the feature branch.
|
||||
These steps should be followed:
|
||||
|
||||
```bash
|
||||
git checkout featureBranch
|
||||
git merge --squash featureBranch-wip
|
||||
git commit -a
|
||||
git push origin featureBranch
|
||||
```
|
||||
|
||||
You may now open a nice, clean, self-contained pull request from featureBranch to master.
|
||||
|
||||
The `git commit -a` command above will open a text editor so that
|
||||
you may provide a comprehensive description of the changes.
|
||||
|
||||
In general, when submitting a pull request against master,
|
||||
be sure to answer the following questions:
|
||||
|
||||
- What is the problem?
|
||||
- Why is it a problem?
|
||||
- What is your solution?
|
||||
- How does your solution work? (Recommended for non-trivial changes.)
|
||||
- Why should we use your solution over someone elses? (Recommended especially if multiple solutions being discussed.)
|
||||
|
||||
Remember that monster-sized pull requests are a bear to code-review,
|
||||
so having helpful commit logs are an absolute must to review changes as quickly as possible.
|
||||
|
||||
Finally, (s)he who breaks master is ultimately responsible for fixing master.
|
||||
|
||||
### Source Representation
|
||||
|
||||
The Go community firmly believes in a consistent representation for all Go source code.
|
||||
We do too.
|
||||
Make sure all source code is passed through "go fmt" *before* you create your pull request.
|
||||
|
||||
Please note, however, that we fully acknowledge and recognize that we no longer rely upon punch-cards for representing source files.
|
||||
Therefore, no 80-column limit exists.
|
||||
However, if a line exceeds 132 columns, you may want to consider splitting the line.
|
||||
|
||||
### Unit and Integration Tests
|
||||
|
||||
Pull requests that include non-trivial code changes without accompanying unit tests will be flatly rejected.
|
||||
While we have no way of enforcing this practice,
|
||||
you can ensure your code is thoroughly tested by always [writing tests first by intention.](http://en.wikipedia.org/wiki/Test-driven_development)
|
||||
|
||||
When creating a pull request, if even one test fails, the PR will be rejected.
|
||||
Make sure all unit tests pass.
|
||||
Make sure all integration tests pass.
|
||||
|
||||
### Documentation
|
||||
|
||||
Private functions and methods which are obvious to anyone unfamiliar with gorax needn't be accompanied by documentation.
|
||||
However, this is a code-smell; if submitting a PR, expect to justify your decision.
|
||||
|
||||
Public functions, regardless of how obvious, **must** have accompanying godoc-style documentation.
|
||||
This is not to suggest you should provide a tome for each function, however.
|
||||
Sometimes a link to more information is more appropriate, provided the link is stable, reliable, and pertinent.
|
||||
|
||||
Changing documentation often results in bizarre diffs in pull requests, due to text often spanning multiple lines.
|
||||
To work around this, put [one logical thought or sentence on a single line.](http://rhodesmill.org/brandon/2012/one-sentence-per-line/)
|
||||
While this looks weird in a plain-text editor,
|
||||
remember that both godoc and HTML viewers will reflow text.
|
||||
The source code and its comments should be easy to edit with minimal diff pollution.
|
||||
Let software dedicated to presenting the documentation to human readers deal with its presentation.
|
||||
|
||||
## Examples
|
||||
|
||||
t.b.d.
|
||||
|
|
@ -0,0 +1,269 @@
|
|||
// vim: ts=8 sw=8 noet ai
|
||||
|
||||
package perigee
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// The UnexpectedResponseCodeError structure represents a mismatch in understanding between server and client in terms of response codes.
|
||||
// Most often, this is due to an actual error condition (e.g., getting a 404 for a resource when you expect a 200).
|
||||
// However, it needn't always be the case (e.g., getting a 204 (No Content) response back when a 200 is expected).
|
||||
type UnexpectedResponseCodeError struct {
|
||||
Url string
|
||||
Expected []int
|
||||
Actual int
|
||||
Body []byte
|
||||
}
|
||||
|
||||
func (err *UnexpectedResponseCodeError) Error() string {
|
||||
return fmt.Sprintf("Expected HTTP response code %d when accessing URL(%s); got %d instead with the following body:\n%s", err.Expected, err.Url, err.Actual, string(err.Body))
|
||||
}
|
||||
|
||||
// Request issues an HTTP request, marshaling parameters, and unmarshaling results, as configured in the provided Options parameter.
|
||||
// The Response structure returned, if any, will include accumulated results recovered from the HTTP server.
|
||||
// See the Response structure for more details.
|
||||
func Request(method string, url string, opts Options) (*Response, error) {
|
||||
var body io.Reader
|
||||
var response Response
|
||||
|
||||
client := opts.CustomClient
|
||||
if client == nil {
|
||||
client = new(http.Client)
|
||||
}
|
||||
|
||||
contentType := opts.ContentType
|
||||
|
||||
body = nil
|
||||
if opts.ReqBody != nil {
|
||||
if contentType == "" {
|
||||
contentType = "application/json"
|
||||
}
|
||||
|
||||
if contentType == "application/json" {
|
||||
bodyText, err := json.Marshal(opts.ReqBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body = strings.NewReader(string(bodyText))
|
||||
if opts.DumpReqJson {
|
||||
log.Printf("Making request:\n%#v\n", string(bodyText))
|
||||
}
|
||||
} else {
|
||||
// assume opts.ReqBody implements the correct interface
|
||||
body = opts.ReqBody.(io.Reader)
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if contentType != "" {
|
||||
req.Header.Add("Content-Type", contentType)
|
||||
}
|
||||
|
||||
if opts.ContentLength > 0 {
|
||||
req.ContentLength = opts.ContentLength
|
||||
req.Header.Add("Content-Length", string(opts.ContentLength))
|
||||
}
|
||||
|
||||
if opts.MoreHeaders != nil {
|
||||
for k, v := range opts.MoreHeaders {
|
||||
req.Header.Add(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
if accept := req.Header.Get("Accept"); accept == "" {
|
||||
accept = opts.Accept
|
||||
if accept == "" {
|
||||
accept = "application/json"
|
||||
}
|
||||
req.Header.Add("Accept", accept)
|
||||
}
|
||||
|
||||
if opts.SetHeaders != nil {
|
||||
err = opts.SetHeaders(req)
|
||||
if err != nil {
|
||||
return &response, err
|
||||
}
|
||||
}
|
||||
|
||||
httpResponse, err := client.Do(req)
|
||||
if httpResponse != nil {
|
||||
response.HttpResponse = *httpResponse
|
||||
response.StatusCode = httpResponse.StatusCode
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return &response, err
|
||||
}
|
||||
// This if-statement is legacy code, preserved for backward compatibility.
|
||||
if opts.StatusCode != nil {
|
||||
*opts.StatusCode = httpResponse.StatusCode
|
||||
}
|
||||
|
||||
acceptableResponseCodes := opts.OkCodes
|
||||
if len(acceptableResponseCodes) != 0 {
|
||||
if not_in(httpResponse.StatusCode, acceptableResponseCodes) {
|
||||
b, _ := ioutil.ReadAll(httpResponse.Body)
|
||||
httpResponse.Body.Close()
|
||||
return &response, &UnexpectedResponseCodeError{
|
||||
Url: url,
|
||||
Expected: acceptableResponseCodes,
|
||||
Actual: httpResponse.StatusCode,
|
||||
Body: b,
|
||||
}
|
||||
}
|
||||
}
|
||||
if opts.Results != nil {
|
||||
defer httpResponse.Body.Close()
|
||||
jsonResult, err := ioutil.ReadAll(httpResponse.Body)
|
||||
response.JsonResult = jsonResult
|
||||
if err != nil {
|
||||
return &response, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(jsonResult, opts.Results)
|
||||
// This if-statement is legacy code, preserved for backward compatibility.
|
||||
if opts.ResponseJson != nil {
|
||||
*opts.ResponseJson = jsonResult
|
||||
}
|
||||
}
|
||||
return &response, err
|
||||
}
|
||||
|
||||
// not_in returns false if, and only if, the provided needle is _not_
|
||||
// in the given set of integers.
|
||||
func not_in(needle int, haystack []int) bool {
|
||||
for _, straw := range haystack {
|
||||
if needle == straw {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Post makes a POST request against a server using the provided HTTP client.
|
||||
// The url must be a fully-formed URL string.
|
||||
// DEPRECATED. Use Request() instead.
|
||||
func Post(url string, opts Options) error {
|
||||
r, err := Request("POST", url, opts)
|
||||
if opts.Response != nil {
|
||||
*opts.Response = r
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Get makes a GET request against a server using the provided HTTP client.
|
||||
// The url must be a fully-formed URL string.
|
||||
// DEPRECATED. Use Request() instead.
|
||||
func Get(url string, opts Options) error {
|
||||
r, err := Request("GET", url, opts)
|
||||
if opts.Response != nil {
|
||||
*opts.Response = r
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete makes a DELETE request against a server using the provided HTTP client.
|
||||
// The url must be a fully-formed URL string.
|
||||
// DEPRECATED. Use Request() instead.
|
||||
func Delete(url string, opts Options) error {
|
||||
r, err := Request("DELETE", url, opts)
|
||||
if opts.Response != nil {
|
||||
*opts.Response = r
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Put makes a PUT request against a server using the provided HTTP client.
|
||||
// The url must be a fully-formed URL string.
|
||||
// DEPRECATED. Use Request() instead.
|
||||
func Put(url string, opts Options) error {
|
||||
r, err := Request("PUT", url, opts)
|
||||
if opts.Response != nil {
|
||||
*opts.Response = r
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Options describes a set of optional parameters to the various request calls.
|
||||
//
|
||||
// The custom client can be used for a variety of purposes beyond selecting encrypted versus unencrypted channels.
|
||||
// Transports can be defined to provide augmented logging, header manipulation, et. al.
|
||||
//
|
||||
// If the ReqBody field is provided, it will be embedded as a JSON object.
|
||||
// Otherwise, provide nil.
|
||||
//
|
||||
// If JSON output is to be expected from the response,
|
||||
// provide either a pointer to the container structure in Results,
|
||||
// or a pointer to a nil-initialized pointer variable.
|
||||
// The latter method will cause the unmarshaller to allocate the container type for you.
|
||||
// If no response is expected, provide a nil Results value.
|
||||
//
|
||||
// The MoreHeaders map, if non-nil or empty, provides a set of headers to add to those
|
||||
// already present in the request. At present, only Accepted and Content-Type are set
|
||||
// by default.
|
||||
//
|
||||
// OkCodes provides a set of acceptable, positive responses.
|
||||
//
|
||||
// If provided, StatusCode specifies a pointer to an integer, which will receive the
|
||||
// returned HTTP status code, successful or not. DEPRECATED; use the Response.StatusCode field instead for new software.
|
||||
//
|
||||
// ResponseJson, if specified, provides a means for returning the raw JSON. This is
|
||||
// most useful for diagnostics. DEPRECATED; use the Response.JsonResult field instead for new software.
|
||||
//
|
||||
// DumpReqJson, if set to true, will cause the request to appear to stdout for debugging purposes.
|
||||
// This attribute may be removed at any time in the future; DO NOT use this attribute in production software.
|
||||
//
|
||||
// Response, if set, provides a way to communicate the complete set of HTTP response, raw JSON, status code, and
|
||||
// other useful attributes back to the caller. Note that the Request() method returns a Response structure as part
|
||||
// of its public interface; you don't need to set the Response field here to use this structure. The Response field
|
||||
// exists primarily for legacy or deprecated functions.
|
||||
//
|
||||
// SetHeaders allows the caller to provide code to set any custom headers programmatically. Typically, this
|
||||
// facility can invoke, e.g., SetBasicAuth() on the request to easily set up authentication.
|
||||
// Any error generated will terminate the request and will propegate back to the caller.
|
||||
type Options struct {
|
||||
CustomClient *http.Client
|
||||
ReqBody interface{}
|
||||
Results interface{}
|
||||
MoreHeaders map[string]string
|
||||
OkCodes []int
|
||||
StatusCode *int `DEPRECATED`
|
||||
DumpReqJson bool `UNSUPPORTED`
|
||||
ResponseJson *[]byte `DEPRECATED`
|
||||
Response **Response
|
||||
ContentType string `json:"Content-Type,omitempty"`
|
||||
ContentLength int64 `json:"Content-Length,omitempty"`
|
||||
Accept string `json:"Accept,omitempty"`
|
||||
SetHeaders func(r *http.Request) error
|
||||
}
|
||||
|
||||
// Response contains return values from the various request calls.
|
||||
//
|
||||
// HttpResponse will return the http response from the request call.
|
||||
// Note: HttpResponse.Body is always closed and will not be available from this return value.
|
||||
//
|
||||
// StatusCode specifies the returned HTTP status code, successful or not.
|
||||
//
|
||||
// If Results is specified in the Options:
|
||||
// - JsonResult will contain the raw return from the request call
|
||||
// This is most useful for diagnostics.
|
||||
// - Result will contain the unmarshalled json either in the Result passed in
|
||||
// or the unmarshaller will allocate the container type for you.
|
||||
|
||||
type Response struct {
|
||||
HttpResponse http.Response
|
||||
JsonResult []byte
|
||||
Results interface{}
|
||||
StatusCode int
|
||||
}
|
|
@ -0,0 +1,226 @@
|
|||
package perigee
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNormal(t *testing.T) {
|
||||
handler := http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("testing"))
|
||||
})
|
||||
ts := httptest.NewServer(handler)
|
||||
defer ts.Close()
|
||||
|
||||
response, err := Request("GET", ts.URL, Options{})
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
if response.StatusCode != 200 {
|
||||
t.Fatalf("response code %d is not 200", response.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOKCodes(t *testing.T) {
|
||||
expectCode := 201
|
||||
handler := http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(expectCode)
|
||||
w.Write([]byte("testing"))
|
||||
})
|
||||
ts := httptest.NewServer(handler)
|
||||
defer ts.Close()
|
||||
|
||||
options := Options{
|
||||
OkCodes: []int{expectCode},
|
||||
}
|
||||
results, err := Request("GET", ts.URL, options)
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
if results.StatusCode != expectCode {
|
||||
t.Fatalf("response code %d is not %d", results.StatusCode, expectCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocation(t *testing.T) {
|
||||
newLocation := "http://www.example.com"
|
||||
handler := http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Location", newLocation)
|
||||
w.Write([]byte("testing"))
|
||||
})
|
||||
ts := httptest.NewServer(handler)
|
||||
defer ts.Close()
|
||||
|
||||
response, err := Request("GET", ts.URL, Options{})
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
|
||||
location, err := response.HttpResponse.Location()
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
|
||||
if location.String() != newLocation {
|
||||
t.Fatalf("location returned \"%s\" is not \"%s\"", location.String(), newLocation)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeaders(t *testing.T) {
|
||||
newLocation := "http://www.example.com"
|
||||
handler := http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Location", newLocation)
|
||||
w.Write([]byte("testing"))
|
||||
})
|
||||
ts := httptest.NewServer(handler)
|
||||
defer ts.Close()
|
||||
|
||||
response, err := Request("GET", ts.URL, Options{})
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
|
||||
location := response.HttpResponse.Header.Get("Location")
|
||||
if location == "" {
|
||||
t.Fatalf("Location should not empty")
|
||||
}
|
||||
|
||||
if location != newLocation {
|
||||
t.Fatalf("location returned \"%s\" is not \"%s\"", location, newLocation)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomHeaders(t *testing.T) {
|
||||
var contentType, accept, contentLength string
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
m := map[string][]string(r.Header)
|
||||
contentType = m["Content-Type"][0]
|
||||
accept = m["Accept"][0]
|
||||
contentLength = m["Content-Length"][0]
|
||||
})
|
||||
ts := httptest.NewServer(handler)
|
||||
defer ts.Close()
|
||||
|
||||
_, err := Request("GET", ts.URL, Options{
|
||||
ContentLength: 5,
|
||||
ContentType: "x-application/vb",
|
||||
Accept: "x-application/c",
|
||||
ReqBody: strings.NewReader("Hello"),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
if contentType != "x-application/vb" {
|
||||
t.Fatalf("I expected x-application/vb; got ", contentType)
|
||||
}
|
||||
|
||||
if contentLength != "5" {
|
||||
t.Fatalf("I expected 5 byte content length; got ", contentLength)
|
||||
}
|
||||
|
||||
if accept != "x-application/c" {
|
||||
t.Fatalf("I expected x-application/c; got ", accept)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJson(t *testing.T) {
|
||||
newLocation := "http://www.example.com"
|
||||
jsonBytes := []byte(`{"foo": {"bar": "baz"}}`)
|
||||
handler := http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Location", newLocation)
|
||||
w.Write(jsonBytes)
|
||||
})
|
||||
ts := httptest.NewServer(handler)
|
||||
defer ts.Close()
|
||||
|
||||
type Data struct {
|
||||
Foo struct {
|
||||
Bar string `json:"bar"`
|
||||
} `json:"foo"`
|
||||
}
|
||||
var data Data
|
||||
|
||||
response, err := Request("GET", ts.URL, Options{Results: &data})
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
|
||||
if bytes.Compare(jsonBytes, response.JsonResult) != 0 {
|
||||
t.Fatalf("json returned \"%s\" is not \"%s\"", response.JsonResult, jsonBytes)
|
||||
}
|
||||
|
||||
if data.Foo.Bar != "baz" {
|
||||
t.Fatalf("Results returned %v", data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetHeaders(t *testing.T) {
|
||||
var wasCalled bool
|
||||
handler := http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("Hi"))
|
||||
})
|
||||
ts := httptest.NewServer(handler)
|
||||
defer ts.Close()
|
||||
|
||||
_, err := Request("GET", ts.URL, Options{
|
||||
SetHeaders: func(r *http.Request) error {
|
||||
wasCalled = true
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !wasCalled {
|
||||
t.Fatal("I expected header setter callback to be called, but it wasn't")
|
||||
}
|
||||
|
||||
myError := fmt.Errorf("boo")
|
||||
|
||||
_, err = Request("GET", ts.URL, Options{
|
||||
SetHeaders: func(r *http.Request) error {
|
||||
return myError
|
||||
},
|
||||
})
|
||||
|
||||
if err != myError {
|
||||
t.Fatal("I expected errors to propegate back to the caller.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBodilessMethodsAreSentWithoutContentHeaders(t *testing.T) {
|
||||
var h map[string][]string
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
h = r.Header
|
||||
})
|
||||
ts := httptest.NewServer(handler)
|
||||
defer ts.Close()
|
||||
|
||||
_, err := Request("GET", ts.URL, Options{})
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
if len(h["Content-Type"]) != 0 {
|
||||
t.Fatalf("I expected nothing for Content-Type but got ", h["Content-Type"])
|
||||
}
|
||||
|
||||
if len(h["Content-Length"]) != 0 {
|
||||
t.Fatalf("I expected nothing for Content-Length but got ", h["Content-Length"])
|
||||
}
|
||||
}
|
14
Godeps/_workspace/src/github.com/rackspace/gophercloud/.travis.yml
generated
vendored
Normal file
14
Godeps/_workspace/src/github.com/rackspace/gophercloud/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
language: go
|
||||
install:
|
||||
- go get -v -tags 'fixtures acceptance' ./...
|
||||
go:
|
||||
- 1.1
|
||||
- 1.2
|
||||
- tip
|
||||
script: script/cibuild
|
||||
after_success:
|
||||
- go get code.google.com/p/go.tools/cmd/cover
|
||||
- go get github.com/axw/gocov/gocov
|
||||
- go get github.com/mattn/goveralls
|
||||
- export PATH=$PATH:$HOME/gopath/bin/
|
||||
- goveralls 2k7PTU3xa474Hymwgdj6XjqenNfGTNkO8
|
275
Godeps/_workspace/src/github.com/rackspace/gophercloud/CONTRIBUTING.md
generated
vendored
Normal file
275
Godeps/_workspace/src/github.com/rackspace/gophercloud/CONTRIBUTING.md
generated
vendored
Normal file
|
@ -0,0 +1,275 @@
|
|||
# Contributing to gophercloud
|
||||
|
||||
- [Getting started](#getting-started)
|
||||
- [Tests](#tests)
|
||||
- [Style guide](#basic-style-guide)
|
||||
- [5 ways to get involved](#5-ways-to-get-involved)
|
||||
|
||||
## Setting up your git workspace
|
||||
|
||||
As a contributor you will need to setup your workspace in a slightly different
|
||||
way than just downloading it. Here are the basic installation instructions:
|
||||
|
||||
1. Configure your `$GOPATH` and run `go get` as described in the main
|
||||
[README](/README.md#how-to-install).
|
||||
|
||||
2. Move into the directory that houses your local repository:
|
||||
|
||||
```bash
|
||||
cd ${GOPATH}/src/github.com/rackspace/gophercloud
|
||||
```
|
||||
|
||||
3. Fork the `rackspace/gophercloud` repository and update your remote refs. You
|
||||
will need to rename the `origin` remote branch to `upstream`, and add your
|
||||
fork as `origin` instead:
|
||||
|
||||
```bash
|
||||
git remote rename origin upstream
|
||||
git remote add origin git@github.com/<my_username>/gophercloud
|
||||
```
|
||||
|
||||
4. Checkout the latest development branch ([click here](/branches) to see all
|
||||
the branches):
|
||||
|
||||
```bash
|
||||
git checkout release/v1.0.1
|
||||
```
|
||||
|
||||
5. If you're working on something (discussed more in detail below), you will
|
||||
need to checkout a new feature branch:
|
||||
|
||||
```bash
|
||||
git checkout -b my-new-feature
|
||||
```
|
||||
|
||||
Another thing to bear in mind is that you will need to add a few extra
|
||||
environment variables for acceptance tests - this is documented in our
|
||||
[acceptance tests readme](/acceptance).
|
||||
|
||||
## Tests
|
||||
|
||||
When working on a new or existing feature, testing will be the backbone of your
|
||||
work since it helps uncover and prevent regressions in the codebase. There are
|
||||
two types of test we use in gophercloud: unit tests and acceptance tests, which
|
||||
are both described below.
|
||||
|
||||
### Unit tests
|
||||
|
||||
Unit tests are the fine-grained tests that establish and ensure the behaviour
|
||||
of individual units of functionality. We usually test on an
|
||||
operation-by-operation basis (an operation typically being an API action) with
|
||||
the use of mocking to set up explicit expectations. Each operation will set up
|
||||
its HTTP response expectation, and then test how the system responds when fed
|
||||
this controlled, pre-determined input.
|
||||
|
||||
To make life easier, we've introduced a bunch of test helpers to simplify the
|
||||
process of testing expectations with assertions:
|
||||
|
||||
```go
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestSomething(t *testing.T) {
|
||||
result, err := Operation()
|
||||
|
||||
testhelper.AssertEquals(t, "foo", result.Bar)
|
||||
testhelper.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func TestSomethingElse(t *testing.T) {
|
||||
testhelper.CheckEquals(t, "expected", "actual")
|
||||
}
|
||||
```
|
||||
|
||||
`AssertEquals` and `AssertNoErr` will throw a fatal error if a value does not
|
||||
match an expected value or if an error has been declared, respectively. You can
|
||||
also use `CheckEquals` and `CheckNoErr` for the same purpose; the only difference
|
||||
being that `t.Errorf` is raised rather than `t.Fatalf`.
|
||||
|
||||
Here is a truncated example of mocked HTTP responses:
|
||||
|
||||
```go
|
||||
import (
|
||||
"testing"
|
||||
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
fake "github.com/rackspace/gophercloud/testhelper/client"
|
||||
)
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
// Setup the HTTP request multiplexer and server
|
||||
th.SetupHTTP()
|
||||
defer th.TeardownHTTP()
|
||||
|
||||
th.Mux.HandleFunc("/networks/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
|
||||
// Test we're using the correct HTTP method
|
||||
th.TestMethod(t, r, "GET")
|
||||
|
||||
// Test we're setting the auth token
|
||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||
|
||||
// Set the appropriate headers for our mocked response
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
// Set the HTTP body
|
||||
fmt.Fprintf(w, `
|
||||
{
|
||||
"network": {
|
||||
"status": "ACTIVE",
|
||||
"name": "private-network",
|
||||
"admin_state_up": true,
|
||||
"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
|
||||
"shared": true,
|
||||
"id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
|
||||
}
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
// Call our API operation
|
||||
network, err := Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
|
||||
|
||||
// Assert no errors and equality
|
||||
th.AssertNoErr(t, err)
|
||||
th.AssertEquals(t, n.Status, "ACTIVE")
|
||||
}
|
||||
```
|
||||
|
||||
### Acceptance tests
|
||||
|
||||
As we've already mentioned, unit tests have a very narrow and confined focus -
|
||||
they test small units of behaviour. Acceptance tests on the other hand have a
|
||||
far larger scope: they are fully functional tests that test the entire API of a
|
||||
service in one fell swoop. They don't care about unit isolation or mocking
|
||||
expectations, they instead do a full run-through and consequently test how the
|
||||
entire system _integrates_ together. When an API satisfies expectations, it
|
||||
proves by default that the requirements for a contract have been met.
|
||||
|
||||
Please be aware that acceptance tests will hit a live API - and may incur
|
||||
service charges from your provider. Although most tests handle their own
|
||||
teardown procedures, it is always worth manually checking that resources are
|
||||
deleted after the test suite finishes.
|
||||
|
||||
### Running tests
|
||||
|
||||
To run all tests:
|
||||
|
||||
```bash
|
||||
go test ./...
|
||||
```
|
||||
|
||||
To run all tests with verbose output:
|
||||
|
||||
```bash
|
||||
go test -v ./...
|
||||
```
|
||||
|
||||
To run tests that match certain [build tags]():
|
||||
|
||||
```bash
|
||||
go test -tags "foo bar" ./...
|
||||
```
|
||||
|
||||
To run tests for a particular sub-package:
|
||||
|
||||
```bash
|
||||
cd ./path/to/package && go test .
|
||||
```
|
||||
|
||||
## Basic style guide
|
||||
|
||||
We follow the standard formatting recommendations and language idioms set out
|
||||
in the [Effective Go](https://golang.org/doc/effective_go.html) guide. It's
|
||||
definitely worth reading - but the relevant sections are
|
||||
[formatting](https://golang.org/doc/effective_go.html#formatting)
|
||||
and [names](https://golang.org/doc/effective_go.html#names).
|
||||
|
||||
## 5 ways to get involved
|
||||
|
||||
There are five main ways you can get involved in our open-source project, and
|
||||
each is described briefly below. Once you've made up your mind and decided on
|
||||
your fix, you will need to follow the same basic steps that all submissions are
|
||||
required to adhere to:
|
||||
|
||||
1. [fork](https://help.github.com/articles/fork-a-repo/) the `rackspace/gophercloud` repository
|
||||
2. checkout a [new branch](https://github.com/Kunena/Kunena-Forum/wiki/Create-a-new-branch-with-git-and-manage-branches)
|
||||
3. submit your branch as a [pull request](https://help.github.com/articles/creating-a-pull-request/)
|
||||
|
||||
### 1. Providing feedback
|
||||
|
||||
On of the easiest ways to get readily involved in our project is to let us know
|
||||
about your experiences using our SDK. Feedback like this is incredibly useful
|
||||
to us, because it allows us to refine and change features based on what our
|
||||
users want and expect of us. There are a bunch of ways to get in contact! You
|
||||
can [ping us](mailto:sdk-support@rackspace.com) via e-mail, talk to us on irc
|
||||
(#rackspace-dev on freenode), [tweet us](https://twitter.com/rackspace), or
|
||||
submit an issue on our [bug tracker](/issues). Things you might like to tell us
|
||||
are:
|
||||
|
||||
* how easy was it to start using our SDK?
|
||||
* did it meet your expectations? If not, why not?
|
||||
* did our documentation help or hinder you?
|
||||
* what could we improve in general?
|
||||
|
||||
### 2. Fixing bugs
|
||||
|
||||
If you want to start fixing open bugs, we'd really appreciate that! Bug fixing
|
||||
is central to any project. The best way to get started is by heading to our
|
||||
[bug tracker](https://github.com/rackspace/gophercloud/issues) and finding open
|
||||
bugs that you think nobody is working on. It might be useful to comment on the
|
||||
thread to see the current state of the issue and if anybody has made any
|
||||
breakthroughs on it so far.
|
||||
|
||||
### 3. Improving documentation
|
||||
|
||||
We have three forms of documentation:
|
||||
|
||||
* short README documents that briefly introduce a topic
|
||||
* reference documentation on [godoc.org](http://godoc.org) that is automatically
|
||||
generated from source code comments
|
||||
* user documentation on our [homepage](http://gophercloud.io) that includes
|
||||
getting started guides, installation guides and code samples
|
||||
|
||||
If you feel that a certain section could be improved - whether it's to clarify
|
||||
ambiguity, correct a technical mistake, or to fix a grammatical error - please
|
||||
feel entitled to do so! We welcome doc pull requests with the same childlike
|
||||
enthusiasm as any other contribution!
|
||||
|
||||
### 4. Optimizing existing features
|
||||
|
||||
If you would like to improve or optimize an existing feature, please be aware
|
||||
that we adhere to [semantic versioning](http://semver.org) - which means that
|
||||
we cannot introduce breaking changes to the API without a major version change
|
||||
(v1.x -> v2.x). Making that leap is a big step, so we encourage contributors to
|
||||
refactor rather than rewrite. Running tests will prevent regression and avoid
|
||||
the possibility of breaking somebody's current implementation.
|
||||
|
||||
Another tip is to keep the focus of your work as small as possible - try not to
|
||||
introduce a change that affects lots and lots of files because it introduces
|
||||
added risk and increases the cognitive load on the reviewers checking your
|
||||
work. Change-sets which are easily understood and will not negatively impact
|
||||
users are more likely to be integrated quickly.
|
||||
|
||||
Lastly, if you're seeking to optimize a particular operation, you should try to
|
||||
demonstrate a negative performance impact - perhaps using go's inbuilt
|
||||
[benchmark capabilities](http://dave.cheney.net/2013/06/30/how-to-write-benchmarks-in-go).
|
||||
|
||||
### 5. Working on a new feature
|
||||
|
||||
If you've found something we've left out, definitely feel free to start work on
|
||||
introducing that feature. It's always useful to open an issue or submit a pull
|
||||
request early on to indicate your intent to a core contributor - this enables
|
||||
quick/early feedback and can help steer you in the right direction by avoiding
|
||||
known issues. It might also help you avoid losing time implementing something
|
||||
that might not ever work. One tip is to prefix your Pull Request issue title
|
||||
with [wip] - then people know it's a work in progress.
|
||||
|
||||
You must ensure that all of your work is well tested - both in terms of unit
|
||||
and acceptance tests. Untested code will not be merged because it introduces
|
||||
too much of a risk to end-users.
|
||||
|
||||
Happy hacking!
|
12
Godeps/_workspace/src/github.com/rackspace/gophercloud/CONTRIBUTORS.md
generated
vendored
Normal file
12
Godeps/_workspace/src/github.com/rackspace/gophercloud/CONTRIBUTORS.md
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
Contributors
|
||||
============
|
||||
|
||||
| Name | Email |
|
||||
| ---- | ----- |
|
||||
| Samuel A. Falvo II | <sam.falvo@rackspace.com>
|
||||
| Glen Campbell | <glen.campbell@rackspace.com>
|
||||
| Jesse Noller | <jesse.noller@rackspace.com>
|
||||
| Jon Perritt | <jon.perritt@rackspace.com>
|
||||
| Ash Wilson | <ash.wilson@rackspace.com>
|
||||
| Jamie Hannaford | <jamie.hannaford@rackspace.com>
|
||||
| Don Schenck | don.schenck@rackspace.com>
|
5
Godeps/_workspace/src/github.com/rackspace/gophercloud/Godeps/Godeps.json
generated
vendored
Normal file
5
Godeps/_workspace/src/github.com/rackspace/gophercloud/Godeps/Godeps.json
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"ImportPath": "github.com/rackspace/gophercloud",
|
||||
"GoVersion": "go1.3.3",
|
||||
"Deps": []
|
||||
}
|
5
Godeps/_workspace/src/github.com/rackspace/gophercloud/Godeps/Readme
generated
vendored
Normal file
5
Godeps/_workspace/src/github.com/rackspace/gophercloud/Godeps/Readme
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
This directory tree is generated automatically by godep.
|
||||
|
||||
Please do not edit.
|
||||
|
||||
See https://github.com/tools/godep for more information.
|
|
@ -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
|
|
@ -0,0 +1,161 @@
|
|||
# Gophercloud: the OpenStack SDK for Go
|
||||
[](https://travis-ci.org/rackspace/gophercloud)
|
||||
|
||||
Gophercloud is a flexible SDK that allows you to consume and work with OpenStack
|
||||
clouds in a simple and idiomatic way using golang. Many services are supported,
|
||||
including Compute, Block Storage, Object Storage, Networking, and Identity.
|
||||
Each service API is backed with getting started guides, code samples, reference
|
||||
documentation, unit tests and acceptance tests.
|
||||
|
||||
## Useful links
|
||||
|
||||
* [Gophercloud homepage](http://gophercloud.io)
|
||||
* [Reference documentation](http://godoc.org/github.com/rackspace/gophercloud)
|
||||
* [Getting started guides](http://gophercloud.io/docs)
|
||||
* [Effective Go](https://golang.org/doc/effective_go.html)
|
||||
|
||||
## How to install
|
||||
|
||||
Before installing, you need to ensure that your [GOPATH environment variable](https://golang.org/doc/code.html#GOPATH)
|
||||
is pointing to an appropriate directory where you want to install Gophercloud:
|
||||
|
||||
```bash
|
||||
mkdir $HOME/go
|
||||
export GOPATH=$HOME/go
|
||||
```
|
||||
|
||||
To protect yourself against changes in your dependencies, we highly recommend choosing a
|
||||
[dependency management solution](https://code.google.com/p/go-wiki/wiki/PackageManagementTools) for
|
||||
your projects, such as [godep](https://github.com/tools/godep). Once this is set up, you can install
|
||||
Gophercloud as a dependency like so:
|
||||
|
||||
```bash
|
||||
go get github.com/rackspace/gophercloud
|
||||
|
||||
# Edit your code to import relevant packages from "github.com/rackspace/gophercloud"
|
||||
|
||||
godep save ./...
|
||||
```
|
||||
|
||||
This will install all the source files you need into a `Godeps/_workspace` directory, which is
|
||||
referenceable from your own source files when you use the `godep go` command.
|
||||
|
||||
## Getting started
|
||||
|
||||
### Credentials
|
||||
|
||||
Because you'll be hitting an API, you will need to retrieve your OpenStack
|
||||
credentials and either store them as environment variables or in your local Go
|
||||
files. The first method is recommended because it decouples credential
|
||||
information from source code, allowing you to push the latter to your version
|
||||
control system without any security risk.
|
||||
|
||||
You will need to retrieve the following:
|
||||
|
||||
* username
|
||||
* password
|
||||
* tenant name or tenant ID
|
||||
* a valid Keystone identity URL
|
||||
|
||||
For users that have the OpenStack dashboard installed, there's a shortcut. If
|
||||
you visit the `project/access_and_security` path in Horizon and click on the
|
||||
"Download OpenStack RC File" button at the top right hand corner, you will
|
||||
download a bash file that exports all of your access details to environment
|
||||
variables. To execute the file, run `source admin-openrc.sh` and you will be
|
||||
prompted for your password.
|
||||
|
||||
### Authentication
|
||||
|
||||
Once you have access to your credentials, you can begin plugging them into
|
||||
Gophercloud. The next step is authentication, and this is handled by a base
|
||||
"Provider" struct. To get one, you can either pass in your credentials
|
||||
explicitly, or tell Gophercloud to use environment variables:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/openstack"
|
||||
"github.com/rackspace/gophercloud/openstack/utils"
|
||||
)
|
||||
|
||||
// Option 1: Pass in the values yourself
|
||||
opts := gophercloud.AuthOptions{
|
||||
IdentityEndpoint: "https://my-openstack.com:5000/v2.0",
|
||||
Username: "{username}",
|
||||
Password: "{password}",
|
||||
TenantID: "{tenant_id}",
|
||||
}
|
||||
|
||||
// Option 2: Use a utility function to retrieve all your environment variables
|
||||
opts, err := openstack.AuthOptionsFromEnv()
|
||||
```
|
||||
|
||||
Once you have the `opts` variable, you can pass it in and get back a
|
||||
`ProviderClient` struct:
|
||||
|
||||
```go
|
||||
provider, err := openstack.AuthenticatedClient(opts)
|
||||
```
|
||||
|
||||
The `ProviderClient` is the top-level client that all of your OpenStack services
|
||||
derive from. The provider contains all of the authentication details that allow
|
||||
your Go code to access the API - such as the base URL and token ID.
|
||||
|
||||
### Provision a server
|
||||
|
||||
Once we have a base Provider, we inject it as a dependency into each OpenStack
|
||||
service. In order to work with the Compute API, we need a Compute service
|
||||
client; which can be created like so:
|
||||
|
||||
```go
|
||||
client, err := openstack.NewComputeV2(provider, gophercloud.EndpointOpts{
|
||||
Region: os.Getenv("OS_REGION_NAME"),
|
||||
})
|
||||
```
|
||||
|
||||
We then use this `client` for any Compute API operation we want. In our case,
|
||||
we want to provision a new server - so we invoke the `Create` method and pass
|
||||
in the flavor ID (hardware specification) and image ID (operating system) we're
|
||||
interested in:
|
||||
|
||||
```go
|
||||
import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
|
||||
server, err := servers.Create(client, servers.CreateOpts{
|
||||
Name: "My new server!",
|
||||
FlavorRef: "flavor_id",
|
||||
ImageRef: "image_id",
|
||||
}).Extract()
|
||||
```
|
||||
|
||||
If you are unsure about what images and flavors are, you can read our [Compute
|
||||
Getting Started guide](http://gophercloud.io/docs/compute). The above code
|
||||
sample creates a new server with the parameters, and embodies the new resource
|
||||
in the `server` variable (a
|
||||
[`servers.Server`](http://godoc.org/github.com/rackspace/gophercloud) struct).
|
||||
|
||||
### Next steps
|
||||
|
||||
Cool! You've handled authentication, got your `ProviderClient` and provisioned
|
||||
a new server. You're now ready to use more OpenStack services.
|
||||
|
||||
* [Getting started with Compute](http://gophercloud.io/docs/compute)
|
||||
* [Getting started with Object Storage](http://gophercloud.io/docs/object-storage)
|
||||
* [Getting started with Networking](http://gophercloud.io/docs/networking)
|
||||
* [Getting started with Block Storage](http://gophercloud.io/docs/block-storage)
|
||||
* [Getting started with Identity](http://gophercloud.io/docs/identity)
|
||||
|
||||
## Contributing
|
||||
|
||||
Engaging the community and lowering barriers for contributors is something we
|
||||
care a lot about. For this reason, we've taken the time to write a [contributing
|
||||
guide](./CONTRIBUTING.md) for folks interested in getting involved in our project.
|
||||
If you're not sure how you can get involved, feel free to submit an issue or
|
||||
[e-mail us](mailto:sdk-support@rackspace.com) privately. You don't need to be a
|
||||
Go expert - all members of the community are welcome!
|
||||
|
||||
## Help and feedback
|
||||
|
||||
If you're struggling with something or have spotted a potential bug, feel free
|
||||
to submit an issue to our [bug tracker](/issues) or e-mail us directly at
|
||||
[sdk-support@rackspace.com](mailto:sdk-support@rackspace.com).
|
338
Godeps/_workspace/src/github.com/rackspace/gophercloud/UPGRADING.md
generated
vendored
Normal file
338
Godeps/_workspace/src/github.com/rackspace/gophercloud/UPGRADING.md
generated
vendored
Normal file
|
@ -0,0 +1,338 @@
|
|||
# Upgrading to v1.0.0
|
||||
|
||||
With the arrival of this new major version increment, the unfortunate news is
|
||||
that breaking changes have been introduced to existing services. The API
|
||||
has been completely rewritten from the ground up to make the library more
|
||||
extensible, maintainable and easy-to-use.
|
||||
|
||||
Below we've compiled upgrade instructions for the various services that
|
||||
existed before. If you have a specific issue that is not addressed below,
|
||||
please [submit an issue](/issues/new) or
|
||||
[e-mail our support team](mailto:sdk-support@rackspace.com).
|
||||
|
||||
* [Authentication](#authentication)
|
||||
* [Servers](#servers)
|
||||
* [List servers](#list-servers)
|
||||
* [Get server details](#get-server-details)
|
||||
* [Create server](#create-server)
|
||||
* [Resize server](#resize-server)
|
||||
* [Reboot server](#reboot-server)
|
||||
* [Update server](#update-server)
|
||||
* [Rebuild server](#rebuild-server)
|
||||
* [Change admin password](#change-admin-password)
|
||||
* [Delete server](#delete-server)
|
||||
* [Rescue server](#rescue-server)
|
||||
* [Images and flavors](#images-and-flavors)
|
||||
* [List images](#list-images)
|
||||
* [List flavors](#list-flavors)
|
||||
* [Create/delete image](#createdelete-image)
|
||||
* [Other](#other)
|
||||
* [List keypairs](#list-keypairs)
|
||||
* [Create/delete keypair](#createdelete-keypair)
|
||||
* [List IP addresses](#list-ip-addresses)
|
||||
|
||||
# Authentication
|
||||
|
||||
One of the major differences that this release introduces is the level of
|
||||
sub-packaging to differentiate between services and providers. You now have
|
||||
the option of authenticating with OpenStack and other providers (like Rackspace).
|
||||
|
||||
To authenticate with a vanilla OpenStack installation, you can either specify
|
||||
your credentials like this:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/openstack"
|
||||
)
|
||||
|
||||
opts := gophercloud.AuthOptions{
|
||||
IdentityEndpoint: "https://my-openstack.com:5000/v2.0",
|
||||
Username: "{username}",
|
||||
Password: "{password}",
|
||||
TenantID: "{tenant_id}",
|
||||
}
|
||||
```
|
||||
|
||||
Or have them pulled in through environment variables, like this:
|
||||
|
||||
```go
|
||||
opts, err := openstack.AuthOptionsFromEnv()
|
||||
```
|
||||
|
||||
Once you have your `AuthOptions` struct, you pass it in to get back a `Provider`,
|
||||
like so:
|
||||
|
||||
```go
|
||||
provider, err := openstack.AuthenticatedClient(opts)
|
||||
```
|
||||
|
||||
This provider is the top-level structure that all services are created from.
|
||||
|
||||
# Servers
|
||||
|
||||
Before you can interact with the Compute API, you need to retrieve a
|
||||
`gophercloud.ServiceClient`. To do this:
|
||||
|
||||
```go
|
||||
// Define your region, etc.
|
||||
opts := gophercloud.EndpointOpts{Region: "RegionOne"}
|
||||
|
||||
client, err := openstack.NewComputeV2(provider, opts)
|
||||
```
|
||||
|
||||
## List servers
|
||||
|
||||
All operations that involve API collections (servers, flavors, images) now use
|
||||
the `pagination.Pager` interface. This interface represents paginated entities
|
||||
that can be iterated over.
|
||||
|
||||
Once you have a Pager, you can then pass a callback function into its `EachPage`
|
||||
method, and this will allow you to traverse over the collection and execute
|
||||
arbitrary functionality. So, an example with list servers:
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
)
|
||||
|
||||
// We have the option of filtering the server list. If we want the full
|
||||
// collection, leave it as an empty struct or nil
|
||||
opts := servers.ListOpts{Name: "server_1"}
|
||||
|
||||
// Retrieve a pager (i.e. a paginated collection)
|
||||
pager := servers.List(client, opts)
|
||||
|
||||
// Define an anonymous function to be executed on each page's iteration
|
||||
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
serverList, err := servers.ExtractServers(page)
|
||||
|
||||
// `s' will be a servers.Server struct
|
||||
for _, s := range serverList {
|
||||
fmt.Printf("We have a server. ID=%s, Name=%s", s.ID, s.Name)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Get server details
|
||||
|
||||
```go
|
||||
import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
|
||||
// Get the HTTP result
|
||||
response := servers.Get(client, "server_id")
|
||||
|
||||
// Extract a Server struct from the response
|
||||
server, err := response.Extract()
|
||||
```
|
||||
|
||||
## Create server
|
||||
|
||||
```go
|
||||
import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
|
||||
// Define our options
|
||||
opts := servers.CreateOpts{
|
||||
Name: "new_server",
|
||||
FlavorRef: "flavorID",
|
||||
ImageRef: "imageID",
|
||||
}
|
||||
|
||||
// Get our response
|
||||
response := servers.Create(client, opts)
|
||||
|
||||
// Extract
|
||||
server, err := response.Extract()
|
||||
```
|
||||
|
||||
## Change admin password
|
||||
|
||||
```go
|
||||
import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
|
||||
result := servers.ChangeAdminPassword(client, "server_id", "newPassword_&123")
|
||||
```
|
||||
|
||||
## Resize server
|
||||
|
||||
```go
|
||||
import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
|
||||
result := servers.Resize(client, "server_id", "new_flavor_id")
|
||||
```
|
||||
|
||||
## Reboot server
|
||||
|
||||
```go
|
||||
import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
|
||||
// You have a choice of two reboot methods: servers.SoftReboot or servers.HardReboot
|
||||
result := servers.Reboot(client, "server_id", servers.SoftReboot)
|
||||
```
|
||||
|
||||
## Update server
|
||||
|
||||
```go
|
||||
import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
|
||||
opts := servers.UpdateOpts{Name: "new_name"}
|
||||
|
||||
server, err := servers.Update(client, "server_id", opts).Extract()
|
||||
```
|
||||
|
||||
## Rebuild server
|
||||
|
||||
```go
|
||||
import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
|
||||
// You have the option of specifying additional options
|
||||
opts := RebuildOpts{
|
||||
Name: "new_name",
|
||||
AdminPass: "admin_password",
|
||||
ImageID: "image_id",
|
||||
Metadata: map[string]string{"owner": "me"},
|
||||
}
|
||||
|
||||
result := servers.Rebuild(client, "server_id", opts)
|
||||
|
||||
// You can extract a servers.Server struct from the HTTP response
|
||||
server, err := result.Extract()
|
||||
```
|
||||
|
||||
## Delete server
|
||||
|
||||
```go
|
||||
import "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
|
||||
response := servers.Delete(client, "server_id")
|
||||
```
|
||||
|
||||
## Rescue server
|
||||
|
||||
The server rescue extension for Compute is not currently supported.
|
||||
|
||||
# Images and flavors
|
||||
|
||||
## List images
|
||||
|
||||
As with listing servers (see above), you first retrieve a Pager, and then pass
|
||||
in a callback over each page:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/images"
|
||||
)
|
||||
|
||||
// We have the option of filtering the image list. If we want the full
|
||||
// collection, leave it as an empty struct
|
||||
opts := images.ListOpts{ChangesSince: "2014-01-01T01:02:03Z", Name: "Ubuntu 12.04"}
|
||||
|
||||
// Retrieve a pager (i.e. a paginated collection)
|
||||
pager := images.List(client, opts)
|
||||
|
||||
// Define an anonymous function to be executed on each page's iteration
|
||||
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
imageList, err := images.ExtractImages(page)
|
||||
|
||||
for _, i := range imageList {
|
||||
// "i" will be an images.Image
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## List flavors
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/flavors"
|
||||
)
|
||||
|
||||
// We have the option of filtering the flavor list. If we want the full
|
||||
// collection, leave it as an empty struct
|
||||
opts := flavors.ListOpts{ChangesSince: "2014-01-01T01:02:03Z", MinRAM: 4}
|
||||
|
||||
// Retrieve a pager (i.e. a paginated collection)
|
||||
pager := flavors.List(client, opts)
|
||||
|
||||
// Define an anonymous function to be executed on each page's iteration
|
||||
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
flavorList, err := networks.ExtractFlavors(page)
|
||||
|
||||
for _, f := range flavorList {
|
||||
// "f" will be a flavors.Flavor
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Create/delete image
|
||||
|
||||
Image management has been shifted to Glance, but unfortunately this service is
|
||||
not supported as of yet. You can, however, list Compute images like so:
|
||||
|
||||
```go
|
||||
import "github.com/rackspace/gophercloud/openstack/compute/v2/images"
|
||||
|
||||
// Retrieve a pager (i.e. a paginated collection)
|
||||
pager := images.List(client, opts)
|
||||
|
||||
// Define an anonymous function to be executed on each page's iteration
|
||||
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
imageList, err := images.ExtractImages(page)
|
||||
|
||||
for _, i := range imageList {
|
||||
// "i" will be an images.Image
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
# Other
|
||||
|
||||
## List keypairs
|
||||
|
||||
```go
|
||||
import "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
|
||||
|
||||
// Retrieve a pager (i.e. a paginated collection)
|
||||
pager := keypairs.List(client, opts)
|
||||
|
||||
// Define an anonymous function to be executed on each page's iteration
|
||||
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
keyList, err := keypairs.ExtractKeyPairs(page)
|
||||
|
||||
for _, k := range keyList {
|
||||
// "k" will be a keypairs.KeyPair
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Create/delete keypairs
|
||||
|
||||
To create a new keypair, you need to specify its name and, optionally, a
|
||||
pregenerated OpenSSH-formatted public key.
|
||||
|
||||
```go
|
||||
import "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
|
||||
|
||||
opts := keypairs.CreateOpts{
|
||||
Name: "new_key",
|
||||
PublicKey: "...",
|
||||
}
|
||||
|
||||
response := keypairs.Create(client, opts)
|
||||
|
||||
key, err := response.Extract()
|
||||
```
|
||||
|
||||
To delete an existing keypair:
|
||||
|
||||
```go
|
||||
response := keypairs.Delete(client, "keypair_id")
|
||||
```
|
||||
|
||||
## List IP addresses
|
||||
|
||||
This operation is not currently supported.
|
57
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/README.md
generated
vendored
Normal file
57
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/README.md
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
# Gophercloud Acceptance tests
|
||||
|
||||
The purpose of these acceptance tests is to validate that SDK features meet
|
||||
the requirements of a contract - to consumers, other parts of the library, and
|
||||
to a remote API.
|
||||
|
||||
> **Note:** Because every test will be run against a real API endpoint, you
|
||||
> may incur bandwidth and service charges for all the resource usage. These
|
||||
> tests *should* remove their remote products automatically. However, there may
|
||||
> be certain cases where this does not happen; always double-check to make sure
|
||||
> you have no stragglers left behind.
|
||||
|
||||
### Step 1. Set environment variables
|
||||
|
||||
A lot of tests rely on environment variables for configuration - so you will need
|
||||
to set them before running the suite. If you're testing against pure OpenStack APIs,
|
||||
you can download a file that contains all of these variables for you: just visit
|
||||
the `project/access_and_security` page in your control panel and click the "Download
|
||||
OpenStack RC File" button at the top right. For all other providers, you will need
|
||||
to set them manually.
|
||||
|
||||
#### Authentication
|
||||
|
||||
|Name|Description|
|
||||
|---|---|
|
||||
|`OS_USERNAME`|Your API username|
|
||||
|`OS_PASSWORD`|Your API password|
|
||||
|`OS_AUTH_URL`|The identity URL you need to authenticate|
|
||||
|`OS_TENANT_NAME`|Your API tenant name|
|
||||
|`OS_TENANT_ID`|Your API tenant ID|
|
||||
|`RS_USERNAME`|Your Rackspace username|
|
||||
|`RS_API_KEY`|Your Rackspace API key|
|
||||
|
||||
#### General
|
||||
|
||||
|Name|Description|
|
||||
|---|---|
|
||||
|`OS_REGION_NAME`|The region you want your resources to reside in|
|
||||
|`RS_REGION`|Rackspace region you want your resource to reside in|
|
||||
|
||||
#### Compute
|
||||
|
||||
|Name|Description|
|
||||
|---|---|
|
||||
|`OS_IMAGE_ID`|The ID of the image your want your server to be based on|
|
||||
|`OS_FLAVOR_ID`|The ID of the flavor you want your server to be based on|
|
||||
|`OS_FLAVOR_ID_RESIZE`|The ID of the flavor you want your server to be resized to|
|
||||
|`RS_IMAGE_ID`|The ID of the image you want servers to be created with|
|
||||
|`RS_FLAVOR_ID`|The ID of the flavor you want your server to be created with|
|
||||
|
||||
### 2. Run the test suite
|
||||
|
||||
From the root directory, run:
|
||||
|
||||
```
|
||||
./script/acceptancetest
|
||||
```
|
70
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/blockstorage/v1/snapshots_test.go
generated
vendored
Normal file
70
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/blockstorage/v1/snapshots_test.go
generated
vendored
Normal file
|
@ -0,0 +1,70 @@
|
|||
// +build acceptance
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/openstack/blockstorage/v1/snapshots"
|
||||
"github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestSnapshots(t *testing.T) {
|
||||
|
||||
client, err := newClient(t)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
v, err := volumes.Create(client, &volumes.CreateOpts{
|
||||
Name: "gophercloud-test-volume",
|
||||
Size: 1,
|
||||
}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
err = volumes.WaitForStatus(client, v.ID, "available", 120)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Created volume: %v\n", v)
|
||||
|
||||
ss, err := snapshots.Create(client, &snapshots.CreateOpts{
|
||||
Name: "gophercloud-test-snapshot",
|
||||
VolumeID: v.ID,
|
||||
}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
err = snapshots.WaitForStatus(client, ss.ID, "available", 120)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Created snapshot: %+v\n", ss)
|
||||
|
||||
err = snapshots.Delete(client, ss.ID).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
err = gophercloud.WaitFor(120, func() (bool, error) {
|
||||
_, err := snapshots.Get(client, ss.ID).Extract()
|
||||
if err != nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Log("Deleted snapshot\n")
|
||||
|
||||
err = volumes.Delete(client, v.ID).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
err = gophercloud.WaitFor(120, func() (bool, error) {
|
||||
_, err := volumes.Get(client, v.ID).Extract()
|
||||
if err != nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Log("Deleted volume\n")
|
||||
}
|
63
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/blockstorage/v1/volumes_test.go
generated
vendored
Normal file
63
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/blockstorage/v1/volumes_test.go
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
// +build acceptance blockstorage
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/openstack"
|
||||
"github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func newClient(t *testing.T) (*gophercloud.ServiceClient, error) {
|
||||
ao, err := openstack.AuthOptionsFromEnv()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
client, err := openstack.AuthenticatedClient(ao)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
return openstack.NewBlockStorageV1(client, gophercloud.EndpointOpts{
|
||||
Region: os.Getenv("OS_REGION_NAME"),
|
||||
})
|
||||
}
|
||||
|
||||
func TestVolumes(t *testing.T) {
|
||||
client, err := newClient(t)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
cv, err := volumes.Create(client, &volumes.CreateOpts{
|
||||
Size: 1,
|
||||
Name: "gophercloud-test-volume",
|
||||
}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
defer func() {
|
||||
err = volumes.WaitForStatus(client, cv.ID, "available", 60)
|
||||
th.AssertNoErr(t, err)
|
||||
err = volumes.Delete(client, cv.ID).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
}()
|
||||
|
||||
_, err = volumes.Update(client, cv.ID, &volumes.UpdateOpts{
|
||||
Name: "gophercloud-updated-volume",
|
||||
}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
v, err := volumes.Get(client, cv.ID).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Got volume: %+v\n", v)
|
||||
|
||||
if v.Name != "gophercloud-updated-volume" {
|
||||
t.Errorf("Unable to update volume: Expected name: gophercloud-updated-volume\nActual name: %s", v.Name)
|
||||
}
|
||||
|
||||
err = volumes.List(client, &volumes.ListOpts{Name: "gophercloud-updated-volume"}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
vols, err := volumes.ExtractVolumes(page)
|
||||
th.CheckEquals(t, 1, len(vols))
|
||||
return true, err
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
49
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/blockstorage/v1/volumetypes_test.go
generated
vendored
Normal file
49
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/blockstorage/v1/volumetypes_test.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
// +build acceptance
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumetypes"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestVolumeTypes(t *testing.T) {
|
||||
client, err := newClient(t)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
vt, err := volumetypes.Create(client, &volumetypes.CreateOpts{
|
||||
ExtraSpecs: map[string]interface{}{
|
||||
"capabilities": "gpu",
|
||||
"priority": 3,
|
||||
},
|
||||
Name: "gophercloud-test-volumeType",
|
||||
}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
defer func() {
|
||||
time.Sleep(10000 * time.Millisecond)
|
||||
err = volumetypes.Delete(client, vt.ID).ExtractErr()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
t.Logf("Created volume type: %+v\n", vt)
|
||||
|
||||
vt, err = volumetypes.Get(client, vt.ID).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Got volume type: %+v\n", vt)
|
||||
|
||||
err = volumetypes.List(client).EachPage(func(page pagination.Page) (bool, error) {
|
||||
volTypes, err := volumetypes.ExtractVolumeTypes(page)
|
||||
if len(volTypes) != 1 {
|
||||
t.Errorf("Expected 1 volume type, got %d", len(volTypes))
|
||||
}
|
||||
t.Logf("Listing volume types: %+v\n", volTypes)
|
||||
return true, err
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
40
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/client_test.go
generated
vendored
Normal file
40
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/client_test.go
generated
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
// +build acceptance
|
||||
|
||||
package openstack
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/openstack"
|
||||
)
|
||||
|
||||
func TestAuthenticatedClient(t *testing.T) {
|
||||
// Obtain credentials from the environment.
|
||||
ao, err := openstack.AuthOptionsFromEnv()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to acquire credentials: %v", err)
|
||||
}
|
||||
|
||||
client, err := openstack.AuthenticatedClient(ao)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to authenticate: %v", err)
|
||||
}
|
||||
|
||||
if client.TokenID == "" {
|
||||
t.Errorf("No token ID assigned to the client")
|
||||
}
|
||||
|
||||
t.Logf("Client successfully acquired a token: %v", client.TokenID)
|
||||
|
||||
// Find the storage service in the service catalog.
|
||||
storage, err := openstack.NewObjectStorageV1(client, gophercloud.EndpointOpts{
|
||||
Region: os.Getenv("OS_REGION_NAME"),
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("Unable to locate a storage service: %v", err)
|
||||
} else {
|
||||
t.Logf("Located a storage service at endpoint: [%s]", storage.Endpoint)
|
||||
}
|
||||
}
|
55
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/bootfromvolume_test.go
generated
vendored
Normal file
55
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/bootfromvolume_test.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
// +build acceptance
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/acceptance/tools"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestBootFromVolume(t *testing.T) {
|
||||
client, err := newClient()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping test that requires server creation in short mode.")
|
||||
}
|
||||
|
||||
choices, err := ComputeChoicesFromEnv()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
name := tools.RandomString("Gophercloud-", 8)
|
||||
t.Logf("Creating server [%s].", name)
|
||||
|
||||
bd := []bootfromvolume.BlockDevice{
|
||||
bootfromvolume.BlockDevice{
|
||||
UUID: choices.ImageID,
|
||||
SourceType: bootfromvolume.Image,
|
||||
VolumeSize: 10,
|
||||
},
|
||||
}
|
||||
|
||||
serverCreateOpts := servers.CreateOpts{
|
||||
Name: name,
|
||||
FlavorRef: choices.FlavorID,
|
||||
ImageRef: choices.ImageID,
|
||||
}
|
||||
server, err := bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{
|
||||
serverCreateOpts,
|
||||
bd,
|
||||
}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("Created server: %+v\n", server)
|
||||
defer servers.Delete(client, server.ID)
|
||||
t.Logf("Deleting server [%s]...", name)
|
||||
}
|
97
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/compute_test.go
generated
vendored
Normal file
97
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/compute_test.go
generated
vendored
Normal file
|
@ -0,0 +1,97 @@
|
|||
// +build acceptance common
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/acceptance/tools"
|
||||
"github.com/rackspace/gophercloud/openstack"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
)
|
||||
|
||||
func newClient() (*gophercloud.ServiceClient, error) {
|
||||
ao, err := openstack.AuthOptionsFromEnv()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := openstack.AuthenticatedClient(ao)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return openstack.NewComputeV2(client, gophercloud.EndpointOpts{
|
||||
Region: os.Getenv("OS_REGION_NAME"),
|
||||
})
|
||||
}
|
||||
|
||||
func waitForStatus(client *gophercloud.ServiceClient, server *servers.Server, status string) error {
|
||||
return tools.WaitFor(func() (bool, error) {
|
||||
latest, err := servers.Get(client, server.ID).Extract()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if latest.Status == status {
|
||||
// Success!
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
})
|
||||
}
|
||||
|
||||
// ComputeChoices contains image and flavor selections for use by the acceptance tests.
|
||||
type ComputeChoices struct {
|
||||
// ImageID contains the ID of a valid image.
|
||||
ImageID string
|
||||
|
||||
// FlavorID contains the ID of a valid flavor.
|
||||
FlavorID string
|
||||
|
||||
// FlavorIDResize contains the ID of a different flavor available on the same OpenStack installation, that is distinct
|
||||
// from FlavorID.
|
||||
FlavorIDResize string
|
||||
}
|
||||
|
||||
// ComputeChoicesFromEnv populates a ComputeChoices struct from environment variables.
|
||||
// If any required state is missing, an `error` will be returned that enumerates the missing properties.
|
||||
func ComputeChoicesFromEnv() (*ComputeChoices, error) {
|
||||
imageID := os.Getenv("OS_IMAGE_ID")
|
||||
flavorID := os.Getenv("OS_FLAVOR_ID")
|
||||
flavorIDResize := os.Getenv("OS_FLAVOR_ID_RESIZE")
|
||||
|
||||
missing := make([]string, 0, 3)
|
||||
if imageID == "" {
|
||||
missing = append(missing, "OS_IMAGE_ID")
|
||||
}
|
||||
if flavorID == "" {
|
||||
missing = append(missing, "OS_FLAVOR_ID")
|
||||
}
|
||||
if flavorIDResize == "" {
|
||||
missing = append(missing, "OS_FLAVOR_ID_RESIZE")
|
||||
}
|
||||
|
||||
notDistinct := ""
|
||||
if flavorID == flavorIDResize {
|
||||
notDistinct = "OS_FLAVOR_ID and OS_FLAVOR_ID_RESIZE must be distinct."
|
||||
}
|
||||
|
||||
if len(missing) > 0 || notDistinct != "" {
|
||||
text := "You're missing some important setup:\n"
|
||||
if len(missing) > 0 {
|
||||
text += " * These environment variables must be provided: " + strings.Join(missing, ", ") + "\n"
|
||||
}
|
||||
if notDistinct != "" {
|
||||
text += " * " + notDistinct + "\n"
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf(text)
|
||||
}
|
||||
|
||||
return &ComputeChoices{ImageID: imageID, FlavorID: flavorID, FlavorIDResize: flavorIDResize}, nil
|
||||
}
|
47
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/extension_test.go
generated
vendored
Normal file
47
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/extension_test.go
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
// +build acceptance compute extensionss
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestListExtensions(t *testing.T) {
|
||||
client, err := newClient()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
err = extensions.List(client).EachPage(func(page pagination.Page) (bool, error) {
|
||||
t.Logf("--- Page ---")
|
||||
|
||||
exts, err := extensions.ExtractExtensions(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for i, ext := range exts {
|
||||
t.Logf("[%02d] name=[%s]\n", i, ext.Name)
|
||||
t.Logf(" alias=[%s]\n", ext.Alias)
|
||||
t.Logf(" description=[%s]\n", ext.Description)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func TestGetExtension(t *testing.T) {
|
||||
client, err := newClient()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
ext, err := extensions.Get(client, "os-admin-actions").Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Extension details:")
|
||||
t.Logf(" name=[%s]\n", ext.Name)
|
||||
t.Logf(" namespace=[%s]\n", ext.Namespace)
|
||||
t.Logf(" alias=[%s]\n", ext.Alias)
|
||||
t.Logf(" description=[%s]\n", ext.Description)
|
||||
t.Logf(" updated=[%s]\n", ext.Updated)
|
||||
}
|
57
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/flavors_test.go
generated
vendored
Normal file
57
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/flavors_test.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
// +build acceptance compute flavors
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/flavors"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
func TestListFlavors(t *testing.T) {
|
||||
client, err := newClient()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create a compute client: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("ID\tRegion\tName\tStatus\tCreated")
|
||||
|
||||
pager := flavors.ListDetail(client, nil)
|
||||
count, pages := 0, 0
|
||||
pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
t.Logf("---")
|
||||
pages++
|
||||
flavors, err := flavors.ExtractFlavors(page)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, f := range flavors {
|
||||
t.Logf("%s\t%s\t%d\t%d\t%d", f.ID, f.Name, f.RAM, f.Disk, f.VCPUs)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
t.Logf("--------\n%d flavors listed on %d pages.", count, pages)
|
||||
}
|
||||
|
||||
func TestGetFlavor(t *testing.T) {
|
||||
client, err := newClient()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create a compute client: %v", err)
|
||||
}
|
||||
|
||||
choices, err := ComputeChoicesFromEnv()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
flavor, err := flavors.Get(client, choices.FlavorID).Extract()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to get flavor information: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("Flavor: %#v", flavor)
|
||||
}
|
37
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/images_test.go
generated
vendored
Normal file
37
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/images_test.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
// +build acceptance compute images
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/images"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
func TestListImages(t *testing.T) {
|
||||
client, err := newClient()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create a compute: client: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("ID\tRegion\tName\tStatus\tCreated")
|
||||
|
||||
pager := images.ListDetail(client, nil)
|
||||
count, pages := 0, 0
|
||||
pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
pages++
|
||||
images, err := images.ExtractImages(page)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, i := range images {
|
||||
t.Logf("%s\t%s\t%s\t%s", i.ID, i.Name, i.Status, i.Created)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
t.Logf("--------\n%d images listed on %d pages.", count, pages)
|
||||
}
|
74
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/keypairs_test.go
generated
vendored
Normal file
74
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/keypairs_test.go
generated
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
// +build acceptance
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/acceptance/tools"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
|
||||
"code.google.com/p/go.crypto/ssh"
|
||||
)
|
||||
|
||||
const keyName = "gophercloud_test_key_pair"
|
||||
|
||||
func TestCreateServerWithKeyPair(t *testing.T) {
|
||||
client, err := newClient()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping test that requires server creation in short mode.")
|
||||
}
|
||||
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
publicKey := privateKey.PublicKey
|
||||
pub, err := ssh.NewPublicKey(&publicKey)
|
||||
th.AssertNoErr(t, err)
|
||||
pubBytes := ssh.MarshalAuthorizedKey(pub)
|
||||
pk := string(pubBytes)
|
||||
|
||||
kp, err := keypairs.Create(client, keypairs.CreateOpts{
|
||||
Name: keyName,
|
||||
PublicKey: pk,
|
||||
}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Created key pair: %s\n", kp)
|
||||
|
||||
choices, err := ComputeChoicesFromEnv()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
name := tools.RandomString("Gophercloud-", 8)
|
||||
t.Logf("Creating server [%s] with key pair.", name)
|
||||
|
||||
serverCreateOpts := servers.CreateOpts{
|
||||
Name: name,
|
||||
FlavorRef: choices.FlavorID,
|
||||
ImageRef: choices.ImageID,
|
||||
}
|
||||
|
||||
server, err := servers.Create(client, keypairs.CreateOptsExt{
|
||||
serverCreateOpts,
|
||||
keyName,
|
||||
}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
defer servers.Delete(client, server.ID)
|
||||
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
|
||||
t.Fatalf("Unable to wait for server: %v", err)
|
||||
}
|
||||
|
||||
server, err = servers.Get(client, server.ID).Extract()
|
||||
t.Logf("Created server: %+v\n", server)
|
||||
th.AssertNoErr(t, err)
|
||||
th.AssertEquals(t, server.KeyName, keyName)
|
||||
|
||||
t.Logf("Deleting key pair [%s]...", kp.Name)
|
||||
err = keypairs.Delete(client, keyName).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Deleting server [%s]...", name)
|
||||
}
|
3
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/pkg.go
generated
vendored
Normal file
3
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/pkg.go
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
// The v2 package contains acceptance tests for the Openstack Compute V2 service.
|
||||
|
||||
package v2
|
72
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/secdefrules_test.go
generated
vendored
Normal file
72
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/secdefrules_test.go
generated
vendored
Normal file
|
@ -0,0 +1,72 @@
|
|||
// +build acceptance compute defsecrules
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/acceptance/tools"
|
||||
dsr "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/defsecrules"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestSecDefRules(t *testing.T) {
|
||||
client, err := newClient()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
id := createDefRule(t, client)
|
||||
|
||||
listDefRules(t, client)
|
||||
|
||||
getDefRule(t, client, id)
|
||||
|
||||
deleteDefRule(t, client, id)
|
||||
}
|
||||
|
||||
func createDefRule(t *testing.T, client *gophercloud.ServiceClient) string {
|
||||
opts := dsr.CreateOpts{
|
||||
FromPort: tools.RandomInt(80, 89),
|
||||
ToPort: tools.RandomInt(90, 99),
|
||||
IPProtocol: "TCP",
|
||||
CIDR: "0.0.0.0/0",
|
||||
}
|
||||
|
||||
rule, err := dsr.Create(client, opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Created default rule %s", rule.ID)
|
||||
|
||||
return rule.ID
|
||||
}
|
||||
|
||||
func listDefRules(t *testing.T, client *gophercloud.ServiceClient) {
|
||||
err := dsr.List(client).EachPage(func(page pagination.Page) (bool, error) {
|
||||
drList, err := dsr.ExtractDefaultRules(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, dr := range drList {
|
||||
t.Logf("Listing default rule %s: Name [%s] From Port [%s] To Port [%s] Protocol [%s]",
|
||||
dr.ID, dr.FromPort, dr.ToPort, dr.IPProtocol)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func getDefRule(t *testing.T, client *gophercloud.ServiceClient, id string) {
|
||||
rule, err := dsr.Get(client, id).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Getting rule %s: %#v", id, rule)
|
||||
}
|
||||
|
||||
func deleteDefRule(t *testing.T, client *gophercloud.ServiceClient, id string) {
|
||||
err := dsr.Delete(client, id).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Deleted rule %s", id)
|
||||
}
|
177
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/secgroup_test.go
generated
vendored
Normal file
177
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/secgroup_test.go
generated
vendored
Normal file
|
@ -0,0 +1,177 @@
|
|||
// +build acceptance compute secgroups
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/acceptance/tools"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestSecGroups(t *testing.T) {
|
||||
client, err := newClient()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
serverID, needsDeletion := findServer(t, client)
|
||||
|
||||
groupID := createSecGroup(t, client)
|
||||
|
||||
listSecGroups(t, client)
|
||||
|
||||
newName := tools.RandomString("secgroup_", 5)
|
||||
updateSecGroup(t, client, groupID, newName)
|
||||
|
||||
getSecGroup(t, client, groupID)
|
||||
|
||||
addRemoveRules(t, client, groupID)
|
||||
|
||||
addServerToSecGroup(t, client, serverID, newName)
|
||||
|
||||
removeServerFromSecGroup(t, client, serverID, newName)
|
||||
|
||||
if needsDeletion {
|
||||
servers.Delete(client, serverID)
|
||||
}
|
||||
|
||||
deleteSecGroup(t, client, groupID)
|
||||
}
|
||||
|
||||
func createSecGroup(t *testing.T, client *gophercloud.ServiceClient) string {
|
||||
opts := secgroups.CreateOpts{
|
||||
Name: tools.RandomString("secgroup_", 5),
|
||||
Description: "something",
|
||||
}
|
||||
|
||||
group, err := secgroups.Create(client, opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Created secgroup %s %s", group.ID, group.Name)
|
||||
|
||||
return group.ID
|
||||
}
|
||||
|
||||
func listSecGroups(t *testing.T, client *gophercloud.ServiceClient) {
|
||||
err := secgroups.List(client).EachPage(func(page pagination.Page) (bool, error) {
|
||||
secGrpList, err := secgroups.ExtractSecurityGroups(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, sg := range secGrpList {
|
||||
t.Logf("Listing secgroup %s: Name [%s] Desc [%s] TenantID [%s]", sg.ID,
|
||||
sg.Name, sg.Description, sg.TenantID)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func updateSecGroup(t *testing.T, client *gophercloud.ServiceClient, id, newName string) {
|
||||
opts := secgroups.UpdateOpts{
|
||||
Name: newName,
|
||||
Description: tools.RandomString("dec_", 10),
|
||||
}
|
||||
group, err := secgroups.Update(client, id, opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Updated %s's name to %s", group.ID, group.Name)
|
||||
}
|
||||
|
||||
func getSecGroup(t *testing.T, client *gophercloud.ServiceClient, id string) {
|
||||
group, err := secgroups.Get(client, id).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Getting %s: %#v", id, group)
|
||||
}
|
||||
|
||||
func addRemoveRules(t *testing.T, client *gophercloud.ServiceClient, id string) {
|
||||
opts := secgroups.CreateRuleOpts{
|
||||
ParentGroupID: id,
|
||||
FromPort: 22,
|
||||
ToPort: 22,
|
||||
IPProtocol: "TCP",
|
||||
CIDR: "0.0.0.0/0",
|
||||
}
|
||||
|
||||
rule, err := secgroups.CreateRule(client, opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Adding rule %s to group %s", rule.ID, id)
|
||||
|
||||
err = secgroups.DeleteRule(client, rule.ID).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Deleted rule %s from group %s", rule.ID, id)
|
||||
}
|
||||
|
||||
func findServer(t *testing.T, client *gophercloud.ServiceClient) (string, bool) {
|
||||
var serverID string
|
||||
var needsDeletion bool
|
||||
|
||||
err := servers.List(client, nil).EachPage(func(page pagination.Page) (bool, error) {
|
||||
sList, err := servers.ExtractServers(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, s := range sList {
|
||||
serverID = s.ID
|
||||
needsDeletion = false
|
||||
|
||||
t.Logf("Found an existing server: ID [%s]", serverID)
|
||||
break
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
if serverID == "" {
|
||||
t.Log("No server found, creating one")
|
||||
|
||||
choices, err := ComputeChoicesFromEnv()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
opts := &servers.CreateOpts{
|
||||
Name: tools.RandomString("secgroup_test_", 5),
|
||||
ImageRef: choices.ImageID,
|
||||
FlavorRef: choices.FlavorID,
|
||||
}
|
||||
|
||||
s, err := servers.Create(client, opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
serverID = s.ID
|
||||
|
||||
t.Logf("Created server %s, waiting for it to build", s.ID)
|
||||
err = servers.WaitForStatus(client, serverID, "ACTIVE", 300)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
needsDeletion = true
|
||||
}
|
||||
|
||||
return serverID, needsDeletion
|
||||
}
|
||||
|
||||
func addServerToSecGroup(t *testing.T, client *gophercloud.ServiceClient, serverID, groupName string) {
|
||||
err := secgroups.AddServerToGroup(client, serverID, groupName).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Adding group %s to server %s", groupName, serverID)
|
||||
}
|
||||
|
||||
func removeServerFromSecGroup(t *testing.T, client *gophercloud.ServiceClient, serverID, groupName string) {
|
||||
err := secgroups.RemoveServerFromGroup(client, serverID, groupName).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Removing group %s from server %s", groupName, serverID)
|
||||
}
|
||||
|
||||
func deleteSecGroup(t *testing.T, client *gophercloud.ServiceClient, id string) {
|
||||
err := secgroups.Delete(client, id).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Deleted group %s", id)
|
||||
}
|
450
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/servers_test.go
generated
vendored
Normal file
450
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/servers_test.go
generated
vendored
Normal file
|
@ -0,0 +1,450 @@
|
|||
// +build acceptance compute servers
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/acceptance/tools"
|
||||
"github.com/rackspace/gophercloud/openstack"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestListServers(t *testing.T) {
|
||||
client, err := newClient()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create a compute client: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("ID\tRegion\tName\tStatus\tIPv4\tIPv6")
|
||||
|
||||
pager := servers.List(client, servers.ListOpts{})
|
||||
count, pages := 0, 0
|
||||
pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
pages++
|
||||
t.Logf("---")
|
||||
|
||||
servers, err := servers.ExtractServers(page)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, s := range servers {
|
||||
t.Logf("%s\t%s\t%s\t%s\t%s\t\n", s.ID, s.Name, s.Status, s.AccessIPv4, s.AccessIPv6)
|
||||
count++
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
t.Logf("--------\n%d servers listed on %d pages.\n", count, pages)
|
||||
}
|
||||
|
||||
func networkingClient() (*gophercloud.ServiceClient, error) {
|
||||
opts, err := openstack.AuthOptionsFromEnv()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
provider, err := openstack.AuthenticatedClient(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{
|
||||
Name: "neutron",
|
||||
Region: os.Getenv("OS_REGION_NAME"),
|
||||
})
|
||||
}
|
||||
|
||||
func createServer(t *testing.T, client *gophercloud.ServiceClient, choices *ComputeChoices) (*servers.Server, error) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping test that requires server creation in short mode.")
|
||||
}
|
||||
|
||||
var network networks.Network
|
||||
|
||||
networkingClient, err := networkingClient()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create a networking client: %v", err)
|
||||
}
|
||||
|
||||
pager := networks.List(networkingClient, networks.ListOpts{Name: "public", Limit: 1})
|
||||
pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
networks, err := networks.ExtractNetworks(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract networks: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
if len(networks) == 0 {
|
||||
t.Fatalf("No networks to attach to server")
|
||||
return false, err
|
||||
}
|
||||
|
||||
network = networks[0]
|
||||
|
||||
return false, nil
|
||||
})
|
||||
|
||||
name := tools.RandomString("ACPTTEST", 16)
|
||||
t.Logf("Attempting to create server: %s\n", name)
|
||||
|
||||
pwd := tools.MakeNewPassword("")
|
||||
|
||||
server, err := servers.Create(client, servers.CreateOpts{
|
||||
Name: name,
|
||||
FlavorRef: choices.FlavorID,
|
||||
ImageRef: choices.ImageID,
|
||||
Networks: []servers.Network{
|
||||
servers.Network{UUID: network.ID},
|
||||
},
|
||||
AdminPass: pwd,
|
||||
}).Extract()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create server: %v", err)
|
||||
}
|
||||
|
||||
th.AssertEquals(t, pwd, server.AdminPass)
|
||||
|
||||
return server, err
|
||||
}
|
||||
|
||||
func TestCreateDestroyServer(t *testing.T) {
|
||||
choices, err := ComputeChoicesFromEnv()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
client, err := newClient()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create a compute client: %v", err)
|
||||
}
|
||||
|
||||
server, err := createServer(t, client, choices)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create server: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
servers.Delete(client, server.ID)
|
||||
t.Logf("Server deleted.")
|
||||
}()
|
||||
|
||||
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
|
||||
t.Fatalf("Unable to wait for server: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateServer(t *testing.T) {
|
||||
client, err := newClient()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create a compute client: %v", err)
|
||||
}
|
||||
|
||||
choices, err := ComputeChoicesFromEnv()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
server, err := createServer(t, client, choices)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer servers.Delete(client, server.ID)
|
||||
|
||||
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
alternateName := tools.RandomString("ACPTTEST", 16)
|
||||
for alternateName == server.Name {
|
||||
alternateName = tools.RandomString("ACPTTEST", 16)
|
||||
}
|
||||
|
||||
t.Logf("Attempting to rename the server to %s.", alternateName)
|
||||
|
||||
updated, err := servers.Update(client, server.ID, servers.UpdateOpts{Name: alternateName}).Extract()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to rename server: %v", err)
|
||||
}
|
||||
|
||||
if updated.ID != server.ID {
|
||||
t.Errorf("Updated server ID [%s] didn't match original server ID [%s]!", updated.ID, server.ID)
|
||||
}
|
||||
|
||||
err = tools.WaitFor(func() (bool, error) {
|
||||
latest, err := servers.Get(client, updated.ID).Extract()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return latest.Name == alternateName, nil
|
||||
})
|
||||
}
|
||||
|
||||
func TestActionChangeAdminPassword(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client, err := newClient()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create a compute client: %v", err)
|
||||
}
|
||||
|
||||
choices, err := ComputeChoicesFromEnv()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
server, err := createServer(t, client, choices)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer servers.Delete(client, server.ID)
|
||||
|
||||
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
randomPassword := tools.MakeNewPassword(server.AdminPass)
|
||||
res := servers.ChangeAdminPassword(client, server.ID, randomPassword)
|
||||
if res.Err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = waitForStatus(client, server, "PASSWORD"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionReboot(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client, err := newClient()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create a compute client: %v", err)
|
||||
}
|
||||
|
||||
choices, err := ComputeChoicesFromEnv()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
server, err := createServer(t, client, choices)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer servers.Delete(client, server.ID)
|
||||
|
||||
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
res := servers.Reboot(client, server.ID, "aldhjflaskhjf")
|
||||
if res.Err == nil {
|
||||
t.Fatal("Expected the SDK to provide an ArgumentError here")
|
||||
}
|
||||
|
||||
t.Logf("Attempting reboot of server %s", server.ID)
|
||||
res = servers.Reboot(client, server.ID, servers.OSReboot)
|
||||
if res.Err != nil {
|
||||
t.Fatalf("Unable to reboot server: %v", err)
|
||||
}
|
||||
|
||||
if err = waitForStatus(client, server, "REBOOT"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionRebuild(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client, err := newClient()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create a compute client: %v", err)
|
||||
}
|
||||
|
||||
choices, err := ComputeChoicesFromEnv()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
server, err := createServer(t, client, choices)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer servers.Delete(client, server.ID)
|
||||
|
||||
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("Attempting to rebuild server %s", server.ID)
|
||||
|
||||
rebuildOpts := servers.RebuildOpts{
|
||||
Name: tools.RandomString("ACPTTEST", 16),
|
||||
AdminPass: tools.MakeNewPassword(server.AdminPass),
|
||||
ImageID: choices.ImageID,
|
||||
}
|
||||
|
||||
rebuilt, err := servers.Rebuild(client, server.ID, rebuildOpts).Extract()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if rebuilt.ID != server.ID {
|
||||
t.Errorf("Expected rebuilt server ID of [%s]; got [%s]", server.ID, rebuilt.ID)
|
||||
}
|
||||
|
||||
if err = waitForStatus(client, rebuilt, "REBUILD"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = waitForStatus(client, rebuilt, "ACTIVE"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func resizeServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server, choices *ComputeChoices) {
|
||||
if err := waitForStatus(client, server, "ACTIVE"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("Attempting to resize server [%s]", server.ID)
|
||||
|
||||
opts := &servers.ResizeOpts{
|
||||
FlavorRef: choices.FlavorIDResize,
|
||||
}
|
||||
if res := servers.Resize(client, server.ID, opts); res.Err != nil {
|
||||
t.Fatal(res.Err)
|
||||
}
|
||||
|
||||
if err := waitForStatus(client, server, "VERIFY_RESIZE"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionResizeConfirm(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
choices, err := ComputeChoicesFromEnv()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
client, err := newClient()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create a compute client: %v", err)
|
||||
}
|
||||
|
||||
server, err := createServer(t, client, choices)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer servers.Delete(client, server.ID)
|
||||
resizeServer(t, client, server, choices)
|
||||
|
||||
t.Logf("Attempting to confirm resize for server %s", server.ID)
|
||||
|
||||
if res := servers.ConfirmResize(client, server.ID); res.Err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionResizeRevert(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
choices, err := ComputeChoicesFromEnv()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
client, err := newClient()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create a compute client: %v", err)
|
||||
}
|
||||
|
||||
server, err := createServer(t, client, choices)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer servers.Delete(client, server.ID)
|
||||
resizeServer(t, client, server, choices)
|
||||
|
||||
t.Logf("Attempting to revert resize for server %s", server.ID)
|
||||
|
||||
if res := servers.RevertResize(client, server.ID); res.Err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerMetadata(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
choices, err := ComputeChoicesFromEnv()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
client, err := newClient()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create a compute client: %v", err)
|
||||
}
|
||||
|
||||
server, err := createServer(t, client, choices)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer servers.Delete(client, server.ID)
|
||||
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
metadata, err := servers.UpdateMetadata(client, server.ID, servers.MetadataOpts{
|
||||
"foo": "bar",
|
||||
"this": "that",
|
||||
}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("UpdateMetadata result: %+v\n", metadata)
|
||||
|
||||
err = servers.DeleteMetadatum(client, server.ID, "foo").ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
metadata, err = servers.CreateMetadatum(client, server.ID, servers.MetadatumOpts{
|
||||
"foo": "baz",
|
||||
}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("CreateMetadatum result: %+v\n", metadata)
|
||||
|
||||
metadata, err = servers.Metadatum(client, server.ID, "foo").Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Metadatum result: %+v\n", metadata)
|
||||
th.AssertEquals(t, "baz", metadata["foo"])
|
||||
|
||||
metadata, err = servers.Metadata(client, server.ID).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Metadata result: %+v\n", metadata)
|
||||
|
||||
metadata, err = servers.ResetMetadata(client, server.ID, servers.MetadataOpts{}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("ResetMetadata result: %+v\n", metadata)
|
||||
th.AssertDeepEquals(t, map[string]string{}, metadata)
|
||||
}
|
46
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v2/extension_test.go
generated
vendored
Normal file
46
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v2/extension_test.go
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
// +build acceptance identity
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
extensions2 "github.com/rackspace/gophercloud/openstack/identity/v2/extensions"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestEnumerateExtensions(t *testing.T) {
|
||||
service := authenticatedClient(t)
|
||||
|
||||
t.Logf("Extensions available on this identity endpoint:")
|
||||
count := 0
|
||||
err := extensions2.List(service).EachPage(func(page pagination.Page) (bool, error) {
|
||||
t.Logf("--- Page %02d ---", count)
|
||||
|
||||
extensions, err := extensions2.ExtractExtensions(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for i, ext := range extensions {
|
||||
t.Logf("[%02d] name=[%s] namespace=[%s]", i, ext.Name, ext.Namespace)
|
||||
t.Logf(" alias=[%s] updated=[%s]", ext.Alias, ext.Updated)
|
||||
t.Logf(" description=[%s]", ext.Description)
|
||||
}
|
||||
|
||||
count++
|
||||
return true, nil
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func TestGetExtension(t *testing.T) {
|
||||
service := authenticatedClient(t)
|
||||
|
||||
ext, err := extensions2.Get(service, "OS-KSCRUD").Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.CheckEquals(t, "OpenStack Keystone User CRUD", ext.Name)
|
||||
th.CheckEquals(t, "http://docs.openstack.org/identity/api/ext/OS-KSCRUD/v1.0", ext.Namespace)
|
||||
th.CheckEquals(t, "OS-KSCRUD", ext.Alias)
|
||||
th.CheckEquals(t, "OpenStack extensions to Keystone v2.0 API enabling User Operations.", ext.Description)
|
||||
}
|
47
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v2/identity_test.go
generated
vendored
Normal file
47
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v2/identity_test.go
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
// +build acceptance identity
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/openstack"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func v2AuthOptions(t *testing.T) gophercloud.AuthOptions {
|
||||
// Obtain credentials from the environment.
|
||||
ao, err := openstack.AuthOptionsFromEnv()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
// Trim out unused fields. Prefer authentication by API key to password.
|
||||
ao.UserID, ao.DomainID, ao.DomainName = "", "", ""
|
||||
if ao.APIKey != "" {
|
||||
ao.Password = ""
|
||||
}
|
||||
|
||||
return ao
|
||||
}
|
||||
|
||||
func createClient(t *testing.T, auth bool) *gophercloud.ServiceClient {
|
||||
ao := v2AuthOptions(t)
|
||||
|
||||
provider, err := openstack.NewClient(ao.IdentityEndpoint)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
if auth {
|
||||
err = openstack.AuthenticateV2(provider, ao)
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
return openstack.NewIdentityV2(provider)
|
||||
}
|
||||
|
||||
func unauthenticatedClient(t *testing.T) *gophercloud.ServiceClient {
|
||||
return createClient(t, false)
|
||||
}
|
||||
|
||||
func authenticatedClient(t *testing.T) *gophercloud.ServiceClient {
|
||||
return createClient(t, true)
|
||||
}
|
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v2/pkg.go
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v2/pkg.go
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
package v2
|
58
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v2/role_test.go
generated
vendored
Normal file
58
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v2/role_test.go
generated
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
// +build acceptance identity roles
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/openstack/identity/v2/extensions/admin/roles"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestRoles(t *testing.T) {
|
||||
client := authenticatedClient(t)
|
||||
|
||||
tenantID := findTenant(t, client)
|
||||
userID := createUser(t, client, tenantID)
|
||||
roleID := listRoles(t, client)
|
||||
|
||||
addUserRole(t, client, tenantID, userID, roleID)
|
||||
|
||||
deleteUserRole(t, client, tenantID, userID, roleID)
|
||||
|
||||
deleteUser(t, client, userID)
|
||||
}
|
||||
|
||||
func listRoles(t *testing.T, client *gophercloud.ServiceClient) string {
|
||||
var roleID string
|
||||
|
||||
err := roles.List(client).EachPage(func(page pagination.Page) (bool, error) {
|
||||
roleList, err := roles.ExtractRoles(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, role := range roleList {
|
||||
t.Logf("Listing role: ID [%s] Name [%s]", role.ID, role.Name)
|
||||
roleID = role.ID
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
return roleID
|
||||
}
|
||||
|
||||
func addUserRole(t *testing.T, client *gophercloud.ServiceClient, tenantID, userID, roleID string) {
|
||||
err := roles.AddUserRole(client, tenantID, userID, roleID).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Added role %s to user %s", roleID, userID)
|
||||
}
|
||||
|
||||
func deleteUserRole(t *testing.T, client *gophercloud.ServiceClient, tenantID, userID, roleID string) {
|
||||
err := roles.DeleteUserRole(client, tenantID, userID, roleID).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Removed role %s from user %s", roleID, userID)
|
||||
}
|
32
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v2/tenant_test.go
generated
vendored
Normal file
32
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v2/tenant_test.go
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
// +build acceptance identity
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
tenants2 "github.com/rackspace/gophercloud/openstack/identity/v2/tenants"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestEnumerateTenants(t *testing.T) {
|
||||
service := authenticatedClient(t)
|
||||
|
||||
t.Logf("Tenants to which your current token grants access:")
|
||||
count := 0
|
||||
err := tenants2.List(service, nil).EachPage(func(page pagination.Page) (bool, error) {
|
||||
t.Logf("--- Page %02d ---", count)
|
||||
|
||||
tenants, err := tenants2.ExtractTenants(page)
|
||||
th.AssertNoErr(t, err)
|
||||
for i, tenant := range tenants {
|
||||
t.Logf("[%02d] name=[%s] id=[%s] description=[%s] enabled=[%v]",
|
||||
i, tenant.Name, tenant.ID, tenant.Description, tenant.Enabled)
|
||||
}
|
||||
|
||||
count++
|
||||
return true, nil
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
38
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v2/token_test.go
generated
vendored
Normal file
38
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v2/token_test.go
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
// +build acceptance identity
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
tokens2 "github.com/rackspace/gophercloud/openstack/identity/v2/tokens"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestAuthenticate(t *testing.T) {
|
||||
ao := v2AuthOptions(t)
|
||||
service := unauthenticatedClient(t)
|
||||
|
||||
// Authenticated!
|
||||
result := tokens2.Create(service, tokens2.WrapOptions(ao))
|
||||
|
||||
// Extract and print the token.
|
||||
token, err := result.ExtractToken()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Acquired token: [%s]", token.ID)
|
||||
t.Logf("The token will expire at: [%s]", token.ExpiresAt.String())
|
||||
t.Logf("The token is valid for tenant: [%#v]", token.Tenant)
|
||||
|
||||
// Extract and print the service catalog.
|
||||
catalog, err := result.ExtractServiceCatalog()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Acquired service catalog listing [%d] services", len(catalog.Entries))
|
||||
for i, entry := range catalog.Entries {
|
||||
t.Logf("[%02d]: name=[%s], type=[%s]", i, entry.Name, entry.Type)
|
||||
for _, endpoint := range entry.Endpoints {
|
||||
t.Logf(" - region=[%s] publicURL=[%s]", endpoint.Region, endpoint.PublicURL)
|
||||
}
|
||||
}
|
||||
}
|
127
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v2/user_test.go
generated
vendored
Normal file
127
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v2/user_test.go
generated
vendored
Normal file
|
@ -0,0 +1,127 @@
|
|||
// +build acceptance identity
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/acceptance/tools"
|
||||
"github.com/rackspace/gophercloud/openstack/identity/v2/tenants"
|
||||
"github.com/rackspace/gophercloud/openstack/identity/v2/users"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestUsers(t *testing.T) {
|
||||
client := authenticatedClient(t)
|
||||
|
||||
tenantID := findTenant(t, client)
|
||||
|
||||
userID := createUser(t, client, tenantID)
|
||||
|
||||
listUsers(t, client)
|
||||
|
||||
getUser(t, client, userID)
|
||||
|
||||
updateUser(t, client, userID)
|
||||
|
||||
listUserRoles(t, client, tenantID, userID)
|
||||
|
||||
deleteUser(t, client, userID)
|
||||
}
|
||||
|
||||
func findTenant(t *testing.T, client *gophercloud.ServiceClient) string {
|
||||
var tenantID string
|
||||
err := tenants.List(client, nil).EachPage(func(page pagination.Page) (bool, error) {
|
||||
tenantList, err := tenants.ExtractTenants(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, t := range tenantList {
|
||||
tenantID = t.ID
|
||||
break
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
return tenantID
|
||||
}
|
||||
|
||||
func createUser(t *testing.T, client *gophercloud.ServiceClient, tenantID string) string {
|
||||
t.Log("Creating user")
|
||||
|
||||
opts := users.CreateOpts{
|
||||
Name: tools.RandomString("user_", 5),
|
||||
Enabled: users.Disabled,
|
||||
TenantID: tenantID,
|
||||
Email: "new_user@foo.com",
|
||||
}
|
||||
|
||||
user, err := users.Create(client, opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Created user %s on tenant %s", user.ID, tenantID)
|
||||
|
||||
return user.ID
|
||||
}
|
||||
|
||||
func listUsers(t *testing.T, client *gophercloud.ServiceClient) {
|
||||
err := users.List(client).EachPage(func(page pagination.Page) (bool, error) {
|
||||
userList, err := users.ExtractUsers(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, user := range userList {
|
||||
t.Logf("Listing user: ID [%s] Name [%s] Email [%s] Enabled? [%s]",
|
||||
user.ID, user.Name, user.Email, strconv.FormatBool(user.Enabled))
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func getUser(t *testing.T, client *gophercloud.ServiceClient, userID string) {
|
||||
_, err := users.Get(client, userID).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Getting user %s", userID)
|
||||
}
|
||||
|
||||
func updateUser(t *testing.T, client *gophercloud.ServiceClient, userID string) {
|
||||
opts := users.UpdateOpts{Name: tools.RandomString("new_name", 5), Email: "new@foo.com"}
|
||||
user, err := users.Update(client, userID, opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Updated user %s: Name [%s] Email [%s]", userID, user.Name, user.Email)
|
||||
}
|
||||
|
||||
func listUserRoles(t *testing.T, client *gophercloud.ServiceClient, tenantID, userID string) {
|
||||
count := 0
|
||||
err := users.ListRoles(client, tenantID, userID).EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
|
||||
roleList, err := users.ExtractRoles(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Listing roles for user %s", userID)
|
||||
|
||||
for _, r := range roleList {
|
||||
t.Logf("- %s (%s)", r.Name, r.ID)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if count == 0 {
|
||||
t.Logf("No roles for user %s", userID)
|
||||
}
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func deleteUser(t *testing.T, client *gophercloud.ServiceClient, userID string) {
|
||||
res := users.Delete(client, userID)
|
||||
th.AssertNoErr(t, res.Err)
|
||||
t.Logf("Deleted user %s", userID)
|
||||
}
|
111
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v3/endpoint_test.go
generated
vendored
Normal file
111
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v3/endpoint_test.go
generated
vendored
Normal file
|
@ -0,0 +1,111 @@
|
|||
// +build acceptance
|
||||
|
||||
package v3
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
endpoints3 "github.com/rackspace/gophercloud/openstack/identity/v3/endpoints"
|
||||
services3 "github.com/rackspace/gophercloud/openstack/identity/v3/services"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
func TestListEndpoints(t *testing.T) {
|
||||
// Create a service client.
|
||||
serviceClient := createAuthenticatedClient(t)
|
||||
if serviceClient == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Use the service to list all available endpoints.
|
||||
pager := endpoints3.List(serviceClient, endpoints3.ListOpts{})
|
||||
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
t.Logf("--- Page ---")
|
||||
|
||||
endpoints, err := endpoints3.ExtractEndpoints(page)
|
||||
if err != nil {
|
||||
t.Fatalf("Error extracting endpoings: %v", err)
|
||||
}
|
||||
|
||||
for _, endpoint := range endpoints {
|
||||
t.Logf("Endpoint: %8s %10s %9s %s",
|
||||
endpoint.ID,
|
||||
endpoint.Availability,
|
||||
endpoint.Name,
|
||||
endpoint.URL)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error while iterating endpoint pages: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNavigateCatalog(t *testing.T) {
|
||||
// Create a service client.
|
||||
client := createAuthenticatedClient(t)
|
||||
if client == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var compute *services3.Service
|
||||
var endpoint *endpoints3.Endpoint
|
||||
|
||||
// Discover the service we're interested in.
|
||||
servicePager := services3.List(client, services3.ListOpts{ServiceType: "compute"})
|
||||
err := servicePager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
part, err := services3.ExtractServices(page)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if compute != nil {
|
||||
t.Fatalf("Expected one service, got more than one page")
|
||||
return false, nil
|
||||
}
|
||||
if len(part) != 1 {
|
||||
t.Fatalf("Expected one service, got %d", len(part))
|
||||
return false, nil
|
||||
}
|
||||
|
||||
compute = &part[0]
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error iterating pages: %v", err)
|
||||
}
|
||||
|
||||
if compute == nil {
|
||||
t.Fatalf("No compute service found.")
|
||||
}
|
||||
|
||||
// Enumerate the endpoints available for this service.
|
||||
computePager := endpoints3.List(client, endpoints3.ListOpts{
|
||||
Availability: gophercloud.AvailabilityPublic,
|
||||
ServiceID: compute.ID,
|
||||
})
|
||||
err = computePager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
part, err := endpoints3.ExtractEndpoints(page)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if endpoint != nil {
|
||||
t.Fatalf("Expected one endpoint, got more than one page")
|
||||
return false, nil
|
||||
}
|
||||
if len(part) != 1 {
|
||||
t.Fatalf("Expected one endpoint, got %d", len(part))
|
||||
return false, nil
|
||||
}
|
||||
|
||||
endpoint = &part[0]
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if endpoint == nil {
|
||||
t.Fatalf("No endpoint found.")
|
||||
}
|
||||
|
||||
t.Logf("Success. The compute endpoint is at %s.", endpoint.URL)
|
||||
}
|
39
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v3/identity_test.go
generated
vendored
Normal file
39
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v3/identity_test.go
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
// +build acceptance
|
||||
|
||||
package v3
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/openstack"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func createAuthenticatedClient(t *testing.T) *gophercloud.ServiceClient {
|
||||
// Obtain credentials from the environment.
|
||||
ao, err := openstack.AuthOptionsFromEnv()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
// Trim out unused fields.
|
||||
ao.Username, ao.TenantID, ao.TenantName = "", "", ""
|
||||
|
||||
if ao.UserID == "" {
|
||||
t.Logf("Skipping identity v3 tests because no OS_USERID is present.")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a client and manually authenticate against v3.
|
||||
providerClient, err := openstack.NewClient(ao.IdentityEndpoint)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to instantiate client: %v", err)
|
||||
}
|
||||
|
||||
err = openstack.AuthenticateV3(providerClient, ao)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to authenticate against identity v3: %v", err)
|
||||
}
|
||||
|
||||
// Create a service client.
|
||||
return openstack.NewIdentityV3(providerClient)
|
||||
}
|
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v3/pkg.go
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v3/pkg.go
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
package v3
|
36
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v3/service_test.go
generated
vendored
Normal file
36
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v3/service_test.go
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
// +build acceptance
|
||||
|
||||
package v3
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
services3 "github.com/rackspace/gophercloud/openstack/identity/v3/services"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
)
|
||||
|
||||
func TestListServices(t *testing.T) {
|
||||
// Create a service client.
|
||||
serviceClient := createAuthenticatedClient(t)
|
||||
if serviceClient == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Use the client to list all available services.
|
||||
pager := services3.List(serviceClient, services3.ListOpts{})
|
||||
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
parts, err := services3.ExtractServices(page)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
t.Logf("--- Page ---")
|
||||
for _, service := range parts {
|
||||
t.Logf("Service: %32s %15s %10s %s", service.ID, service.Type, service.Name, *service.Description)
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error traversing pages: %v", err)
|
||||
}
|
||||
}
|
42
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v3/token_test.go
generated
vendored
Normal file
42
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/identity/v3/token_test.go
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
// +build acceptance
|
||||
|
||||
package v3
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/openstack"
|
||||
tokens3 "github.com/rackspace/gophercloud/openstack/identity/v3/tokens"
|
||||
)
|
||||
|
||||
func TestGetToken(t *testing.T) {
|
||||
// Obtain credentials from the environment.
|
||||
ao, err := openstack.AuthOptionsFromEnv()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to acquire credentials: %v", err)
|
||||
}
|
||||
|
||||
// Trim out unused fields. Skip if we don't have a UserID.
|
||||
ao.Username, ao.TenantID, ao.TenantName = "", "", ""
|
||||
if ao.UserID == "" {
|
||||
t.Logf("Skipping identity v3 tests because no OS_USERID is present.")
|
||||
return
|
||||
}
|
||||
|
||||
// Create an unauthenticated client.
|
||||
provider, err := openstack.NewClient(ao.IdentityEndpoint)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to instantiate client: %v", err)
|
||||
}
|
||||
|
||||
// Create a service client.
|
||||
service := openstack.NewIdentityV3(provider)
|
||||
|
||||
// Use the service to create a token.
|
||||
token, err := tokens3.Create(service, ao, nil).Extract()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to get token: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("Acquired token: %s", token.ID)
|
||||
}
|
51
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/apiversion_test.go
generated
vendored
Normal file
51
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/apiversion_test.go
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
// +build acceptance networking
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/apiversions"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestListAPIVersions(t *testing.T) {
|
||||
Setup(t)
|
||||
defer Teardown()
|
||||
|
||||
pager := apiversions.ListVersions(Client)
|
||||
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
t.Logf("--- Page ---")
|
||||
|
||||
versions, err := apiversions.ExtractAPIVersions(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, v := range versions {
|
||||
t.Logf("API Version: ID [%s] Status [%s]", v.ID, v.Status)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.CheckNoErr(t, err)
|
||||
}
|
||||
|
||||
func TestListAPIResources(t *testing.T) {
|
||||
Setup(t)
|
||||
defer Teardown()
|
||||
|
||||
pager := apiversions.ListVersionResources(Client, "v2.0")
|
||||
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
t.Logf("--- Page ---")
|
||||
|
||||
vrs, err := apiversions.ExtractVersionResources(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, vr := range vrs {
|
||||
t.Logf("Network: Name [%s] Collection [%s]", vr.Name, vr.Collection)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.CheckNoErr(t, err)
|
||||
}
|
39
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/common.go
generated
vendored
Normal file
39
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/common.go
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
package v2
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/openstack"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
var Client *gophercloud.ServiceClient
|
||||
|
||||
func NewClient() (*gophercloud.ServiceClient, error) {
|
||||
opts, err := openstack.AuthOptionsFromEnv()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
provider, err := openstack.AuthenticatedClient(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{
|
||||
Name: "neutron",
|
||||
Region: os.Getenv("OS_REGION_NAME"),
|
||||
})
|
||||
}
|
||||
|
||||
func Setup(t *testing.T) {
|
||||
client, err := NewClient()
|
||||
th.AssertNoErr(t, err)
|
||||
Client = client
|
||||
}
|
||||
|
||||
func Teardown() {
|
||||
Client = nil
|
||||
}
|
45
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/extension_test.go
generated
vendored
Normal file
45
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/extension_test.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
// +build acceptance networking
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestListExts(t *testing.T) {
|
||||
Setup(t)
|
||||
defer Teardown()
|
||||
|
||||
pager := extensions.List(Client)
|
||||
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
t.Logf("--- Page ---")
|
||||
|
||||
exts, err := extensions.ExtractExtensions(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, ext := range exts {
|
||||
t.Logf("Extension: Name [%s] Description [%s]", ext.Name, ext.Description)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.CheckNoErr(t, err)
|
||||
}
|
||||
|
||||
func TestGetExt(t *testing.T) {
|
||||
Setup(t)
|
||||
defer Teardown()
|
||||
|
||||
ext, err := extensions.Get(Client, "service-type").Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, ext.Updated, "2013-01-20T00:00:00-00:00")
|
||||
th.AssertEquals(t, ext.Name, "Neutron Service Type Management")
|
||||
th.AssertEquals(t, ext.Namespace, "http://docs.openstack.org/ext/neutron/service-type/api/v1.0")
|
||||
th.AssertEquals(t, ext.Alias, "service-type")
|
||||
th.AssertEquals(t, ext.Description, "API for retrieving service providers for Neutron advanced services")
|
||||
}
|
300
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/extensions/layer3_test.go
generated
vendored
Normal file
300
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/extensions/layer3_test.go
generated
vendored
Normal file
|
@ -0,0 +1,300 @@
|
|||
// +build acceptance networking layer3ext
|
||||
|
||||
package extensions
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/external"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/ports"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
const (
|
||||
cidr1 = "10.0.0.1/24"
|
||||
cidr2 = "20.0.0.1/24"
|
||||
)
|
||||
|
||||
func TestAll(t *testing.T) {
|
||||
base.Setup(t)
|
||||
defer base.Teardown()
|
||||
|
||||
testRouter(t)
|
||||
testFloatingIP(t)
|
||||
}
|
||||
|
||||
func testRouter(t *testing.T) {
|
||||
// Setup: Create network
|
||||
networkID := createNetwork(t)
|
||||
|
||||
// Create router
|
||||
routerID := createRouter(t, networkID)
|
||||
|
||||
// Lists routers
|
||||
listRouters(t)
|
||||
|
||||
// Update router
|
||||
updateRouter(t, routerID)
|
||||
|
||||
// Get router
|
||||
getRouter(t, routerID)
|
||||
|
||||
// Create new subnet. Note: this subnet will be deleted when networkID is deleted
|
||||
subnetID := createSubnet(t, networkID, cidr2)
|
||||
|
||||
// Add interface
|
||||
addInterface(t, routerID, subnetID)
|
||||
|
||||
// Remove interface
|
||||
removeInterface(t, routerID, subnetID)
|
||||
|
||||
// Delete router
|
||||
deleteRouter(t, routerID)
|
||||
|
||||
// Cleanup
|
||||
deleteNetwork(t, networkID)
|
||||
}
|
||||
|
||||
func testFloatingIP(t *testing.T) {
|
||||
// Setup external network
|
||||
extNetworkID := createNetwork(t)
|
||||
|
||||
// Setup internal network, subnet and port
|
||||
intNetworkID, subnetID, portID := createInternalTopology(t)
|
||||
|
||||
// Now the important part: we need to allow the external network to talk to
|
||||
// the internal subnet. For this we need a router that has an interface to
|
||||
// the internal subnet.
|
||||
routerID := bridgeIntSubnetWithExtNetwork(t, extNetworkID, subnetID)
|
||||
|
||||
// Create floating IP
|
||||
ipID := createFloatingIP(t, extNetworkID, portID)
|
||||
|
||||
// Get floating IP
|
||||
getFloatingIP(t, ipID)
|
||||
|
||||
// Update floating IP
|
||||
updateFloatingIP(t, ipID, portID)
|
||||
|
||||
// Delete floating IP
|
||||
deleteFloatingIP(t, ipID)
|
||||
|
||||
// Remove the internal subnet interface
|
||||
removeInterface(t, routerID, subnetID)
|
||||
|
||||
// Delete router and external network
|
||||
deleteRouter(t, routerID)
|
||||
deleteNetwork(t, extNetworkID)
|
||||
|
||||
// Delete internal port and network
|
||||
deletePort(t, portID)
|
||||
deleteNetwork(t, intNetworkID)
|
||||
}
|
||||
|
||||
func createNetwork(t *testing.T) string {
|
||||
t.Logf("Creating a network")
|
||||
|
||||
asu := true
|
||||
opts := external.CreateOpts{
|
||||
Parent: networks.CreateOpts{Name: "sample_network", AdminStateUp: &asu},
|
||||
External: true,
|
||||
}
|
||||
n, err := networks.Create(base.Client, opts).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
if n.ID == "" {
|
||||
t.Fatalf("No ID returned when creating a network")
|
||||
}
|
||||
|
||||
createSubnet(t, n.ID, cidr1)
|
||||
|
||||
t.Logf("Network created: ID [%s]", n.ID)
|
||||
|
||||
return n.ID
|
||||
}
|
||||
|
||||
func deleteNetwork(t *testing.T, networkID string) {
|
||||
t.Logf("Deleting network %s", networkID)
|
||||
networks.Delete(base.Client, networkID)
|
||||
}
|
||||
|
||||
func deletePort(t *testing.T, portID string) {
|
||||
t.Logf("Deleting port %s", portID)
|
||||
ports.Delete(base.Client, portID)
|
||||
}
|
||||
|
||||
func createInternalTopology(t *testing.T) (string, string, string) {
|
||||
t.Logf("Creating an internal network (for port)")
|
||||
opts := networks.CreateOpts{Name: "internal_network"}
|
||||
n, err := networks.Create(base.Client, opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
// A subnet is also needed
|
||||
subnetID := createSubnet(t, n.ID, cidr2)
|
||||
|
||||
t.Logf("Creating an internal port on network %s", n.ID)
|
||||
p, err := ports.Create(base.Client, ports.CreateOpts{
|
||||
NetworkID: n.ID,
|
||||
Name: "fixed_internal_port",
|
||||
}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
return n.ID, subnetID, p.ID
|
||||
}
|
||||
|
||||
func bridgeIntSubnetWithExtNetwork(t *testing.T, networkID, subnetID string) string {
|
||||
// Create router with external gateway info
|
||||
routerID := createRouter(t, networkID)
|
||||
|
||||
// Add interface for internal subnet
|
||||
addInterface(t, routerID, subnetID)
|
||||
|
||||
return routerID
|
||||
}
|
||||
|
||||
func createSubnet(t *testing.T, networkID, cidr string) string {
|
||||
t.Logf("Creating a subnet for network %s", networkID)
|
||||
|
||||
iFalse := false
|
||||
s, err := subnets.Create(base.Client, subnets.CreateOpts{
|
||||
NetworkID: networkID,
|
||||
CIDR: cidr,
|
||||
IPVersion: subnets.IPv4,
|
||||
Name: "my_subnet",
|
||||
EnableDHCP: &iFalse,
|
||||
}).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Subnet created: ID [%s]", s.ID)
|
||||
|
||||
return s.ID
|
||||
}
|
||||
|
||||
func createRouter(t *testing.T, networkID string) string {
|
||||
t.Logf("Creating a router for network %s", networkID)
|
||||
|
||||
asu := false
|
||||
gwi := routers.GatewayInfo{NetworkID: networkID}
|
||||
r, err := routers.Create(base.Client, routers.CreateOpts{
|
||||
Name: "foo_router",
|
||||
AdminStateUp: &asu,
|
||||
GatewayInfo: &gwi,
|
||||
}).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
if r.ID == "" {
|
||||
t.Fatalf("No ID returned when creating a router")
|
||||
}
|
||||
|
||||
t.Logf("Router created: ID [%s]", r.ID)
|
||||
|
||||
return r.ID
|
||||
}
|
||||
|
||||
func listRouters(t *testing.T) {
|
||||
pager := routers.List(base.Client, routers.ListOpts{})
|
||||
|
||||
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
routerList, err := routers.ExtractRouters(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, r := range routerList {
|
||||
t.Logf("Listing router: ID [%s] Name [%s] Status [%s] GatewayInfo [%#v]",
|
||||
r.ID, r.Name, r.Status, r.GatewayInfo)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func updateRouter(t *testing.T, routerID string) {
|
||||
_, err := routers.Update(base.Client, routerID, routers.UpdateOpts{
|
||||
Name: "another_name",
|
||||
}).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func getRouter(t *testing.T, routerID string) {
|
||||
r, err := routers.Get(base.Client, routerID).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Getting router: ID [%s] Name [%s] Status [%s]", r.ID, r.Name, r.Status)
|
||||
}
|
||||
|
||||
func addInterface(t *testing.T, routerID, subnetID string) {
|
||||
ir, err := routers.AddInterface(base.Client, routerID, routers.InterfaceOpts{SubnetID: subnetID}).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Interface added to router %s: SubnetID [%s] PortID [%s]", routerID, ir.SubnetID, ir.PortID)
|
||||
}
|
||||
|
||||
func removeInterface(t *testing.T, routerID, subnetID string) {
|
||||
ir, err := routers.RemoveInterface(base.Client, routerID, routers.InterfaceOpts{SubnetID: subnetID}).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Interface %s removed from %s", ir.ID, routerID)
|
||||
}
|
||||
|
||||
func deleteRouter(t *testing.T, routerID string) {
|
||||
t.Logf("Deleting router %s", routerID)
|
||||
|
||||
res := routers.Delete(base.Client, routerID)
|
||||
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
|
||||
func createFloatingIP(t *testing.T, networkID, portID string) string {
|
||||
t.Logf("Creating floating IP on network [%s] with port [%s]", networkID, portID)
|
||||
|
||||
opts := floatingips.CreateOpts{
|
||||
FloatingNetworkID: networkID,
|
||||
PortID: portID,
|
||||
}
|
||||
|
||||
ip, err := floatingips.Create(base.Client, opts).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Floating IP created: ID [%s] Status [%s] Fixed (internal) IP: [%s] Floating (external) IP: [%s]",
|
||||
ip.ID, ip.Status, ip.FixedIP, ip.FloatingIP)
|
||||
|
||||
return ip.ID
|
||||
}
|
||||
|
||||
func getFloatingIP(t *testing.T, ipID string) {
|
||||
ip, err := floatingips.Get(base.Client, ipID).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Getting floating IP: ID [%s] Status [%s]", ip.ID, ip.Status)
|
||||
}
|
||||
|
||||
func updateFloatingIP(t *testing.T, ipID, portID string) {
|
||||
t.Logf("Disassociate all ports from IP %s", ipID)
|
||||
_, err := floatingips.Update(base.Client, ipID, floatingips.UpdateOpts{PortID: ""}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Re-associate the port %s", portID)
|
||||
_, err = floatingips.Update(base.Client, ipID, floatingips.UpdateOpts{PortID: portID}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func deleteFloatingIP(t *testing.T, ipID string) {
|
||||
t.Logf("Deleting IP %s", ipID)
|
||||
res := floatingips.Delete(base.Client, ipID)
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
78
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/extensions/lbaas/common.go
generated
vendored
Normal file
78
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/extensions/lbaas/common.go
generated
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
package lbaas
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func SetupTopology(t *testing.T) (string, string) {
|
||||
// create network
|
||||
n, err := networks.Create(base.Client, networks.CreateOpts{Name: "tmp_network"}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Created network %s", n.ID)
|
||||
|
||||
// create subnet
|
||||
s, err := subnets.Create(base.Client, subnets.CreateOpts{
|
||||
NetworkID: n.ID,
|
||||
CIDR: "192.168.199.0/24",
|
||||
IPVersion: subnets.IPv4,
|
||||
Name: "tmp_subnet",
|
||||
}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Created subnet %s", s.ID)
|
||||
|
||||
return n.ID, s.ID
|
||||
}
|
||||
|
||||
func DeleteTopology(t *testing.T, networkID string) {
|
||||
res := networks.Delete(base.Client, networkID)
|
||||
th.AssertNoErr(t, res.Err)
|
||||
t.Logf("Deleted network %s", networkID)
|
||||
}
|
||||
|
||||
func CreatePool(t *testing.T, subnetID string) string {
|
||||
p, err := pools.Create(base.Client, pools.CreateOpts{
|
||||
LBMethod: pools.LBMethodRoundRobin,
|
||||
Protocol: "HTTP",
|
||||
Name: "tmp_pool",
|
||||
SubnetID: subnetID,
|
||||
}).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Created pool %s", p.ID)
|
||||
|
||||
return p.ID
|
||||
}
|
||||
|
||||
func DeletePool(t *testing.T, poolID string) {
|
||||
res := pools.Delete(base.Client, poolID)
|
||||
th.AssertNoErr(t, res.Err)
|
||||
t.Logf("Deleted pool %s", poolID)
|
||||
}
|
||||
|
||||
func CreateMonitor(t *testing.T) string {
|
||||
m, err := monitors.Create(base.Client, monitors.CreateOpts{
|
||||
Delay: 10,
|
||||
Timeout: 10,
|
||||
MaxRetries: 3,
|
||||
Type: monitors.TypeHTTP,
|
||||
ExpectedCodes: "200",
|
||||
URLPath: "/login",
|
||||
HTTPMethod: "GET",
|
||||
}).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Created monitor ID [%s]", m.ID)
|
||||
|
||||
return m.ID
|
||||
}
|
95
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/extensions/lbaas/member_test.go
generated
vendored
Normal file
95
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/extensions/lbaas/member_test.go
generated
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
// +build acceptance networking lbaas lbaasmember
|
||||
|
||||
package lbaas
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestMembers(t *testing.T) {
|
||||
base.Setup(t)
|
||||
defer base.Teardown()
|
||||
|
||||
// setup
|
||||
networkID, subnetID := SetupTopology(t)
|
||||
poolID := CreatePool(t, subnetID)
|
||||
|
||||
// create member
|
||||
memberID := createMember(t, poolID)
|
||||
|
||||
// list members
|
||||
listMembers(t)
|
||||
|
||||
// update member
|
||||
updateMember(t, memberID)
|
||||
|
||||
// get member
|
||||
getMember(t, memberID)
|
||||
|
||||
// delete member
|
||||
deleteMember(t, memberID)
|
||||
|
||||
// teardown
|
||||
DeletePool(t, poolID)
|
||||
DeleteTopology(t, networkID)
|
||||
}
|
||||
|
||||
func createMember(t *testing.T, poolID string) string {
|
||||
m, err := members.Create(base.Client, members.CreateOpts{
|
||||
Address: "192.168.199.1",
|
||||
ProtocolPort: 8080,
|
||||
PoolID: poolID,
|
||||
}).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Created member: ID [%s] Status [%s] Weight [%d] Address [%s] Port [%d]",
|
||||
m.ID, m.Status, m.Weight, m.Address, m.ProtocolPort)
|
||||
|
||||
return m.ID
|
||||
}
|
||||
|
||||
func listMembers(t *testing.T) {
|
||||
err := members.List(base.Client, members.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
memberList, err := members.ExtractMembers(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract members: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, m := range memberList {
|
||||
t.Logf("Listing member: ID [%s] Status [%s]", m.ID, m.Status)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func updateMember(t *testing.T, memberID string) {
|
||||
m, err := members.Update(base.Client, memberID, members.UpdateOpts{AdminStateUp: true}).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Updated member ID [%s]", m.ID)
|
||||
}
|
||||
|
||||
func getMember(t *testing.T, memberID string) {
|
||||
m, err := members.Get(base.Client, memberID).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Getting member ID [%s]", m.ID)
|
||||
}
|
||||
|
||||
func deleteMember(t *testing.T, memberID string) {
|
||||
res := members.Delete(base.Client, memberID)
|
||||
th.AssertNoErr(t, res.Err)
|
||||
t.Logf("Deleted member %s", memberID)
|
||||
}
|
77
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/extensions/lbaas/monitor_test.go
generated
vendored
Normal file
77
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/extensions/lbaas/monitor_test.go
generated
vendored
Normal file
|
@ -0,0 +1,77 @@
|
|||
// +build acceptance networking lbaas lbaasmonitor
|
||||
|
||||
package lbaas
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestMonitors(t *testing.T) {
|
||||
base.Setup(t)
|
||||
defer base.Teardown()
|
||||
|
||||
// create monitor
|
||||
monitorID := CreateMonitor(t)
|
||||
|
||||
// list monitors
|
||||
listMonitors(t)
|
||||
|
||||
// update monitor
|
||||
updateMonitor(t, monitorID)
|
||||
|
||||
// get monitor
|
||||
getMonitor(t, monitorID)
|
||||
|
||||
// delete monitor
|
||||
deleteMonitor(t, monitorID)
|
||||
}
|
||||
|
||||
func listMonitors(t *testing.T) {
|
||||
err := monitors.List(base.Client, monitors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
monitorList, err := monitors.ExtractMonitors(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract monitors: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, m := range monitorList {
|
||||
t.Logf("Listing monitor: ID [%s] Type [%s] Delay [%ds] Timeout [%d] Retries [%d] Status [%s]",
|
||||
m.ID, m.Type, m.Delay, m.Timeout, m.MaxRetries, m.Status)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func updateMonitor(t *testing.T, monitorID string) {
|
||||
opts := monitors.UpdateOpts{Delay: 10, Timeout: 10, MaxRetries: 3}
|
||||
m, err := monitors.Update(base.Client, monitorID, opts).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Updated monitor ID [%s]", m.ID)
|
||||
}
|
||||
|
||||
func getMonitor(t *testing.T, monitorID string) {
|
||||
m, err := monitors.Get(base.Client, monitorID).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Getting monitor ID [%s]: URL path [%s] HTTP Method [%s] Accepted codes [%s]",
|
||||
m.ID, m.URLPath, m.HTTPMethod, m.ExpectedCodes)
|
||||
}
|
||||
|
||||
func deleteMonitor(t *testing.T, monitorID string) {
|
||||
res := monitors.Delete(base.Client, monitorID)
|
||||
|
||||
th.AssertNoErr(t, res.Err)
|
||||
|
||||
t.Logf("Deleted monitor %s", monitorID)
|
||||
}
|
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/extensions/lbaas/pkg.go
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/extensions/lbaas/pkg.go
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
package lbaas
|
98
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/extensions/lbaas/pool_test.go
generated
vendored
Normal file
98
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/extensions/lbaas/pool_test.go
generated
vendored
Normal file
|
@ -0,0 +1,98 @@
|
|||
// +build acceptance networking lbaas lbaaspool
|
||||
|
||||
package lbaas
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestPools(t *testing.T) {
|
||||
base.Setup(t)
|
||||
defer base.Teardown()
|
||||
|
||||
// setup
|
||||
networkID, subnetID := SetupTopology(t)
|
||||
|
||||
// create pool
|
||||
poolID := CreatePool(t, subnetID)
|
||||
|
||||
// list pools
|
||||
listPools(t)
|
||||
|
||||
// update pool
|
||||
updatePool(t, poolID)
|
||||
|
||||
// get pool
|
||||
getPool(t, poolID)
|
||||
|
||||
// create monitor
|
||||
monitorID := CreateMonitor(t)
|
||||
|
||||
// associate health monitor
|
||||
associateMonitor(t, poolID, monitorID)
|
||||
|
||||
// disassociate health monitor
|
||||
disassociateMonitor(t, poolID, monitorID)
|
||||
|
||||
// delete pool
|
||||
DeletePool(t, poolID)
|
||||
|
||||
// teardown
|
||||
DeleteTopology(t, networkID)
|
||||
}
|
||||
|
||||
func listPools(t *testing.T) {
|
||||
err := pools.List(base.Client, pools.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
poolList, err := pools.ExtractPools(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract pools: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, p := range poolList {
|
||||
t.Logf("Listing pool: ID [%s] Name [%s] Status [%s] LB algorithm [%s]", p.ID, p.Name, p.Status, p.LBMethod)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func updatePool(t *testing.T, poolID string) {
|
||||
opts := pools.UpdateOpts{Name: "SuperPool", LBMethod: pools.LBMethodLeastConnections}
|
||||
p, err := pools.Update(base.Client, poolID, opts).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Updated pool ID [%s]", p.ID)
|
||||
}
|
||||
|
||||
func getPool(t *testing.T, poolID string) {
|
||||
p, err := pools.Get(base.Client, poolID).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Getting pool ID [%s]", p.ID)
|
||||
}
|
||||
|
||||
func associateMonitor(t *testing.T, poolID, monitorID string) {
|
||||
res := pools.AssociateMonitor(base.Client, poolID, monitorID)
|
||||
|
||||
th.AssertNoErr(t, res.Err)
|
||||
|
||||
t.Logf("Associated pool %s with monitor %s", poolID, monitorID)
|
||||
}
|
||||
|
||||
func disassociateMonitor(t *testing.T, poolID, monitorID string) {
|
||||
res := pools.DisassociateMonitor(base.Client, poolID, monitorID)
|
||||
|
||||
th.AssertNoErr(t, res.Err)
|
||||
|
||||
t.Logf("Disassociated pool %s with monitor %s", poolID, monitorID)
|
||||
}
|
101
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/extensions/lbaas/vip_test.go
generated
vendored
Normal file
101
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/extensions/lbaas/vip_test.go
generated
vendored
Normal file
|
@ -0,0 +1,101 @@
|
|||
// +build acceptance networking lbaas lbaasvip
|
||||
|
||||
package lbaas
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestVIPs(t *testing.T) {
|
||||
base.Setup(t)
|
||||
defer base.Teardown()
|
||||
|
||||
// setup
|
||||
networkID, subnetID := SetupTopology(t)
|
||||
poolID := CreatePool(t, subnetID)
|
||||
|
||||
// create VIP
|
||||
VIPID := createVIP(t, subnetID, poolID)
|
||||
|
||||
// list VIPs
|
||||
listVIPs(t)
|
||||
|
||||
// update VIP
|
||||
updateVIP(t, VIPID)
|
||||
|
||||
// get VIP
|
||||
getVIP(t, VIPID)
|
||||
|
||||
// delete VIP
|
||||
deleteVIP(t, VIPID)
|
||||
|
||||
// teardown
|
||||
DeletePool(t, poolID)
|
||||
DeleteTopology(t, networkID)
|
||||
}
|
||||
|
||||
func createVIP(t *testing.T, subnetID, poolID string) string {
|
||||
p, err := vips.Create(base.Client, vips.CreateOpts{
|
||||
Protocol: "HTTP",
|
||||
Name: "New_VIP",
|
||||
AdminStateUp: vips.Up,
|
||||
SubnetID: subnetID,
|
||||
PoolID: poolID,
|
||||
ProtocolPort: 80,
|
||||
}).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Created pool %s", p.ID)
|
||||
|
||||
return p.ID
|
||||
}
|
||||
|
||||
func listVIPs(t *testing.T) {
|
||||
err := vips.List(base.Client, vips.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
vipList, err := vips.ExtractVIPs(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract VIPs: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, vip := range vipList {
|
||||
t.Logf("Listing VIP: ID [%s] Name [%s] Address [%s] Port [%s] Connection Limit [%d]",
|
||||
vip.ID, vip.Name, vip.Address, vip.ProtocolPort, vip.ConnLimit)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func updateVIP(t *testing.T, VIPID string) {
|
||||
i1000 := 1000
|
||||
_, err := vips.Update(base.Client, VIPID, vips.UpdateOpts{ConnLimit: &i1000}).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Updated VIP ID [%s]", VIPID)
|
||||
}
|
||||
|
||||
func getVIP(t *testing.T, VIPID string) {
|
||||
vip, err := vips.Get(base.Client, VIPID).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Getting VIP ID [%s]: Status [%s]", vip.ID, vip.Status)
|
||||
}
|
||||
|
||||
func deleteVIP(t *testing.T, VIPID string) {
|
||||
res := vips.Delete(base.Client, VIPID)
|
||||
|
||||
th.AssertNoErr(t, res.Err)
|
||||
|
||||
t.Logf("Deleted VIP %s", VIPID)
|
||||
}
|
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/extensions/pkg.go
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/extensions/pkg.go
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
package extensions
|
68
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/extensions/provider_test.go
generated
vendored
Normal file
68
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/extensions/provider_test.go
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
// +build acceptance networking
|
||||
|
||||
package extensions
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestNetworkCRUDOperations(t *testing.T) {
|
||||
base.Setup(t)
|
||||
defer base.Teardown()
|
||||
|
||||
// Create a network
|
||||
n, err := networks.Create(base.Client, networks.CreateOpts{Name: "sample_network", AdminStateUp: networks.Up}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
th.AssertEquals(t, n.Name, "sample_network")
|
||||
th.AssertEquals(t, n.AdminStateUp, true)
|
||||
networkID := n.ID
|
||||
|
||||
// List networks
|
||||
pager := networks.List(base.Client, networks.ListOpts{Limit: 2})
|
||||
err = pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
t.Logf("--- Page ---")
|
||||
|
||||
networkList, err := networks.ExtractNetworks(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, n := range networkList {
|
||||
t.Logf("Network: ID [%s] Name [%s] Status [%s] Is shared? [%s]",
|
||||
n.ID, n.Name, n.Status, strconv.FormatBool(n.Shared))
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.CheckNoErr(t, err)
|
||||
|
||||
// Get a network
|
||||
if networkID == "" {
|
||||
t.Fatalf("In order to retrieve a network, the NetworkID must be set")
|
||||
}
|
||||
n, err = networks.Get(base.Client, networkID).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
th.AssertEquals(t, n.Status, "ACTIVE")
|
||||
th.AssertDeepEquals(t, n.Subnets, []string{})
|
||||
th.AssertEquals(t, n.Name, "sample_network")
|
||||
th.AssertEquals(t, n.AdminStateUp, true)
|
||||
th.AssertEquals(t, n.Shared, false)
|
||||
th.AssertEquals(t, n.ID, networkID)
|
||||
|
||||
// Update network
|
||||
n, err = networks.Update(base.Client, networkID, networks.UpdateOpts{Name: "new_network_name"}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
th.AssertEquals(t, n.Name, "new_network_name")
|
||||
|
||||
// Delete network
|
||||
res := networks.Delete(base.Client, networkID)
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
|
||||
func TestCreateMultipleNetworks(t *testing.T) {
|
||||
//networks.CreateMany()
|
||||
}
|
171
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/extensions/security_test.go
generated
vendored
Normal file
171
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/extensions/security_test.go
generated
vendored
Normal file
|
@ -0,0 +1,171 @@
|
|||
// +build acceptance networking security
|
||||
|
||||
package extensions
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/ports"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestSecurityGroups(t *testing.T) {
|
||||
base.Setup(t)
|
||||
defer base.Teardown()
|
||||
|
||||
// create security group
|
||||
groupID := createSecGroup(t)
|
||||
|
||||
// delete security group
|
||||
defer deleteSecGroup(t, groupID)
|
||||
|
||||
// list security group
|
||||
listSecGroups(t)
|
||||
|
||||
// get security group
|
||||
getSecGroup(t, groupID)
|
||||
|
||||
// create port with security group
|
||||
networkID, portID := createPort(t, groupID)
|
||||
|
||||
// teardown
|
||||
defer deleteNetwork(t, networkID)
|
||||
|
||||
// delete port
|
||||
defer deletePort(t, portID)
|
||||
}
|
||||
|
||||
func TestSecurityGroupRules(t *testing.T) {
|
||||
base.Setup(t)
|
||||
defer base.Teardown()
|
||||
|
||||
// create security group
|
||||
groupID := createSecGroup(t)
|
||||
|
||||
defer deleteSecGroup(t, groupID)
|
||||
|
||||
// create security group rule
|
||||
ruleID := createSecRule(t, groupID)
|
||||
|
||||
// delete security group rule
|
||||
defer deleteSecRule(t, ruleID)
|
||||
|
||||
// list security group rule
|
||||
listSecRules(t)
|
||||
|
||||
// get security group rule
|
||||
getSecRule(t, ruleID)
|
||||
}
|
||||
|
||||
func createSecGroup(t *testing.T) string {
|
||||
sg, err := groups.Create(base.Client, groups.CreateOpts{
|
||||
Name: "new-webservers",
|
||||
Description: "security group for webservers",
|
||||
}).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Created security group %s", sg.ID)
|
||||
|
||||
return sg.ID
|
||||
}
|
||||
|
||||
func listSecGroups(t *testing.T) {
|
||||
err := groups.List(base.Client, groups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
list, err := groups.ExtractGroups(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract secgroups: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, sg := range list {
|
||||
t.Logf("Listing security group: ID [%s] Name [%s]", sg.ID, sg.Name)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func getSecGroup(t *testing.T, id string) {
|
||||
sg, err := groups.Get(base.Client, id).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Getting security group: ID [%s] Name [%s] Description [%s]", sg.ID, sg.Name, sg.Description)
|
||||
}
|
||||
|
||||
func createPort(t *testing.T, groupID string) (string, string) {
|
||||
n, err := networks.Create(base.Client, networks.CreateOpts{Name: "tmp_network"}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Created network %s", n.ID)
|
||||
|
||||
opts := ports.CreateOpts{
|
||||
NetworkID: n.ID,
|
||||
Name: "my_port",
|
||||
SecurityGroups: []string{groupID},
|
||||
}
|
||||
p, err := ports.Create(base.Client, opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Created port %s with security group %s", p.ID, groupID)
|
||||
|
||||
return n.ID, p.ID
|
||||
}
|
||||
|
||||
func deleteSecGroup(t *testing.T, groupID string) {
|
||||
res := groups.Delete(base.Client, groupID)
|
||||
th.AssertNoErr(t, res.Err)
|
||||
t.Logf("Deleted security group %s", groupID)
|
||||
}
|
||||
|
||||
func createSecRule(t *testing.T, groupID string) string {
|
||||
r, err := rules.Create(base.Client, rules.CreateOpts{
|
||||
Direction: "ingress",
|
||||
PortRangeMin: 80,
|
||||
EtherType: "IPv4",
|
||||
PortRangeMax: 80,
|
||||
Protocol: "tcp",
|
||||
SecGroupID: groupID,
|
||||
}).Extract()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Created security group rule %s", r.ID)
|
||||
|
||||
return r.ID
|
||||
}
|
||||
|
||||
func listSecRules(t *testing.T) {
|
||||
err := rules.List(base.Client, rules.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
list, err := rules.ExtractRules(page)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to extract sec rules: %v", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, r := range list {
|
||||
t.Logf("Listing security rule: ID [%s]", r.ID)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func getSecRule(t *testing.T, id string) {
|
||||
r, err := rules.Get(base.Client, id).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Getting security rule: ID [%s] Direction [%s] EtherType [%s] Protocol [%s]",
|
||||
r.ID, r.Direction, r.EtherType, r.Protocol)
|
||||
}
|
||||
|
||||
func deleteSecRule(t *testing.T, id string) {
|
||||
res := rules.Delete(base.Client, id)
|
||||
th.AssertNoErr(t, res.Err)
|
||||
t.Logf("Deleted security rule %s", id)
|
||||
}
|
68
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/network_test.go
generated
vendored
Normal file
68
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/network_test.go
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
// +build acceptance networking
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestNetworkCRUDOperations(t *testing.T) {
|
||||
Setup(t)
|
||||
defer Teardown()
|
||||
|
||||
// Create a network
|
||||
n, err := networks.Create(Client, networks.CreateOpts{Name: "sample_network", AdminStateUp: networks.Up}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
defer networks.Delete(Client, n.ID)
|
||||
th.AssertEquals(t, n.Name, "sample_network")
|
||||
th.AssertEquals(t, n.AdminStateUp, true)
|
||||
networkID := n.ID
|
||||
|
||||
// List networks
|
||||
pager := networks.List(Client, networks.ListOpts{Limit: 2})
|
||||
err = pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
t.Logf("--- Page ---")
|
||||
|
||||
networkList, err := networks.ExtractNetworks(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, n := range networkList {
|
||||
t.Logf("Network: ID [%s] Name [%s] Status [%s] Is shared? [%s]",
|
||||
n.ID, n.Name, n.Status, strconv.FormatBool(n.Shared))
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.CheckNoErr(t, err)
|
||||
|
||||
// Get a network
|
||||
if networkID == "" {
|
||||
t.Fatalf("In order to retrieve a network, the NetworkID must be set")
|
||||
}
|
||||
n, err = networks.Get(Client, networkID).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
th.AssertEquals(t, n.Status, "ACTIVE")
|
||||
th.AssertDeepEquals(t, n.Subnets, []string{})
|
||||
th.AssertEquals(t, n.Name, "sample_network")
|
||||
th.AssertEquals(t, n.AdminStateUp, true)
|
||||
th.AssertEquals(t, n.Shared, false)
|
||||
th.AssertEquals(t, n.ID, networkID)
|
||||
|
||||
// Update network
|
||||
n, err = networks.Update(Client, networkID, networks.UpdateOpts{Name: "new_network_name"}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
th.AssertEquals(t, n.Name, "new_network_name")
|
||||
|
||||
// Delete network
|
||||
res := networks.Delete(Client, networkID)
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
|
||||
func TestCreateMultipleNetworks(t *testing.T) {
|
||||
//networks.CreateMany()
|
||||
}
|
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/pkg.go
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/pkg.go
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
package v2
|
117
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/port_test.go
generated
vendored
Normal file
117
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/port_test.go
generated
vendored
Normal file
|
@ -0,0 +1,117 @@
|
|||
// +build acceptance networking
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/ports"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestPortCRUD(t *testing.T) {
|
||||
Setup(t)
|
||||
defer Teardown()
|
||||
|
||||
// Setup network
|
||||
t.Log("Setting up network")
|
||||
networkID, err := createNetwork()
|
||||
th.AssertNoErr(t, err)
|
||||
defer networks.Delete(Client, networkID)
|
||||
|
||||
// Setup subnet
|
||||
t.Logf("Setting up subnet on network %s", networkID)
|
||||
subnetID, err := createSubnet(networkID)
|
||||
th.AssertNoErr(t, err)
|
||||
defer subnets.Delete(Client, subnetID)
|
||||
|
||||
// Create port
|
||||
t.Logf("Create port based on subnet %s", subnetID)
|
||||
portID := createPort(t, networkID, subnetID)
|
||||
|
||||
// List ports
|
||||
t.Logf("Listing all ports")
|
||||
listPorts(t)
|
||||
|
||||
// Get port
|
||||
if portID == "" {
|
||||
t.Fatalf("In order to retrieve a port, the portID must be set")
|
||||
}
|
||||
p, err := ports.Get(Client, portID).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
th.AssertEquals(t, p.ID, portID)
|
||||
|
||||
// Update port
|
||||
p, err = ports.Update(Client, portID, ports.UpdateOpts{Name: "new_port_name"}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
th.AssertEquals(t, p.Name, "new_port_name")
|
||||
|
||||
// Delete port
|
||||
res := ports.Delete(Client, portID)
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
|
||||
func createPort(t *testing.T, networkID, subnetID string) string {
|
||||
enable := false
|
||||
opts := ports.CreateOpts{
|
||||
NetworkID: networkID,
|
||||
Name: "my_port",
|
||||
AdminStateUp: &enable,
|
||||
FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}},
|
||||
}
|
||||
p, err := ports.Create(Client, opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
th.AssertEquals(t, p.NetworkID, networkID)
|
||||
th.AssertEquals(t, p.Name, "my_port")
|
||||
th.AssertEquals(t, p.AdminStateUp, false)
|
||||
|
||||
return p.ID
|
||||
}
|
||||
|
||||
func listPorts(t *testing.T) {
|
||||
count := 0
|
||||
pager := ports.List(Client, ports.ListOpts{})
|
||||
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
t.Logf("--- Page ---")
|
||||
|
||||
portList, err := ports.ExtractPorts(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, p := range portList {
|
||||
t.Logf("Port: ID [%s] Name [%s] Status [%s] MAC addr [%s] Fixed IPs [%#v] Security groups [%#v]",
|
||||
p.ID, p.Name, p.Status, p.MACAddress, p.FixedIPs, p.SecurityGroups)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
th.CheckNoErr(t, err)
|
||||
|
||||
if count == 0 {
|
||||
t.Logf("No pages were iterated over when listing ports")
|
||||
}
|
||||
}
|
||||
|
||||
func createNetwork() (string, error) {
|
||||
res, err := networks.Create(Client, networks.CreateOpts{Name: "tmp_network", AdminStateUp: networks.Up}).Extract()
|
||||
return res.ID, err
|
||||
}
|
||||
|
||||
func createSubnet(networkID string) (string, error) {
|
||||
s, err := subnets.Create(Client, subnets.CreateOpts{
|
||||
NetworkID: networkID,
|
||||
CIDR: "192.168.199.0/24",
|
||||
IPVersion: subnets.IPv4,
|
||||
Name: "my_subnet",
|
||||
EnableDHCP: subnets.Down,
|
||||
}).Extract()
|
||||
return s.ID, err
|
||||
}
|
||||
|
||||
func TestPortBatchCreate(t *testing.T) {
|
||||
// todo
|
||||
}
|
86
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/subnet_test.go
generated
vendored
Normal file
86
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/networking/v2/subnet_test.go
generated
vendored
Normal file
|
@ -0,0 +1,86 @@
|
|||
// +build acceptance networking
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
|
||||
"github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
Setup(t)
|
||||
defer Teardown()
|
||||
|
||||
pager := subnets.List(Client, subnets.ListOpts{Limit: 2})
|
||||
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
t.Logf("--- Page ---")
|
||||
|
||||
subnetList, err := subnets.ExtractSubnets(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, s := range subnetList {
|
||||
t.Logf("Subnet: ID [%s] Name [%s] IP Version [%d] CIDR [%s] GatewayIP [%s]",
|
||||
s.ID, s.Name, s.IPVersion, s.CIDR, s.GatewayIP)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.CheckNoErr(t, err)
|
||||
}
|
||||
|
||||
func TestCRUD(t *testing.T) {
|
||||
Setup(t)
|
||||
defer Teardown()
|
||||
|
||||
// Setup network
|
||||
t.Log("Setting up network")
|
||||
n, err := networks.Create(Client, networks.CreateOpts{Name: "tmp_network", AdminStateUp: networks.Up}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
networkID := n.ID
|
||||
defer networks.Delete(Client, networkID)
|
||||
|
||||
// Create subnet
|
||||
t.Log("Create subnet")
|
||||
enable := false
|
||||
opts := subnets.CreateOpts{
|
||||
NetworkID: networkID,
|
||||
CIDR: "192.168.199.0/24",
|
||||
IPVersion: subnets.IPv4,
|
||||
Name: "my_subnet",
|
||||
EnableDHCP: &enable,
|
||||
}
|
||||
s, err := subnets.Create(Client, opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
th.AssertEquals(t, s.NetworkID, networkID)
|
||||
th.AssertEquals(t, s.CIDR, "192.168.199.0/24")
|
||||
th.AssertEquals(t, s.IPVersion, 4)
|
||||
th.AssertEquals(t, s.Name, "my_subnet")
|
||||
th.AssertEquals(t, s.EnableDHCP, false)
|
||||
subnetID := s.ID
|
||||
|
||||
// Get subnet
|
||||
t.Log("Getting subnet")
|
||||
s, err = subnets.Get(Client, subnetID).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
th.AssertEquals(t, s.ID, subnetID)
|
||||
|
||||
// Update subnet
|
||||
t.Log("Update subnet")
|
||||
s, err = subnets.Update(Client, subnetID, subnets.UpdateOpts{Name: "new_subnet_name"}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
th.AssertEquals(t, s.Name, "new_subnet_name")
|
||||
|
||||
// Delete subnet
|
||||
t.Log("Delete subnet")
|
||||
res := subnets.Delete(Client, subnetID)
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
|
||||
func TestBatchCreate(t *testing.T) {
|
||||
// todo
|
||||
}
|
44
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/objectstorage/v1/accounts_test.go
generated
vendored
Normal file
44
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/objectstorage/v1/accounts_test.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
// +build acceptance
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/openstack/objectstorage/v1/accounts"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestAccounts(t *testing.T) {
|
||||
// Create a provider client for making the HTTP requests.
|
||||
// See common.go in this directory for more information.
|
||||
client := newClient(t)
|
||||
|
||||
// Update an account's metadata.
|
||||
updateres := accounts.Update(client, accounts.UpdateOpts{Metadata: metadata})
|
||||
th.AssertNoErr(t, updateres.Err)
|
||||
|
||||
// Defer the deletion of the metadata set above.
|
||||
defer func() {
|
||||
tempMap := make(map[string]string)
|
||||
for k := range metadata {
|
||||
tempMap[k] = ""
|
||||
}
|
||||
updateres = accounts.Update(client, accounts.UpdateOpts{Metadata: tempMap})
|
||||
th.AssertNoErr(t, updateres.Err)
|
||||
}()
|
||||
|
||||
// Retrieve account metadata.
|
||||
getres := accounts.Get(client, nil)
|
||||
th.AssertNoErr(t, getres.Err)
|
||||
// Extract the custom metadata from the 'Get' response.
|
||||
am, err := getres.ExtractMetadata()
|
||||
th.AssertNoErr(t, err)
|
||||
for k := range metadata {
|
||||
if am[k] != metadata[strings.Title(k)] {
|
||||
t.Errorf("Expected custom metadata with key: %s", k)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
28
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/objectstorage/v1/common.go
generated
vendored
Normal file
28
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/objectstorage/v1/common.go
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
// +build acceptance
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/openstack"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
var metadata = map[string]string{"gopher": "cloud"}
|
||||
|
||||
func newClient(t *testing.T) *gophercloud.ServiceClient {
|
||||
ao, err := openstack.AuthOptionsFromEnv()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
client, err := openstack.AuthenticatedClient(ao)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
c, err := openstack.NewObjectStorageV1(client, gophercloud.EndpointOpts{
|
||||
Region: os.Getenv("OS_REGION_NAME"),
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
return c
|
||||
}
|
89
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/objectstorage/v1/containers_test.go
generated
vendored
Normal file
89
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/objectstorage/v1/containers_test.go
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
|||
// +build acceptance
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/acceptance/tools"
|
||||
"github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
// numContainers is the number of containers to create for testing.
|
||||
var numContainers = 2
|
||||
|
||||
func TestContainers(t *testing.T) {
|
||||
// Create a new client to execute the HTTP requests. See common.go for newClient body.
|
||||
client := newClient(t)
|
||||
|
||||
// Create a slice of random container names.
|
||||
cNames := make([]string, numContainers)
|
||||
for i := 0; i < numContainers; i++ {
|
||||
cNames[i] = tools.RandomString("gophercloud-test-container-", 8)
|
||||
}
|
||||
|
||||
// Create numContainers containers.
|
||||
for i := 0; i < len(cNames); i++ {
|
||||
res := containers.Create(client, cNames[i], nil)
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
// Delete the numContainers containers after function completion.
|
||||
defer func() {
|
||||
for i := 0; i < len(cNames); i++ {
|
||||
res := containers.Delete(client, cNames[i])
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
}()
|
||||
|
||||
// List the numContainer names that were just created. To just list those,
|
||||
// the 'prefix' parameter is used.
|
||||
err := containers.List(client, &containers.ListOpts{Full: true, Prefix: "gophercloud-test-container-"}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
containerList, err := containers.ExtractInfo(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, n := range containerList {
|
||||
t.Logf("Container: Name [%s] Count [%d] Bytes [%d]",
|
||||
n.Name, n.Count, n.Bytes)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
// List the info for the numContainer containers that were created.
|
||||
err = containers.List(client, &containers.ListOpts{Full: false, Prefix: "gophercloud-test-container-"}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
containerList, err := containers.ExtractNames(page)
|
||||
th.AssertNoErr(t, err)
|
||||
for _, n := range containerList {
|
||||
t.Logf("Container: Name [%s]", n)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
// Update one of the numContainer container metadata.
|
||||
updateres := containers.Update(client, cNames[0], &containers.UpdateOpts{Metadata: metadata})
|
||||
th.AssertNoErr(t, updateres.Err)
|
||||
// After the tests are done, delete the metadata that was set.
|
||||
defer func() {
|
||||
tempMap := make(map[string]string)
|
||||
for k := range metadata {
|
||||
tempMap[k] = ""
|
||||
}
|
||||
res := containers.Update(client, cNames[0], &containers.UpdateOpts{Metadata: tempMap})
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}()
|
||||
|
||||
// Retrieve a container's metadata.
|
||||
cm, err := containers.Get(client, cNames[0]).ExtractMetadata()
|
||||
th.AssertNoErr(t, err)
|
||||
for k := range metadata {
|
||||
if cm[k] != metadata[strings.Title(k)] {
|
||||
t.Errorf("Expected custom metadata with key: %s", k)
|
||||
}
|
||||
}
|
||||
}
|
119
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/objectstorage/v1/objects_test.go
generated
vendored
Normal file
119
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/objectstorage/v1/objects_test.go
generated
vendored
Normal file
|
@ -0,0 +1,119 @@
|
|||
// +build acceptance
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/acceptance/tools"
|
||||
"github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers"
|
||||
"github.com/rackspace/gophercloud/openstack/objectstorage/v1/objects"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
// numObjects is the number of objects to create for testing.
|
||||
var numObjects = 2
|
||||
|
||||
func TestObjects(t *testing.T) {
|
||||
// Create a provider client for executing the HTTP request.
|
||||
// See common.go for more information.
|
||||
client := newClient(t)
|
||||
|
||||
// Make a slice of length numObjects to hold the random object names.
|
||||
oNames := make([]string, numObjects)
|
||||
for i := 0; i < len(oNames); i++ {
|
||||
oNames[i] = tools.RandomString("test-object-", 8)
|
||||
}
|
||||
|
||||
// Create a container to hold the test objects.
|
||||
cName := tools.RandomString("test-container-", 8)
|
||||
header, err := containers.Create(client, cName, nil).ExtractHeader()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Create object headers: %+v\n", header)
|
||||
|
||||
// Defer deletion of the container until after testing.
|
||||
defer func() {
|
||||
res := containers.Delete(client, cName)
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}()
|
||||
|
||||
// Create a slice of buffers to hold the test object content.
|
||||
oContents := make([]*bytes.Buffer, numObjects)
|
||||
for i := 0; i < numObjects; i++ {
|
||||
oContents[i] = bytes.NewBuffer([]byte(tools.RandomString("", 10)))
|
||||
res := objects.Create(client, cName, oNames[i], oContents[i], nil)
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
// Delete the objects after testing.
|
||||
defer func() {
|
||||
for i := 0; i < numObjects; i++ {
|
||||
res := objects.Delete(client, cName, oNames[i], nil)
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}
|
||||
}()
|
||||
|
||||
ons := make([]string, 0, len(oNames))
|
||||
err = objects.List(client, cName, &objects.ListOpts{Full: false, Prefix: "test-object-"}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
names, err := objects.ExtractNames(page)
|
||||
th.AssertNoErr(t, err)
|
||||
ons = append(ons, names...)
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
th.AssertEquals(t, len(ons), len(oNames))
|
||||
|
||||
ois := make([]objects.Object, 0, len(oNames))
|
||||
err = objects.List(client, cName, &objects.ListOpts{Full: true, Prefix: "test-object-"}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
info, err := objects.ExtractInfo(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
ois = append(ois, info...)
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
th.AssertEquals(t, len(ois), len(oNames))
|
||||
|
||||
// Copy the contents of one object to another.
|
||||
copyres := objects.Copy(client, cName, oNames[0], &objects.CopyOpts{Destination: cName + "/" + oNames[1]})
|
||||
th.AssertNoErr(t, copyres.Err)
|
||||
|
||||
// Download one of the objects that was created above.
|
||||
o1Content, err := objects.Download(client, cName, oNames[0], nil).ExtractContent()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
// Download the another object that was create above.
|
||||
o2Content, err := objects.Download(client, cName, oNames[1], nil).ExtractContent()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
// Compare the two object's contents to test that the copy worked.
|
||||
th.AssertEquals(t, string(o2Content), string(o1Content))
|
||||
|
||||
// Update an object's metadata.
|
||||
updateres := objects.Update(client, cName, oNames[0], &objects.UpdateOpts{Metadata: metadata})
|
||||
th.AssertNoErr(t, updateres.Err)
|
||||
|
||||
// Delete the object's metadata after testing.
|
||||
defer func() {
|
||||
tempMap := make(map[string]string)
|
||||
for k := range metadata {
|
||||
tempMap[k] = ""
|
||||
}
|
||||
res := objects.Update(client, cName, oNames[0], &objects.UpdateOpts{Metadata: tempMap})
|
||||
th.AssertNoErr(t, res.Err)
|
||||
}()
|
||||
|
||||
// Retrieve an object's metadata.
|
||||
om, err := objects.Get(client, cName, oNames[0], nil).ExtractMetadata()
|
||||
th.AssertNoErr(t, err)
|
||||
for k := range metadata {
|
||||
if om[k] != metadata[strings.Title(k)] {
|
||||
t.Errorf("Expected custom metadata with key: %s", k)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
4
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/pkg.go
generated
vendored
Normal file
4
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/pkg.go
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
// +build acceptance
|
||||
|
||||
package openstack
|
||||
|
38
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/blockstorage/v1/common.go
generated
vendored
Normal file
38
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/blockstorage/v1/common.go
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
// +build acceptance
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/acceptance/tools"
|
||||
"github.com/rackspace/gophercloud/rackspace"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func newClient() (*gophercloud.ServiceClient, error) {
|
||||
opts, err := rackspace.AuthOptionsFromEnv()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts = tools.OnlyRS(opts)
|
||||
region := os.Getenv("RS_REGION")
|
||||
|
||||
provider, err := rackspace.AuthenticatedClient(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rackspace.NewBlockStorageV1(provider, gophercloud.EndpointOpts{
|
||||
Region: region,
|
||||
})
|
||||
}
|
||||
|
||||
func setup(t *testing.T) *gophercloud.ServiceClient {
|
||||
client, err := newClient()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
return client
|
||||
}
|
82
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/blockstorage/v1/snapshot_test.go
generated
vendored
Normal file
82
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/blockstorage/v1/snapshot_test.go
generated
vendored
Normal file
|
@ -0,0 +1,82 @@
|
|||
// +build acceptance blockstorage snapshots
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
"github.com/rackspace/gophercloud/rackspace/blockstorage/v1/snapshots"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestSnapshots(t *testing.T) {
|
||||
client := setup(t)
|
||||
volID := testVolumeCreate(t, client)
|
||||
|
||||
t.Log("Creating snapshots")
|
||||
s := testSnapshotCreate(t, client, volID)
|
||||
id := s.ID
|
||||
|
||||
t.Log("Listing snapshots")
|
||||
testSnapshotList(t, client)
|
||||
|
||||
t.Logf("Getting snapshot %s", id)
|
||||
testSnapshotGet(t, client, id)
|
||||
|
||||
t.Logf("Updating snapshot %s", id)
|
||||
testSnapshotUpdate(t, client, id)
|
||||
|
||||
t.Logf("Deleting snapshot %s", id)
|
||||
testSnapshotDelete(t, client, id)
|
||||
s.WaitUntilDeleted(client, -1)
|
||||
|
||||
t.Logf("Deleting volume %s", volID)
|
||||
testVolumeDelete(t, client, volID)
|
||||
}
|
||||
|
||||
func testSnapshotCreate(t *testing.T, client *gophercloud.ServiceClient, volID string) *snapshots.Snapshot {
|
||||
opts := snapshots.CreateOpts{VolumeID: volID, Name: "snapshot-001"}
|
||||
s, err := snapshots.Create(client, opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Created snapshot %s", s.ID)
|
||||
|
||||
t.Logf("Waiting for new snapshot to become available...")
|
||||
start := time.Now().Second()
|
||||
s.WaitUntilComplete(client, -1)
|
||||
t.Logf("Snapshot completed after %ds", time.Now().Second()-start)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func testSnapshotList(t *testing.T, client *gophercloud.ServiceClient) {
|
||||
snapshots.List(client).EachPage(func(page pagination.Page) (bool, error) {
|
||||
sList, err := snapshots.ExtractSnapshots(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, s := range sList {
|
||||
t.Logf("Snapshot: ID [%s] Name [%s] Volume ID [%s] Progress [%s] Created [%s]",
|
||||
s.ID, s.Name, s.VolumeID, s.Progress, s.CreatedAt)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
|
||||
func testSnapshotGet(t *testing.T, client *gophercloud.ServiceClient, id string) {
|
||||
_, err := snapshots.Get(client, id).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func testSnapshotUpdate(t *testing.T, client *gophercloud.ServiceClient, id string) {
|
||||
_, err := snapshots.Update(client, id, snapshots.UpdateOpts{Name: "new_name"}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func testSnapshotDelete(t *testing.T, client *gophercloud.ServiceClient, id string) {
|
||||
res := snapshots.Delete(client, id)
|
||||
th.AssertNoErr(t, res.Err)
|
||||
t.Logf("Deleted snapshot %s", id)
|
||||
}
|
71
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/blockstorage/v1/volume_test.go
generated
vendored
Normal file
71
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/blockstorage/v1/volume_test.go
generated
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
// +build acceptance blockstorage volumes
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
os "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
"github.com/rackspace/gophercloud/rackspace/blockstorage/v1/volumes"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestVolumes(t *testing.T) {
|
||||
client := setup(t)
|
||||
|
||||
t.Logf("Listing volumes")
|
||||
testVolumeList(t, client)
|
||||
|
||||
t.Logf("Creating volume")
|
||||
volumeID := testVolumeCreate(t, client)
|
||||
|
||||
t.Logf("Getting volume %s", volumeID)
|
||||
testVolumeGet(t, client, volumeID)
|
||||
|
||||
t.Logf("Updating volume %s", volumeID)
|
||||
testVolumeUpdate(t, client, volumeID)
|
||||
|
||||
t.Logf("Deleting volume %s", volumeID)
|
||||
testVolumeDelete(t, client, volumeID)
|
||||
}
|
||||
|
||||
func testVolumeList(t *testing.T, client *gophercloud.ServiceClient) {
|
||||
volumes.List(client).EachPage(func(page pagination.Page) (bool, error) {
|
||||
vList, err := volumes.ExtractVolumes(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, v := range vList {
|
||||
t.Logf("Volume: ID [%s] Name [%s] Type [%s] Created [%s]", v.ID, v.Name,
|
||||
v.VolumeType, v.CreatedAt)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
|
||||
func testVolumeCreate(t *testing.T, client *gophercloud.ServiceClient) string {
|
||||
vol, err := volumes.Create(client, os.CreateOpts{Size: 75}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Created volume: ID [%s] Size [%s]", vol.ID, vol.Size)
|
||||
return vol.ID
|
||||
}
|
||||
|
||||
func testVolumeGet(t *testing.T, client *gophercloud.ServiceClient, id string) {
|
||||
vol, err := volumes.Get(client, id).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Created volume: ID [%s] Size [%s]", vol.ID, vol.Size)
|
||||
}
|
||||
|
||||
func testVolumeUpdate(t *testing.T, client *gophercloud.ServiceClient, id string) {
|
||||
vol, err := volumes.Update(client, id, volumes.UpdateOpts{Name: "new_name"}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Created volume: ID [%s] Name [%s]", vol.ID, vol.Name)
|
||||
}
|
||||
|
||||
func testVolumeDelete(t *testing.T, client *gophercloud.ServiceClient, id string) {
|
||||
res := volumes.Delete(client, id)
|
||||
th.AssertNoErr(t, res.Err)
|
||||
t.Logf("Deleted volume %s", id)
|
||||
}
|
46
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/blockstorage/v1/volume_type_test.go
generated
vendored
Normal file
46
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/blockstorage/v1/volume_type_test.go
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
// +build acceptance blockstorage volumetypes
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
"github.com/rackspace/gophercloud/rackspace/blockstorage/v1/volumetypes"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestAll(t *testing.T) {
|
||||
client := setup(t)
|
||||
|
||||
t.Logf("Listing volume types")
|
||||
id := testList(t, client)
|
||||
|
||||
t.Logf("Getting volume type %s", id)
|
||||
testGet(t, client, id)
|
||||
}
|
||||
|
||||
func testList(t *testing.T, client *gophercloud.ServiceClient) string {
|
||||
var lastID string
|
||||
|
||||
volumetypes.List(client).EachPage(func(page pagination.Page) (bool, error) {
|
||||
typeList, err := volumetypes.ExtractVolumeTypes(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, vt := range typeList {
|
||||
t.Logf("Volume type: ID [%s] Name [%s]", vt.ID, vt.Name)
|
||||
lastID = vt.ID
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
return lastID
|
||||
}
|
||||
|
||||
func testGet(t *testing.T, client *gophercloud.ServiceClient, id string) {
|
||||
vt, err := volumetypes.Get(client, id).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Volume: ID [%s] Name [%s]", vt.ID, vt.Name)
|
||||
}
|
28
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/client_test.go
generated
vendored
Normal file
28
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/client_test.go
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
// +build acceptance
|
||||
|
||||
package rackspace
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/acceptance/tools"
|
||||
"github.com/rackspace/gophercloud/rackspace"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestAuthenticatedClient(t *testing.T) {
|
||||
// Obtain credentials from the environment.
|
||||
ao, err := rackspace.AuthOptionsFromEnv()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
client, err := rackspace.AuthenticatedClient(tools.OnlyRS(ao))
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to authenticate: %v", err)
|
||||
}
|
||||
|
||||
if client.TokenID == "" {
|
||||
t.Errorf("No token ID assigned to the client")
|
||||
}
|
||||
|
||||
t.Logf("Client successfully acquired a token: %v", client.TokenID)
|
||||
}
|
46
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/compute/v2/bootfromvolume_test.go
generated
vendored
Normal file
46
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/compute/v2/bootfromvolume_test.go
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
// +build acceptance
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/acceptance/tools"
|
||||
osBFV "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
|
||||
"github.com/rackspace/gophercloud/rackspace/compute/v2/bootfromvolume"
|
||||
"github.com/rackspace/gophercloud/rackspace/compute/v2/servers"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestBootFromVolume(t *testing.T) {
|
||||
client, err := newClient()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping test that requires server creation in short mode.")
|
||||
}
|
||||
|
||||
options, err := optionsFromEnv()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
name := tools.RandomString("Gophercloud-", 8)
|
||||
t.Logf("Creating server [%s].", name)
|
||||
|
||||
bd := []osBFV.BlockDevice{
|
||||
osBFV.BlockDevice{
|
||||
UUID: options.imageID,
|
||||
SourceType: osBFV.Image,
|
||||
VolumeSize: 10,
|
||||
},
|
||||
}
|
||||
|
||||
server, err := bootfromvolume.Create(client, servers.CreateOpts{
|
||||
Name: name,
|
||||
FlavorRef: "performance1-1",
|
||||
BlockDevice: bd,
|
||||
}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Created server: %+v\n", server)
|
||||
//defer deleteServer(t, client, server)
|
||||
t.Logf("Deleting server [%s]...", name)
|
||||
}
|
60
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/compute/v2/compute_test.go
generated
vendored
Normal file
60
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/compute/v2/compute_test.go
generated
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
// +build acceptance
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/acceptance/tools"
|
||||
"github.com/rackspace/gophercloud/rackspace"
|
||||
)
|
||||
|
||||
func newClient() (*gophercloud.ServiceClient, error) {
|
||||
// Obtain credentials from the environment.
|
||||
options, err := rackspace.AuthOptionsFromEnv()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options = tools.OnlyRS(options)
|
||||
region := os.Getenv("RS_REGION")
|
||||
|
||||
if options.Username == "" {
|
||||
return nil, errors.New("Please provide a Rackspace username as RS_USERNAME.")
|
||||
}
|
||||
if options.APIKey == "" {
|
||||
return nil, errors.New("Please provide a Rackspace API key as RS_API_KEY.")
|
||||
}
|
||||
if region == "" {
|
||||
return nil, errors.New("Please provide a Rackspace region as RS_REGION.")
|
||||
}
|
||||
|
||||
client, err := rackspace.AuthenticatedClient(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rackspace.NewComputeV2(client, gophercloud.EndpointOpts{
|
||||
Region: region,
|
||||
})
|
||||
}
|
||||
|
||||
type serverOpts struct {
|
||||
imageID string
|
||||
flavorID string
|
||||
}
|
||||
|
||||
func optionsFromEnv() (*serverOpts, error) {
|
||||
options := &serverOpts{
|
||||
imageID: os.Getenv("RS_IMAGE_ID"),
|
||||
flavorID: os.Getenv("RS_FLAVOR_ID"),
|
||||
}
|
||||
if options.imageID == "" {
|
||||
return nil, errors.New("Please provide a valid Rackspace image ID as RS_IMAGE_ID")
|
||||
}
|
||||
if options.flavorID == "" {
|
||||
return nil, errors.New("Please provide a valid Rackspace flavor ID as RS_FLAVOR_ID")
|
||||
}
|
||||
return options, nil
|
||||
}
|
61
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/compute/v2/flavors_test.go
generated
vendored
Normal file
61
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/compute/v2/flavors_test.go
generated
vendored
Normal file
|
@ -0,0 +1,61 @@
|
|||
// +build acceptance
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
"github.com/rackspace/gophercloud/rackspace/compute/v2/flavors"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestListFlavors(t *testing.T) {
|
||||
client, err := newClient()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
count := 0
|
||||
err = flavors.ListDetail(client, nil).EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
t.Logf("-- Page %0d --", count)
|
||||
|
||||
fs, err := flavors.ExtractFlavors(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for i, flavor := range fs {
|
||||
t.Logf("[%02d] id=[%s]", i, flavor.ID)
|
||||
t.Logf(" name=[%s]", flavor.Name)
|
||||
t.Logf(" disk=[%d]", flavor.Disk)
|
||||
t.Logf(" RAM=[%d]", flavor.RAM)
|
||||
t.Logf(" rxtx_factor=[%f]", flavor.RxTxFactor)
|
||||
t.Logf(" swap=[%d]", flavor.Swap)
|
||||
t.Logf(" VCPUs=[%d]", flavor.VCPUs)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
if count == 0 {
|
||||
t.Errorf("No flavors listed!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFlavor(t *testing.T) {
|
||||
client, err := newClient()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
options, err := optionsFromEnv()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
flavor, err := flavors.Get(client, options.flavorID).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Requested flavor:")
|
||||
t.Logf(" id=[%s]", flavor.ID)
|
||||
t.Logf(" name=[%s]", flavor.Name)
|
||||
t.Logf(" disk=[%d]", flavor.Disk)
|
||||
t.Logf(" RAM=[%d]", flavor.RAM)
|
||||
t.Logf(" rxtx_factor=[%f]", flavor.RxTxFactor)
|
||||
t.Logf(" swap=[%d]", flavor.Swap)
|
||||
t.Logf(" VCPUs=[%d]", flavor.VCPUs)
|
||||
}
|
63
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/compute/v2/images_test.go
generated
vendored
Normal file
63
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/compute/v2/images_test.go
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
// +build acceptance
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
"github.com/rackspace/gophercloud/rackspace/compute/v2/images"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestListImages(t *testing.T) {
|
||||
client, err := newClient()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
count := 0
|
||||
err = images.ListDetail(client, nil).EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
t.Logf("-- Page %02d --", count)
|
||||
|
||||
is, err := images.ExtractImages(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for i, image := range is {
|
||||
t.Logf("[%02d] id=[%s]", i, image.ID)
|
||||
t.Logf(" name=[%s]", image.Name)
|
||||
t.Logf(" created=[%s]", image.Created)
|
||||
t.Logf(" updated=[%s]", image.Updated)
|
||||
t.Logf(" min disk=[%d]", image.MinDisk)
|
||||
t.Logf(" min RAM=[%d]", image.MinRAM)
|
||||
t.Logf(" progress=[%d]", image.Progress)
|
||||
t.Logf(" status=[%s]", image.Status)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
if count < 1 {
|
||||
t.Errorf("Expected at least one page of images.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetImage(t *testing.T) {
|
||||
client, err := newClient()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
options, err := optionsFromEnv()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
image, err := images.Get(client, options.imageID).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Requested image:")
|
||||
t.Logf(" id=[%s]", image.ID)
|
||||
t.Logf(" name=[%s]", image.Name)
|
||||
t.Logf(" created=[%s]", image.Created)
|
||||
t.Logf(" updated=[%s]", image.Updated)
|
||||
t.Logf(" min disk=[%d]", image.MinDisk)
|
||||
t.Logf(" min RAM=[%d]", image.MinRAM)
|
||||
t.Logf(" progress=[%d]", image.Progress)
|
||||
t.Logf(" status=[%s]", image.Status)
|
||||
}
|
87
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/compute/v2/keypairs_test.go
generated
vendored
Normal file
87
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/compute/v2/keypairs_test.go
generated
vendored
Normal file
|
@ -0,0 +1,87 @@
|
|||
// +build acceptance rackspace
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/acceptance/tools"
|
||||
os "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
"github.com/rackspace/gophercloud/rackspace/compute/v2/keypairs"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func deleteKeyPair(t *testing.T, client *gophercloud.ServiceClient, name string) {
|
||||
err := keypairs.Delete(client, name).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Successfully deleted key [%s].", name)
|
||||
}
|
||||
|
||||
func TestCreateKeyPair(t *testing.T) {
|
||||
client, err := newClient()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
name := tools.RandomString("createdkey-", 8)
|
||||
k, err := keypairs.Create(client, os.CreateOpts{Name: name}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
defer deleteKeyPair(t, client, name)
|
||||
|
||||
t.Logf("Created a new keypair:")
|
||||
t.Logf(" name=[%s]", k.Name)
|
||||
t.Logf(" fingerprint=[%s]", k.Fingerprint)
|
||||
t.Logf(" publickey=[%s]", tools.Elide(k.PublicKey))
|
||||
t.Logf(" privatekey=[%s]", tools.Elide(k.PrivateKey))
|
||||
t.Logf(" userid=[%s]", k.UserID)
|
||||
}
|
||||
|
||||
func TestImportKeyPair(t *testing.T) {
|
||||
client, err := newClient()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
name := tools.RandomString("importedkey-", 8)
|
||||
pubkey := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDlIQ3r+zd97kb9Hzmujd3V6pbO53eb3Go4q2E8iqVGWQfZTrFdL9KACJnqJIm9HmncfRkUTxE37hqeGCCv8uD+ZPmPiZG2E60OX1mGDjbbzAyReRwYWXgXHopggZTLak5k4mwZYaxwaufbVBDRn847e01lZnaXaszEToLM37NLw+uz29sl3TwYy2R0RGHPwPc160aWmdLjSyd1Nd4c9pvvOP/EoEuBjIC6NJJwg2Rvg9sjjx9jYj0QUgc8CqKLN25oMZ69kNJzlFylKRUoeeVr89txlR59yehJWk6Uw6lYFTdJmcmQOFVAJ12RMmS1hLWCM8UzAgtw+EDa0eqBxBDl smash@winter"
|
||||
|
||||
k, err := keypairs.Create(client, os.CreateOpts{
|
||||
Name: name,
|
||||
PublicKey: pubkey,
|
||||
}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
defer deleteKeyPair(t, client, name)
|
||||
|
||||
th.CheckEquals(t, pubkey, k.PublicKey)
|
||||
th.CheckEquals(t, "", k.PrivateKey)
|
||||
|
||||
t.Logf("Imported an existing keypair:")
|
||||
t.Logf(" name=[%s]", k.Name)
|
||||
t.Logf(" fingerprint=[%s]", k.Fingerprint)
|
||||
t.Logf(" publickey=[%s]", tools.Elide(k.PublicKey))
|
||||
t.Logf(" privatekey=[%s]", tools.Elide(k.PrivateKey))
|
||||
t.Logf(" userid=[%s]", k.UserID)
|
||||
}
|
||||
|
||||
func TestListKeyPairs(t *testing.T) {
|
||||
client, err := newClient()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
count := 0
|
||||
err = keypairs.List(client).EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
t.Logf("--- %02d ---", count)
|
||||
|
||||
ks, err := keypairs.ExtractKeyPairs(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for i, keypair := range ks {
|
||||
t.Logf("[%02d] name=[%s]", i, keypair.Name)
|
||||
t.Logf(" fingerprint=[%s]", keypair.Fingerprint)
|
||||
t.Logf(" publickey=[%s]", tools.Elide(keypair.PublicKey))
|
||||
t.Logf(" privatekey=[%s]", tools.Elide(keypair.PrivateKey))
|
||||
t.Logf(" userid=[%s]", keypair.UserID)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
53
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/compute/v2/networks_test.go
generated
vendored
Normal file
53
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/compute/v2/networks_test.go
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
// +build acceptance rackspace
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
"github.com/rackspace/gophercloud/rackspace/compute/v2/networks"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestNetworks(t *testing.T) {
|
||||
client, err := newClient()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
// Create a network
|
||||
n, err := networks.Create(client, networks.CreateOpts{Label: "sample_network", CIDR: "172.20.0.0/24"}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Created network: %+v\n", n)
|
||||
defer networks.Delete(client, n.ID)
|
||||
th.AssertEquals(t, n.Label, "sample_network")
|
||||
th.AssertEquals(t, n.CIDR, "172.20.0.0/24")
|
||||
networkID := n.ID
|
||||
|
||||
// List networks
|
||||
pager := networks.List(client)
|
||||
err = pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
t.Logf("--- Page ---")
|
||||
|
||||
networkList, err := networks.ExtractNetworks(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, n := range networkList {
|
||||
t.Logf("Network: ID [%s] Label [%s] CIDR [%s]",
|
||||
n.ID, n.Label, n.CIDR)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.CheckNoErr(t, err)
|
||||
|
||||
// Get a network
|
||||
if networkID == "" {
|
||||
t.Fatalf("In order to retrieve a network, the NetworkID must be set")
|
||||
}
|
||||
n, err = networks.Get(client, networkID).Extract()
|
||||
t.Logf("Retrieved Network: %+v\n", n)
|
||||
th.AssertNoErr(t, err)
|
||||
th.AssertEquals(t, n.CIDR, "172.20.0.0/24")
|
||||
th.AssertEquals(t, n.Label, "sample_network")
|
||||
th.AssertEquals(t, n.ID, networkID)
|
||||
}
|
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/compute/v2/pkg.go
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/compute/v2/pkg.go
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
package v2
|
204
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/compute/v2/servers_test.go
generated
vendored
Normal file
204
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/compute/v2/servers_test.go
generated
vendored
Normal file
|
@ -0,0 +1,204 @@
|
|||
// +build acceptance
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/acceptance/tools"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/diskconfig"
|
||||
oskey "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
|
||||
os "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
"github.com/rackspace/gophercloud/rackspace/compute/v2/keypairs"
|
||||
"github.com/rackspace/gophercloud/rackspace/compute/v2/servers"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func createServerKeyPair(t *testing.T, client *gophercloud.ServiceClient) *oskey.KeyPair {
|
||||
name := tools.RandomString("importedkey-", 8)
|
||||
pubkey := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDlIQ3r+zd97kb9Hzmujd3V6pbO53eb3Go4q2E8iqVGWQfZTrFdL9KACJnqJIm9HmncfRkUTxE37hqeGCCv8uD+ZPmPiZG2E60OX1mGDjbbzAyReRwYWXgXHopggZTLak5k4mwZYaxwaufbVBDRn847e01lZnaXaszEToLM37NLw+uz29sl3TwYy2R0RGHPwPc160aWmdLjSyd1Nd4c9pvvOP/EoEuBjIC6NJJwg2Rvg9sjjx9jYj0QUgc8CqKLN25oMZ69kNJzlFylKRUoeeVr89txlR59yehJWk6Uw6lYFTdJmcmQOFVAJ12RMmS1hLWCM8UzAgtw+EDa0eqBxBDl smash@winter"
|
||||
|
||||
k, err := keypairs.Create(client, oskey.CreateOpts{
|
||||
Name: name,
|
||||
PublicKey: pubkey,
|
||||
}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
return k
|
||||
}
|
||||
|
||||
func createServer(t *testing.T, client *gophercloud.ServiceClient, keyName string) *os.Server {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping test that requires server creation in short mode.")
|
||||
}
|
||||
|
||||
options, err := optionsFromEnv()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
name := tools.RandomString("Gophercloud-", 8)
|
||||
|
||||
pwd := tools.MakeNewPassword("")
|
||||
|
||||
opts := &servers.CreateOpts{
|
||||
Name: name,
|
||||
ImageRef: options.imageID,
|
||||
FlavorRef: options.flavorID,
|
||||
DiskConfig: diskconfig.Manual,
|
||||
AdminPass: pwd,
|
||||
}
|
||||
|
||||
if keyName != "" {
|
||||
opts.KeyPair = keyName
|
||||
}
|
||||
|
||||
t.Logf("Creating server [%s].", name)
|
||||
s, err := servers.Create(client, opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Creating server.")
|
||||
|
||||
err = servers.WaitForStatus(client, s.ID, "ACTIVE", 300)
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Server created successfully.")
|
||||
|
||||
th.CheckEquals(t, pwd, s.AdminPass)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func logServer(t *testing.T, server *os.Server, index int) {
|
||||
if index == -1 {
|
||||
t.Logf(" id=[%s]", server.ID)
|
||||
} else {
|
||||
t.Logf("[%02d] id=[%s]", index, server.ID)
|
||||
}
|
||||
t.Logf(" name=[%s]", server.Name)
|
||||
t.Logf(" tenant ID=[%s]", server.TenantID)
|
||||
t.Logf(" user ID=[%s]", server.UserID)
|
||||
t.Logf(" updated=[%s]", server.Updated)
|
||||
t.Logf(" created=[%s]", server.Created)
|
||||
t.Logf(" host ID=[%s]", server.HostID)
|
||||
t.Logf(" access IPv4=[%s]", server.AccessIPv4)
|
||||
t.Logf(" access IPv6=[%s]", server.AccessIPv6)
|
||||
t.Logf(" image=[%v]", server.Image)
|
||||
t.Logf(" flavor=[%v]", server.Flavor)
|
||||
t.Logf(" addresses=[%v]", server.Addresses)
|
||||
t.Logf(" metadata=[%v]", server.Metadata)
|
||||
t.Logf(" links=[%v]", server.Links)
|
||||
t.Logf(" keyname=[%s]", server.KeyName)
|
||||
t.Logf(" admin password=[%s]", server.AdminPass)
|
||||
t.Logf(" status=[%s]", server.Status)
|
||||
t.Logf(" progress=[%d]", server.Progress)
|
||||
}
|
||||
|
||||
func getServer(t *testing.T, client *gophercloud.ServiceClient, server *os.Server) {
|
||||
t.Logf("> servers.Get")
|
||||
|
||||
details, err := servers.Get(client, server.ID).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
logServer(t, details, -1)
|
||||
}
|
||||
|
||||
func listServers(t *testing.T, client *gophercloud.ServiceClient) {
|
||||
t.Logf("> servers.List")
|
||||
|
||||
count := 0
|
||||
err := servers.List(client, nil).EachPage(func(page pagination.Page) (bool, error) {
|
||||
count++
|
||||
t.Logf("--- Page %02d ---", count)
|
||||
|
||||
s, err := servers.ExtractServers(page)
|
||||
th.AssertNoErr(t, err)
|
||||
for index, server := range s {
|
||||
logServer(t, &server, index)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func changeAdminPassword(t *testing.T, client *gophercloud.ServiceClient, server *os.Server) {
|
||||
t.Logf("> servers.ChangeAdminPassword")
|
||||
|
||||
original := server.AdminPass
|
||||
|
||||
t.Logf("Changing server password.")
|
||||
err := servers.ChangeAdminPassword(client, server.ID, tools.MakeNewPassword(original)).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
err = servers.WaitForStatus(client, server.ID, "ACTIVE", 300)
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Password changed successfully.")
|
||||
}
|
||||
|
||||
func rebootServer(t *testing.T, client *gophercloud.ServiceClient, server *os.Server) {
|
||||
t.Logf("> servers.Reboot")
|
||||
|
||||
err := servers.Reboot(client, server.ID, os.HardReboot).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
err = servers.WaitForStatus(client, server.ID, "ACTIVE", 300)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Server successfully rebooted.")
|
||||
}
|
||||
|
||||
func rebuildServer(t *testing.T, client *gophercloud.ServiceClient, server *os.Server) {
|
||||
t.Logf("> servers.Rebuild")
|
||||
|
||||
options, err := optionsFromEnv()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
opts := servers.RebuildOpts{
|
||||
Name: tools.RandomString("RenamedGopher", 16),
|
||||
AdminPass: tools.MakeNewPassword(server.AdminPass),
|
||||
ImageID: options.imageID,
|
||||
DiskConfig: diskconfig.Manual,
|
||||
}
|
||||
after, err := servers.Rebuild(client, server.ID, opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
th.CheckEquals(t, after.ID, server.ID)
|
||||
|
||||
err = servers.WaitForStatus(client, after.ID, "ACTIVE", 300)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Server successfully rebuilt.")
|
||||
logServer(t, after, -1)
|
||||
}
|
||||
|
||||
func deleteServer(t *testing.T, client *gophercloud.ServiceClient, server *os.Server) {
|
||||
t.Logf("> servers.Delete")
|
||||
|
||||
res := servers.Delete(client, server.ID)
|
||||
th.AssertNoErr(t, res.Err)
|
||||
|
||||
t.Logf("Server deleted successfully.")
|
||||
}
|
||||
|
||||
func deleteServerKeyPair(t *testing.T, client *gophercloud.ServiceClient, k *oskey.KeyPair) {
|
||||
t.Logf("> keypairs.Delete")
|
||||
|
||||
err := keypairs.Delete(client, k.Name).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Keypair deleted successfully.")
|
||||
}
|
||||
|
||||
func TestServerOperations(t *testing.T) {
|
||||
client, err := newClient()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
kp := createServerKeyPair(t, client)
|
||||
defer deleteServerKeyPair(t, client, kp)
|
||||
|
||||
server := createServer(t, client, kp.Name)
|
||||
defer deleteServer(t, client, server)
|
||||
|
||||
getServer(t, client, server)
|
||||
listServers(t, client)
|
||||
changeAdminPassword(t, client, server)
|
||||
rebootServer(t, client, server)
|
||||
rebuildServer(t, client, server)
|
||||
}
|
53
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/compute/v2/virtualinterfaces_test.go
generated
vendored
Normal file
53
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/compute/v2/virtualinterfaces_test.go
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
// +build acceptance rackspace
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
"github.com/rackspace/gophercloud/rackspace/compute/v2/networks"
|
||||
"github.com/rackspace/gophercloud/rackspace/compute/v2/virtualinterfaces"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestVirtualInterfaces(t *testing.T) {
|
||||
client, err := newClient()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
// Create a server
|
||||
server := createServer(t, client, "")
|
||||
t.Logf("Created Server: %v\n", server)
|
||||
defer deleteServer(t, client, server)
|
||||
serverID := server.ID
|
||||
|
||||
// Create a network
|
||||
n, err := networks.Create(client, networks.CreateOpts{Label: "sample_network", CIDR: "172.20.0.0/24"}).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Created Network: %v\n", n)
|
||||
defer networks.Delete(client, n.ID)
|
||||
networkID := n.ID
|
||||
|
||||
// Create a virtual interface
|
||||
vi, err := virtualinterfaces.Create(client, serverID, networkID).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Created virtual interface: %+v\n", vi)
|
||||
defer virtualinterfaces.Delete(client, serverID, vi.ID)
|
||||
|
||||
// List virtual interfaces
|
||||
pager := virtualinterfaces.List(client, serverID)
|
||||
err = pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
t.Logf("--- Page ---")
|
||||
|
||||
virtualinterfacesList, err := virtualinterfaces.ExtractVirtualInterfaces(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, vi := range virtualinterfacesList {
|
||||
t.Logf("Virtual Interface: ID [%s] MAC Address [%s] IP Addresses [%v]",
|
||||
vi.ID, vi.MACAddress, vi.IPAddresses)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.CheckNoErr(t, err)
|
||||
}
|
54
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/identity/v2/extension_test.go
generated
vendored
Normal file
54
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/identity/v2/extension_test.go
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
// +build acceptance
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
extensions2 "github.com/rackspace/gophercloud/rackspace/identity/v2/extensions"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestExtensions(t *testing.T) {
|
||||
service := authenticatedClient(t)
|
||||
|
||||
t.Logf("Extensions available on this identity endpoint:")
|
||||
count := 0
|
||||
var chosen string
|
||||
err := extensions2.List(service).EachPage(func(page pagination.Page) (bool, error) {
|
||||
t.Logf("--- Page %02d ---", count)
|
||||
|
||||
extensions, err := extensions2.ExtractExtensions(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for i, ext := range extensions {
|
||||
if chosen == "" {
|
||||
chosen = ext.Alias
|
||||
}
|
||||
|
||||
t.Logf("[%02d] name=[%s] namespace=[%s]", i, ext.Name, ext.Namespace)
|
||||
t.Logf(" alias=[%s] updated=[%s]", ext.Alias, ext.Updated)
|
||||
t.Logf(" description=[%s]", ext.Description)
|
||||
}
|
||||
|
||||
count++
|
||||
return true, nil
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
if chosen == "" {
|
||||
t.Logf("No extensions found.")
|
||||
return
|
||||
}
|
||||
|
||||
ext, err := extensions2.Get(service, chosen).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Detail for extension [%s]:", chosen)
|
||||
t.Logf(" name=[%s]", ext.Name)
|
||||
t.Logf(" namespace=[%s]", ext.Namespace)
|
||||
t.Logf(" alias=[%s]", ext.Alias)
|
||||
t.Logf(" updated=[%s]", ext.Updated)
|
||||
t.Logf(" description=[%s]", ext.Description)
|
||||
}
|
50
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/identity/v2/identity_test.go
generated
vendored
Normal file
50
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/identity/v2/identity_test.go
generated
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
// +build acceptance
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/acceptance/tools"
|
||||
"github.com/rackspace/gophercloud/rackspace"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func rackspaceAuthOptions(t *testing.T) gophercloud.AuthOptions {
|
||||
// Obtain credentials from the environment.
|
||||
options, err := rackspace.AuthOptionsFromEnv()
|
||||
th.AssertNoErr(t, err)
|
||||
options = tools.OnlyRS(options)
|
||||
|
||||
if options.Username == "" {
|
||||
t.Fatal("Please provide a Rackspace username as RS_USERNAME.")
|
||||
}
|
||||
if options.APIKey == "" {
|
||||
t.Fatal("Please provide a Rackspace API key as RS_API_KEY.")
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
func createClient(t *testing.T, auth bool) *gophercloud.ServiceClient {
|
||||
ao := rackspaceAuthOptions(t)
|
||||
|
||||
provider, err := rackspace.NewClient(ao.IdentityEndpoint)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
if auth {
|
||||
err = rackspace.Authenticate(provider, ao)
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
return rackspace.NewIdentityV2(provider)
|
||||
}
|
||||
|
||||
func unauthenticatedClient(t *testing.T) *gophercloud.ServiceClient {
|
||||
return createClient(t, false)
|
||||
}
|
||||
|
||||
func authenticatedClient(t *testing.T) *gophercloud.ServiceClient {
|
||||
return createClient(t, true)
|
||||
}
|
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/identity/v2/pkg.go
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/identity/v2/pkg.go
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
package v2
|
59
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/identity/v2/role_test.go
generated
vendored
Normal file
59
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/identity/v2/role_test.go
generated
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
// +build acceptance identity roles
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
os "github.com/rackspace/gophercloud/openstack/identity/v2/extensions/admin/roles"
|
||||
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
"github.com/rackspace/gophercloud/rackspace/identity/v2/roles"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestRoles(t *testing.T) {
|
||||
client := authenticatedClient(t)
|
||||
|
||||
userID := createUser(t, client)
|
||||
roleID := listRoles(t, client)
|
||||
|
||||
addUserRole(t, client, userID, roleID)
|
||||
|
||||
deleteUserRole(t, client, userID, roleID)
|
||||
|
||||
deleteUser(t, client, userID)
|
||||
}
|
||||
|
||||
func listRoles(t *testing.T, client *gophercloud.ServiceClient) string {
|
||||
var roleID string
|
||||
|
||||
err := roles.List(client).EachPage(func(page pagination.Page) (bool, error) {
|
||||
roleList, err := os.ExtractRoles(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, role := range roleList {
|
||||
t.Logf("Listing role: ID [%s] Name [%s]", role.ID, role.Name)
|
||||
roleID = role.ID
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
return roleID
|
||||
}
|
||||
|
||||
func addUserRole(t *testing.T, client *gophercloud.ServiceClient, userID, roleID string) {
|
||||
err := roles.AddUserRole(client, userID, roleID).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Added role %s to user %s", roleID, userID)
|
||||
}
|
||||
|
||||
func deleteUserRole(t *testing.T, client *gophercloud.ServiceClient, userID, roleID string) {
|
||||
err := roles.DeleteUserRole(client, userID, roleID).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Removed role %s from user %s", roleID, userID)
|
||||
}
|
37
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/identity/v2/tenant_test.go
generated
vendored
Normal file
37
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/identity/v2/tenant_test.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
// +build acceptance
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
rstenants "github.com/rackspace/gophercloud/rackspace/identity/v2/tenants"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestTenants(t *testing.T) {
|
||||
service := authenticatedClient(t)
|
||||
|
||||
t.Logf("Tenants available to the currently issued token:")
|
||||
count := 0
|
||||
err := rstenants.List(service, nil).EachPage(func(page pagination.Page) (bool, error) {
|
||||
t.Logf("--- Page %02d ---", count)
|
||||
|
||||
tenants, err := rstenants.ExtractTenants(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for i, tenant := range tenants {
|
||||
t.Logf("[%02d] id=[%s]", i, tenant.ID)
|
||||
t.Logf(" name=[%s] enabled=[%v]", i, tenant.Name, tenant.Enabled)
|
||||
t.Logf(" description=[%s]", tenant.Description)
|
||||
}
|
||||
|
||||
count++
|
||||
return true, nil
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
if count == 0 {
|
||||
t.Errorf("No tenants listed for your current token.")
|
||||
}
|
||||
}
|
93
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/identity/v2/user_test.go
generated
vendored
Normal file
93
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/identity/v2/user_test.go
generated
vendored
Normal file
|
@ -0,0 +1,93 @@
|
|||
// +build acceptance identity
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/acceptance/tools"
|
||||
os "github.com/rackspace/gophercloud/openstack/identity/v2/users"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
"github.com/rackspace/gophercloud/rackspace/identity/v2/users"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestUsers(t *testing.T) {
|
||||
client := authenticatedClient(t)
|
||||
|
||||
userID := createUser(t, client)
|
||||
|
||||
listUsers(t, client)
|
||||
|
||||
getUser(t, client, userID)
|
||||
|
||||
updateUser(t, client, userID)
|
||||
|
||||
resetApiKey(t, client, userID)
|
||||
|
||||
deleteUser(t, client, userID)
|
||||
}
|
||||
|
||||
func createUser(t *testing.T, client *gophercloud.ServiceClient) string {
|
||||
t.Log("Creating user")
|
||||
|
||||
opts := users.CreateOpts{
|
||||
Username: tools.RandomString("user_", 5),
|
||||
Enabled: os.Disabled,
|
||||
Email: "new_user@foo.com",
|
||||
}
|
||||
|
||||
user, err := users.Create(client, opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Created user %s", user.ID)
|
||||
|
||||
return user.ID
|
||||
}
|
||||
|
||||
func listUsers(t *testing.T, client *gophercloud.ServiceClient) {
|
||||
err := users.List(client).EachPage(func(page pagination.Page) (bool, error) {
|
||||
userList, err := os.ExtractUsers(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, user := range userList {
|
||||
t.Logf("Listing user: ID [%s] Username [%s] Email [%s] Enabled? [%s]",
|
||||
user.ID, user.Username, user.Email, strconv.FormatBool(user.Enabled))
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func getUser(t *testing.T, client *gophercloud.ServiceClient, userID string) {
|
||||
_, err := users.Get(client, userID).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Getting user %s", userID)
|
||||
}
|
||||
|
||||
func updateUser(t *testing.T, client *gophercloud.ServiceClient, userID string) {
|
||||
opts := users.UpdateOpts{Username: tools.RandomString("new_name", 5), Email: "new@foo.com"}
|
||||
user, err := users.Update(client, userID, opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Updated user %s: Username [%s] Email [%s]", userID, user.Username, user.Email)
|
||||
}
|
||||
|
||||
func deleteUser(t *testing.T, client *gophercloud.ServiceClient, userID string) {
|
||||
res := users.Delete(client, userID)
|
||||
th.AssertNoErr(t, res.Err)
|
||||
t.Logf("Deleted user %s", userID)
|
||||
}
|
||||
|
||||
func resetApiKey(t *testing.T, client *gophercloud.ServiceClient, userID string) {
|
||||
key, err := users.ResetAPIKey(client, userID).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
if key.APIKey == "" {
|
||||
t.Fatal("failed to reset API key for user")
|
||||
}
|
||||
|
||||
t.Logf("Reset API key for user %s to %s", key.Username, key.APIKey)
|
||||
}
|
94
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/lb/v1/acl_test.go
generated
vendored
Normal file
94
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/lb/v1/acl_test.go
generated
vendored
Normal file
|
@ -0,0 +1,94 @@
|
|||
// +build acceptance lbs
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
"github.com/rackspace/gophercloud/rackspace/lb/v1/acl"
|
||||
"github.com/rackspace/gophercloud/rackspace/lb/v1/lbs"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestACL(t *testing.T) {
|
||||
client := setup(t)
|
||||
|
||||
ids := createLB(t, client, 1)
|
||||
lbID := ids[0]
|
||||
|
||||
createACL(t, client, lbID)
|
||||
|
||||
waitForLB(client, lbID, lbs.ACTIVE)
|
||||
|
||||
networkIDs := showACL(t, client, lbID)
|
||||
|
||||
deleteNetworkItem(t, client, lbID, networkIDs[0])
|
||||
waitForLB(client, lbID, lbs.ACTIVE)
|
||||
|
||||
bulkDeleteACL(t, client, lbID, networkIDs[1:2])
|
||||
waitForLB(client, lbID, lbs.ACTIVE)
|
||||
|
||||
deleteACL(t, client, lbID)
|
||||
waitForLB(client, lbID, lbs.ACTIVE)
|
||||
|
||||
deleteLB(t, client, lbID)
|
||||
}
|
||||
|
||||
func createACL(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
|
||||
opts := acl.CreateOpts{
|
||||
acl.CreateOpt{Address: "206.160.163.21", Type: acl.DENY},
|
||||
acl.CreateOpt{Address: "206.160.165.11", Type: acl.DENY},
|
||||
acl.CreateOpt{Address: "206.160.165.12", Type: acl.DENY},
|
||||
acl.CreateOpt{Address: "206.160.165.13", Type: acl.ALLOW},
|
||||
}
|
||||
|
||||
err := acl.Create(client, lbID, opts).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Created ACL items for LB %d", lbID)
|
||||
}
|
||||
|
||||
func showACL(t *testing.T, client *gophercloud.ServiceClient, lbID int) []int {
|
||||
ids := []int{}
|
||||
|
||||
err := acl.List(client, lbID).EachPage(func(page pagination.Page) (bool, error) {
|
||||
accessList, err := acl.ExtractAccessList(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, i := range accessList {
|
||||
t.Logf("Listing network item: ID [%s] Address [%s] Type [%s]", i.ID, i.Address, i.Type)
|
||||
ids = append(ids, i.ID)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
return ids
|
||||
}
|
||||
|
||||
func deleteNetworkItem(t *testing.T, client *gophercloud.ServiceClient, lbID, itemID int) {
|
||||
err := acl.Delete(client, lbID, itemID).ExtractErr()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Deleted network item %d", itemID)
|
||||
}
|
||||
|
||||
func bulkDeleteACL(t *testing.T, client *gophercloud.ServiceClient, lbID int, items []int) {
|
||||
err := acl.BulkDelete(client, lbID, items).ExtractErr()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Deleted network items %s", intsToStr(items))
|
||||
}
|
||||
|
||||
func deleteACL(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
|
||||
err := acl.DeleteAll(client, lbID).ExtractErr()
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Deleted ACL from LB %d", lbID)
|
||||
}
|
62
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/lb/v1/common.go
generated
vendored
Normal file
62
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/lb/v1/common.go
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
// +build acceptance lbs
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/acceptance/tools"
|
||||
"github.com/rackspace/gophercloud/rackspace"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func newProvider() (*gophercloud.ProviderClient, error) {
|
||||
opts, err := rackspace.AuthOptionsFromEnv()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts = tools.OnlyRS(opts)
|
||||
|
||||
return rackspace.AuthenticatedClient(opts)
|
||||
}
|
||||
|
||||
func newClient() (*gophercloud.ServiceClient, error) {
|
||||
provider, err := newProvider()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rackspace.NewLBV1(provider, gophercloud.EndpointOpts{
|
||||
Region: os.Getenv("RS_REGION"),
|
||||
})
|
||||
}
|
||||
|
||||
func newComputeClient() (*gophercloud.ServiceClient, error) {
|
||||
provider, err := newProvider()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rackspace.NewComputeV2(provider, gophercloud.EndpointOpts{
|
||||
Region: os.Getenv("RS_REGION"),
|
||||
})
|
||||
}
|
||||
|
||||
func setup(t *testing.T) *gophercloud.ServiceClient {
|
||||
client, err := newClient()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
func intsToStr(ids []int) string {
|
||||
strIDs := []string{}
|
||||
for _, id := range ids {
|
||||
strIDs = append(strIDs, strconv.Itoa(id))
|
||||
}
|
||||
return strings.Join(strIDs, ", ")
|
||||
}
|
214
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/lb/v1/lb_test.go
generated
vendored
Normal file
214
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/lb/v1/lb_test.go
generated
vendored
Normal file
|
@ -0,0 +1,214 @@
|
|||
// +build acceptance lbs
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/acceptance/tools"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
"github.com/rackspace/gophercloud/rackspace/lb/v1/lbs"
|
||||
"github.com/rackspace/gophercloud/rackspace/lb/v1/vips"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestLBs(t *testing.T) {
|
||||
client := setup(t)
|
||||
|
||||
ids := createLB(t, client, 3)
|
||||
id := ids[0]
|
||||
|
||||
listLBProtocols(t, client)
|
||||
|
||||
listLBAlgorithms(t, client)
|
||||
|
||||
listLBs(t, client)
|
||||
|
||||
getLB(t, client, id)
|
||||
|
||||
checkLBLogging(t, client, id)
|
||||
|
||||
checkErrorPage(t, client, id)
|
||||
|
||||
getStats(t, client, id)
|
||||
|
||||
updateLB(t, client, id)
|
||||
|
||||
deleteLB(t, client, id)
|
||||
|
||||
batchDeleteLBs(t, client, ids[1:])
|
||||
}
|
||||
|
||||
func createLB(t *testing.T, client *gophercloud.ServiceClient, count int) []int {
|
||||
ids := []int{}
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
opts := lbs.CreateOpts{
|
||||
Name: tools.RandomString("test_", 5),
|
||||
Port: 80,
|
||||
Protocol: "HTTP",
|
||||
VIPs: []vips.VIP{
|
||||
vips.VIP{Type: vips.PUBLIC},
|
||||
},
|
||||
}
|
||||
|
||||
lb, err := lbs.Create(client, opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Created LB %d - waiting for it to build...", lb.ID)
|
||||
waitForLB(client, lb.ID, lbs.ACTIVE)
|
||||
t.Logf("LB %d has reached ACTIVE state", lb.ID)
|
||||
|
||||
ids = append(ids, lb.ID)
|
||||
}
|
||||
|
||||
return ids
|
||||
}
|
||||
|
||||
func waitForLB(client *gophercloud.ServiceClient, id int, state lbs.Status) {
|
||||
gophercloud.WaitFor(60, func() (bool, error) {
|
||||
lb, err := lbs.Get(client, id).Extract()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if lb.Status != state {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
|
||||
func listLBProtocols(t *testing.T, client *gophercloud.ServiceClient) {
|
||||
err := lbs.ListProtocols(client).EachPage(func(page pagination.Page) (bool, error) {
|
||||
pList, err := lbs.ExtractProtocols(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, p := range pList {
|
||||
t.Logf("Listing protocol: Name [%s]", p.Name)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func listLBAlgorithms(t *testing.T, client *gophercloud.ServiceClient) {
|
||||
err := lbs.ListAlgorithms(client).EachPage(func(page pagination.Page) (bool, error) {
|
||||
aList, err := lbs.ExtractAlgorithms(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, a := range aList {
|
||||
t.Logf("Listing algorithm: Name [%s]", a.Name)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func listLBs(t *testing.T, client *gophercloud.ServiceClient) {
|
||||
err := lbs.List(client, lbs.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||
lbList, err := lbs.ExtractLBs(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, lb := range lbList {
|
||||
t.Logf("Listing LB: ID [%d] Name [%s] Protocol [%s] Status [%s] Node count [%d] Port [%d]",
|
||||
lb.ID, lb.Name, lb.Protocol, lb.Status, lb.NodeCount, lb.Port)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func getLB(t *testing.T, client *gophercloud.ServiceClient, id int) {
|
||||
lb, err := lbs.Get(client, id).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Getting LB %d: Created [%s] VIPs [%#v] Logging [%#v] Persistence [%#v] SourceAddrs [%#v]",
|
||||
lb.ID, lb.Created, lb.VIPs, lb.ConnectionLogging, lb.SessionPersistence, lb.SourceAddrs)
|
||||
}
|
||||
|
||||
func updateLB(t *testing.T, client *gophercloud.ServiceClient, id int) {
|
||||
opts := lbs.UpdateOpts{
|
||||
Name: tools.RandomString("new_", 5),
|
||||
Protocol: "TCP",
|
||||
HalfClosed: gophercloud.Enabled,
|
||||
Algorithm: "RANDOM",
|
||||
Port: 8080,
|
||||
Timeout: 100,
|
||||
HTTPSRedirect: gophercloud.Disabled,
|
||||
}
|
||||
|
||||
err := lbs.Update(client, id, opts).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Updating LB %d - waiting for it to finish", id)
|
||||
waitForLB(client, id, lbs.ACTIVE)
|
||||
t.Logf("LB %d has reached ACTIVE state", id)
|
||||
}
|
||||
|
||||
func deleteLB(t *testing.T, client *gophercloud.ServiceClient, id int) {
|
||||
err := lbs.Delete(client, id).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Deleted LB %d", id)
|
||||
}
|
||||
|
||||
func batchDeleteLBs(t *testing.T, client *gophercloud.ServiceClient, ids []int) {
|
||||
err := lbs.BulkDelete(client, ids).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Deleted LB %s", intsToStr(ids))
|
||||
}
|
||||
|
||||
func checkLBLogging(t *testing.T, client *gophercloud.ServiceClient, id int) {
|
||||
err := lbs.EnableLogging(client, id).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Enabled logging for LB %d", id)
|
||||
|
||||
res, err := lbs.IsLoggingEnabled(client, id)
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("LB %d log enabled? %s", id, strconv.FormatBool(res))
|
||||
|
||||
waitForLB(client, id, lbs.ACTIVE)
|
||||
|
||||
err = lbs.DisableLogging(client, id).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Disabled logging for LB %d", id)
|
||||
}
|
||||
|
||||
func checkErrorPage(t *testing.T, client *gophercloud.ServiceClient, id int) {
|
||||
content, err := lbs.SetErrorPage(client, id, "<html>New content!</html>").Extract()
|
||||
t.Logf("Set error page for LB %d", id)
|
||||
|
||||
content, err = lbs.GetErrorPage(client, id).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Error page for LB %d: %s", id, content)
|
||||
|
||||
err = lbs.DeleteErrorPage(client, id).ExtractErr()
|
||||
t.Logf("Deleted error page for LB %d", id)
|
||||
}
|
||||
|
||||
func getStats(t *testing.T, client *gophercloud.ServiceClient, id int) {
|
||||
waitForLB(client, id, lbs.ACTIVE)
|
||||
|
||||
stats, err := lbs.GetStats(client, id).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
t.Logf("Stats for LB %d: %#v", id, stats)
|
||||
}
|
||||
|
||||
func checkCaching(t *testing.T, client *gophercloud.ServiceClient, id int) {
|
||||
err := lbs.EnableCaching(client, id).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Enabled caching for LB %d", id)
|
||||
|
||||
res, err := lbs.IsContentCached(client, id)
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Is caching enabled for LB? %s", strconv.FormatBool(res))
|
||||
|
||||
err = lbs.DisableCaching(client, id).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Disabled caching for LB %d", id)
|
||||
}
|
60
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/lb/v1/monitor_test.go
generated
vendored
Normal file
60
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/lb/v1/monitor_test.go
generated
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
// +build acceptance lbs
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/rackspace/lb/v1/lbs"
|
||||
"github.com/rackspace/gophercloud/rackspace/lb/v1/monitors"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestMonitors(t *testing.T) {
|
||||
client := setup(t)
|
||||
|
||||
ids := createLB(t, client, 1)
|
||||
lbID := ids[0]
|
||||
|
||||
getMonitor(t, client, lbID)
|
||||
|
||||
updateMonitor(t, client, lbID)
|
||||
|
||||
deleteMonitor(t, client, lbID)
|
||||
|
||||
deleteLB(t, client, lbID)
|
||||
}
|
||||
|
||||
func getMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
|
||||
hm, err := monitors.Get(client, lbID).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Health monitor for LB %d: Type [%s] Delay [%d] Timeout [%d] AttemptLimit [%d]",
|
||||
lbID, hm.Type, hm.Delay, hm.Timeout, hm.AttemptLimit)
|
||||
}
|
||||
|
||||
func updateMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
|
||||
opts := monitors.UpdateHTTPMonitorOpts{
|
||||
AttemptLimit: 3,
|
||||
Delay: 10,
|
||||
Timeout: 10,
|
||||
BodyRegex: "hello is it me you're looking for",
|
||||
Path: "/foo",
|
||||
StatusRegex: "200",
|
||||
Type: monitors.HTTP,
|
||||
}
|
||||
|
||||
err := monitors.Update(client, lbID, opts).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
waitForLB(client, lbID, lbs.ACTIVE)
|
||||
t.Logf("Updated monitor for LB %d", lbID)
|
||||
}
|
||||
|
||||
func deleteMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
|
||||
err := monitors.Delete(client, lbID).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
waitForLB(client, lbID, lbs.ACTIVE)
|
||||
t.Logf("Deleted monitor for LB %d", lbID)
|
||||
}
|
175
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/lb/v1/node_test.go
generated
vendored
Normal file
175
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/lb/v1/node_test.go
generated
vendored
Normal file
|
@ -0,0 +1,175 @@
|
|||
// +build acceptance lbs
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/acceptance/tools"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/diskconfig"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
"github.com/rackspace/gophercloud/rackspace/compute/v2/servers"
|
||||
"github.com/rackspace/gophercloud/rackspace/lb/v1/lbs"
|
||||
"github.com/rackspace/gophercloud/rackspace/lb/v1/nodes"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestNodes(t *testing.T) {
|
||||
client := setup(t)
|
||||
|
||||
serverIP := findServer(t)
|
||||
ids := createLB(t, client, 1)
|
||||
lbID := ids[0]
|
||||
|
||||
nodeID := addNodes(t, client, lbID, serverIP)
|
||||
|
||||
listNodes(t, client, lbID)
|
||||
|
||||
getNode(t, client, lbID, nodeID)
|
||||
|
||||
updateNode(t, client, lbID, nodeID)
|
||||
|
||||
listEvents(t, client, lbID)
|
||||
|
||||
deleteNode(t, client, lbID, nodeID)
|
||||
|
||||
waitForLB(client, lbID, lbs.ACTIVE)
|
||||
deleteLB(t, client, lbID)
|
||||
}
|
||||
|
||||
func findServer(t *testing.T) string {
|
||||
var serverIP string
|
||||
|
||||
client, err := newComputeClient()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
err = servers.List(client, nil).EachPage(func(page pagination.Page) (bool, error) {
|
||||
sList, err := servers.ExtractServers(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, s := range sList {
|
||||
serverIP = s.AccessIPv4
|
||||
t.Logf("Found an existing server: ID [%s] Public IP [%s]", s.ID, serverIP)
|
||||
break
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
if serverIP == "" {
|
||||
t.Log("No server found, creating one")
|
||||
|
||||
imageRef := os.Getenv("RS_IMAGE_ID")
|
||||
if imageRef == "" {
|
||||
t.Fatalf("OS var RS_IMAGE_ID undefined")
|
||||
}
|
||||
flavorRef := os.Getenv("RS_FLAVOR_ID")
|
||||
if flavorRef == "" {
|
||||
t.Fatalf("OS var RS_FLAVOR_ID undefined")
|
||||
}
|
||||
|
||||
opts := &servers.CreateOpts{
|
||||
Name: tools.RandomString("lb_test_", 5),
|
||||
ImageRef: imageRef,
|
||||
FlavorRef: flavorRef,
|
||||
DiskConfig: diskconfig.Manual,
|
||||
}
|
||||
|
||||
s, err := servers.Create(client, opts).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
serverIP = s.AccessIPv4
|
||||
|
||||
t.Logf("Created server %s, waiting for it to build", s.ID)
|
||||
err = servers.WaitForStatus(client, s.ID, "ACTIVE", 300)
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Server created successfully.")
|
||||
}
|
||||
|
||||
return serverIP
|
||||
}
|
||||
|
||||
func addNodes(t *testing.T, client *gophercloud.ServiceClient, lbID int, serverIP string) int {
|
||||
opts := nodes.CreateOpts{
|
||||
nodes.CreateOpt{
|
||||
Address: serverIP,
|
||||
Port: 80,
|
||||
Condition: nodes.ENABLED,
|
||||
Type: nodes.PRIMARY,
|
||||
},
|
||||
}
|
||||
|
||||
page := nodes.Create(client, lbID, opts)
|
||||
|
||||
nodeList, err := page.ExtractNodes()
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
var nodeID int
|
||||
for _, n := range nodeList {
|
||||
nodeID = n.ID
|
||||
}
|
||||
if nodeID == 0 {
|
||||
t.Fatalf("nodeID could not be extracted from create response")
|
||||
}
|
||||
|
||||
t.Logf("Added node %d to LB %d", nodeID, lbID)
|
||||
waitForLB(client, lbID, lbs.ACTIVE)
|
||||
|
||||
return nodeID
|
||||
}
|
||||
|
||||
func listNodes(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
|
||||
err := nodes.List(client, lbID, nil).EachPage(func(page pagination.Page) (bool, error) {
|
||||
nodeList, err := nodes.ExtractNodes(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, n := range nodeList {
|
||||
t.Logf("Listing node: ID [%d] Address [%s:%d] Status [%s]", n.ID, n.Address, n.Port, n.Status)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func getNode(t *testing.T, client *gophercloud.ServiceClient, lbID int, nodeID int) {
|
||||
node, err := nodes.Get(client, lbID, nodeID).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Getting node %d: Type [%s] Weight [%d]", nodeID, node.Type, node.Weight)
|
||||
}
|
||||
|
||||
func updateNode(t *testing.T, client *gophercloud.ServiceClient, lbID int, nodeID int) {
|
||||
opts := nodes.UpdateOpts{
|
||||
Weight: gophercloud.IntToPointer(10),
|
||||
Condition: nodes.DRAINING,
|
||||
Type: nodes.SECONDARY,
|
||||
}
|
||||
err := nodes.Update(client, lbID, nodeID, opts).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Updated node %d", nodeID)
|
||||
waitForLB(client, lbID, lbs.ACTIVE)
|
||||
}
|
||||
|
||||
func listEvents(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
|
||||
pager := nodes.ListEvents(client, lbID, nodes.ListEventsOpts{})
|
||||
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
eventList, err := nodes.ExtractNodeEvents(page)
|
||||
th.AssertNoErr(t, err)
|
||||
|
||||
for _, e := range eventList {
|
||||
t.Logf("Listing events for node %d: Type [%s] Msg [%s] Severity [%s] Date [%s]",
|
||||
e.NodeID, e.Type, e.DetailedMessage, e.Severity, e.Created)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
th.AssertNoErr(t, err)
|
||||
}
|
||||
|
||||
func deleteNode(t *testing.T, client *gophercloud.ServiceClient, lbID int, nodeID int) {
|
||||
err := nodes.Delete(client, lbID, nodeID).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Deleted node %d", nodeID)
|
||||
}
|
47
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/lb/v1/session_test.go
generated
vendored
Normal file
47
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/lb/v1/session_test.go
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
// +build acceptance lbs
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/rackspace/lb/v1/sessions"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestSession(t *testing.T) {
|
||||
client := setup(t)
|
||||
|
||||
ids := createLB(t, client, 1)
|
||||
lbID := ids[0]
|
||||
|
||||
getSession(t, client, lbID)
|
||||
|
||||
enableSession(t, client, lbID)
|
||||
waitForLB(client, lbID, "ACTIVE")
|
||||
|
||||
disableSession(t, client, lbID)
|
||||
waitForLB(client, lbID, "ACTIVE")
|
||||
|
||||
deleteLB(t, client, lbID)
|
||||
}
|
||||
|
||||
func getSession(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
|
||||
sp, err := sessions.Get(client, lbID).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Session config: Type [%s]", sp.Type)
|
||||
}
|
||||
|
||||
func enableSession(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
|
||||
opts := sessions.CreateOpts{Type: sessions.HTTPCOOKIE}
|
||||
err := sessions.Enable(client, lbID, opts).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Enable %s sessions for %d", opts.Type, lbID)
|
||||
}
|
||||
|
||||
func disableSession(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
|
||||
err := sessions.Disable(client, lbID).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Disable sessions for %d", lbID)
|
||||
}
|
53
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/lb/v1/throttle_test.go
generated
vendored
Normal file
53
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/lb/v1/throttle_test.go
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
// +build acceptance lbs
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/rackspace/lb/v1/throttle"
|
||||
th "github.com/rackspace/gophercloud/testhelper"
|
||||
)
|
||||
|
||||
func TestThrottle(t *testing.T) {
|
||||
client := setup(t)
|
||||
|
||||
ids := createLB(t, client, 1)
|
||||
lbID := ids[0]
|
||||
|
||||
getThrottleConfig(t, client, lbID)
|
||||
|
||||
createThrottleConfig(t, client, lbID)
|
||||
waitForLB(client, lbID, "ACTIVE")
|
||||
|
||||
deleteThrottleConfig(t, client, lbID)
|
||||
waitForLB(client, lbID, "ACTIVE")
|
||||
|
||||
deleteLB(t, client, lbID)
|
||||
}
|
||||
|
||||
func getThrottleConfig(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
|
||||
sp, err := throttle.Get(client, lbID).Extract()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Throttle config: MaxConns [%s]", sp.MaxConnections)
|
||||
}
|
||||
|
||||
func createThrottleConfig(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
|
||||
opts := throttle.CreateOpts{
|
||||
MaxConnections: 200,
|
||||
MaxConnectionRate: 100,
|
||||
MinConnections: 0,
|
||||
RateInterval: 10,
|
||||
}
|
||||
|
||||
err := throttle.Create(client, lbID, opts).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Enable throttling for %d", lbID)
|
||||
}
|
||||
|
||||
func deleteThrottleConfig(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
|
||||
err := throttle.Delete(client, lbID).ExtractErr()
|
||||
th.AssertNoErr(t, err)
|
||||
t.Logf("Disable throttling for %d", lbID)
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue