Updated vendor after update of gophercloud in 88a84f5

This commit is contained in:
Rickard von Essen 2016-02-22 09:45:21 +01:00
parent ba2b438001
commit 0cffcb0c39
14 changed files with 246 additions and 27 deletions

View File

@ -1,11 +1,11 @@
language: go
install:
- go get golang.org/x/crypto/ssh
- go get -v -tags 'fixtures acceptance' ./...
go:
- 1.2
- 1.3
- 1.4
- 1.5
- tip
script: script/cibuild
after_success:
- go get golang.org/x/tools/cmd/cover

View File

@ -3,6 +3,7 @@ package openstack
import (
"fmt"
"net/url"
"strings"
"github.com/rackspace/gophercloud"
tokens2 "github.com/rackspace/gophercloud/openstack/identity/v2/tokens"
@ -64,8 +65,8 @@ func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.Provider
// Authenticate or re-authenticate against the most recent identity service supported at the provided endpoint.
func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
versions := []*utils.Version{
&utils.Version{ID: v20, Priority: 20, Suffix: "/v2.0/"},
&utils.Version{ID: v30, Priority: 30, Suffix: "/v3/"},
{ID: v20, Priority: 20, Suffix: "/v2.0/"},
{ID: v30, Priority: 30, Suffix: "/v3/"},
}
chosen, endpoint, err := utils.ChooseVersion(client, versions)
@ -110,7 +111,7 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc
if options.AllowReauth {
client.ReauthFunc = func() error {
client.TokenID = ""
return AuthenticateV2(client, options)
return v2auth(client, endpoint, options)
}
}
client.TokenID = token.ID
@ -168,7 +169,7 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, options gopherc
if options.AllowReauth {
client.ReauthFunc = func() error {
client.TokenID = ""
return AuthenticateV3(client, options)
return v3auth(client, endpoint, options)
}
}
client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
@ -198,6 +199,40 @@ func NewIdentityV3(client *gophercloud.ProviderClient) *gophercloud.ServiceClien
}
}
func NewIdentityAdminV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
eo.ApplyDefaults("identity")
eo.Availability = gophercloud.AvailabilityAdmin
url, err := client.EndpointLocator(eo)
if err != nil {
return nil, err
}
// Force using v2 API
if strings.Contains(url, "/v3") {
url = strings.Replace(url, "/v3", "/v2.0", -1)
}
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
}
func NewIdentityAdminV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
eo.ApplyDefaults("identity")
eo.Availability = gophercloud.AvailabilityAdmin
url, err := client.EndpointLocator(eo)
if err != nil {
return nil, err
}
// Force using v3 API
if strings.Contains(url, "/v2.0") {
url = strings.Replace(url, "/v2.0", "/v3", -1)
}
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
}
// NewObjectStorageV1 creates a ServiceClient that may be used with the v1 object storage package.
func NewObjectStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
eo.ApplyDefaults("object-store")

View File

@ -15,6 +15,7 @@ const (
Volume SourceType = "volume"
Snapshot SourceType = "snapshot"
Image SourceType = "image"
Blank SourceType = "blank"
)
// BlockDevice is a structure with options for booting a server instance
@ -32,6 +33,9 @@ type BlockDevice struct {
// and "local".
DestinationType string `json:"destination_type"`
// GuestFormat [optional] specifies the format of the block device.
GuestFormat string `json:"guest_format"`
// SourceType [required] must be one of: "volume", "snapshot", "image".
SourceType SourceType `json:"source_type"`
@ -82,6 +86,9 @@ func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) {
if bd.DestinationType != "" {
blockDevice[i]["destination_type"] = bd.DestinationType
}
if bd.GuestFormat != "" {
blockDevice[i]["guest_format"] = bd.GuestFormat
}
}
serverMap["block_device_mapping_v2"] = blockDevice

View File

@ -235,6 +235,12 @@ const SingleServerBody = `
}
`
const ServerPasswordBody = `
{
"password": "xlozO3wLCBRWAa2yDjCCVx8vwNPypxnypmRYDa/zErlQ+EzPe1S/Gz6nfmC52mOlOSCRuUOmG7kqqgejPof6M7bOezS387zjq4LSvvwp28zUknzy4YzfFGhnHAdai3TxUJ26pfQCYrq8UTzmKF2Bq8ioSEtVVzM0A96pDh8W2i7BOz6MdoiVyiev/I1K2LsuipfxSJR7Wdke4zNXJjHHP2RfYsVbZ/k9ANu+Nz4iIH8/7Cacud/pphH7EjrY6a4RZNrjQskrhKYed0YERpotyjYk1eDtRe72GrSiXteqCM4biaQ5w3ruS+AcX//PXk3uJ5kC7d67fPXaVz4WaQRYMg=="
}
`
var (
// ServerHerp is a Server struct that should correspond to the first result in ServerListBody.
ServerHerp = Server{
@ -399,6 +405,18 @@ func HandleServerDeletionSuccessfully(t *testing.T) {
})
}
// HandleServerForceDeletionSuccessfully sets up the test server to respond to a server force deletion
// request.
func HandleServerForceDeletionSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/servers/asdfasdfasdf/action", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "POST")
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
th.TestJSONRequest(t, r, `{ "forceDelete": "" }`)
w.WriteHeader(http.StatusAccepted)
})
}
// HandleServerGetSuccessfully sets up the test server to respond to a server Get request.
func HandleServerGetSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/servers/1234asdf", func(w http.ResponseWriter, r *http.Request) {
@ -662,3 +680,13 @@ func HandleCreateServerImageSuccessfully(t *testing.T) {
})
}
// HandlePasswordGetSuccessfully sets up the test server to respond to a password Get request.
func HandlePasswordGetSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/servers/1234asdf/os-server-password", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
th.TestHeader(t, r, "Accept", "application/json")
fmt.Fprintf(w, ServerPasswordBody)
})
}

View File

@ -303,6 +303,17 @@ func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
return res
}
func ForceDelete(client *gophercloud.ServiceClient, id string) ActionResult {
var req struct {
ForceDelete string `json:"forceDelete"`
}
var res ActionResult
_, res.Err = client.Post(actionURL(client, id), req, nil, nil)
return res
}
// Get requests details on a single server, by ID.
func Get(client *gophercloud.ServiceClient, id string) GetResult {
var result GetResult
@ -850,3 +861,12 @@ func IDFromName(client *gophercloud.ServiceClient, name string) (string, error)
return "", fmt.Errorf("Found %d servers matching %s", serverCount, name)
}
}
// GetPassword makes a request against the nova API to get the encrypted administrative password.
func GetPassword(client *gophercloud.ServiceClient, serverId string) GetPasswordResult {
var res GetPasswordResult
_, res.Err = client.Request("GET", passwordURL(client, serverId), gophercloud.RequestOpts{
JSONResponse: &res.Body,
})
return res
}

View File

@ -1,10 +1,12 @@
package servers
import (
"reflect"
"crypto/rsa"
"encoding/base64"
"fmt"
"path"
"net/url"
"path"
"reflect"
"github.com/mitchellh/mapstructure"
"github.com/rackspace/gophercloud"
@ -82,6 +84,47 @@ type CreateImageResult struct {
gophercloud.Result
}
// GetPasswordResult represent the result of a get os-server-password operation.
type GetPasswordResult struct {
gophercloud.Result
}
// ExtractPassword gets the encrypted password.
// If privateKey != nil the password is decrypted with the private key.
// If privateKey == nil the encrypted password is returned and can be decrypted with:
// echo '<pwd>' | base64 -D | openssl rsautl -decrypt -inkey <private_key>
func (r GetPasswordResult) ExtractPassword(privateKey *rsa.PrivateKey) (string, error) {
if r.Err != nil {
return "", r.Err
}
var response struct {
Password string `mapstructure:"password"`
}
err := mapstructure.Decode(r.Body, &response)
if err == nil && privateKey != nil && response.Password != "" {
return decryptPassword(response.Password, privateKey)
}
return response.Password, err
}
func decryptPassword(encryptedPassword string, privateKey *rsa.PrivateKey) (string, error) {
b64EncryptedPassword := make([]byte, base64.StdEncoding.DecodedLen(len(encryptedPassword)))
n, err := base64.StdEncoding.Decode(b64EncryptedPassword, []byte(encryptedPassword))
if err != nil {
return "", fmt.Errorf("Failed to base64 decode encrypted password: %s", err)
}
password, err := rsa.DecryptPKCS1v15(nil, privateKey, b64EncryptedPassword[0:n])
if err != nil {
return "", fmt.Errorf("Failed to decrypt password: %s", err)
}
return string(password), nil
}
// ExtractImageID gets the ID of the newly created server image from the header
func (res CreateImageResult) ExtractImageID() (string, error) {
if res.Err != nil {

View File

@ -45,3 +45,7 @@ func listAddressesURL(client *gophercloud.ServiceClient, id string) string {
func listAddressesByNetworkURL(client *gophercloud.ServiceClient, id, network string) string {
return client.ServiceURL("servers", id, "ips", network)
}
func passwordURL(client *gophercloud.ServiceClient, id string) string {
return client.ServiceURL("servers", id, "os-server-password")
}

View File

@ -15,9 +15,9 @@ type Scope struct {
}
func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[string]string {
h := c.AuthenticatedHeaders()
h["X-Subject-Token"] = subjectToken
return h
return map[string]string{
"X-Subject-Token": subjectToken,
}
}
// Create authenticates and either generates a new token, or changes the Scope of an existing token.

View File

@ -16,6 +16,7 @@ type ListOpts struct {
ID string `q:"id"`
Name string `q:"name"`
AdminStateUp *bool `q:"admin_state_up"`
Distributed *bool `q:"distributed"`
Status string `q:"status"`
TenantID string `q:"tenant_id"`
Limit int `q:"limit"`
@ -46,6 +47,7 @@ func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager {
type CreateOpts struct {
Name string
AdminStateUp *bool
Distributed *bool
TenantID string
GatewayInfo *GatewayInfo
}
@ -62,6 +64,7 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
type router struct {
Name *string `json:"name,omitempty"`
AdminStateUp *bool `json:"admin_state_up,omitempty"`
Distributed *bool `json:"distributed,omitempty"`
TenantID *string `json:"tenant_id,omitempty"`
GatewayInfo *GatewayInfo `json:"external_gateway_info,omitempty"`
}
@ -73,6 +76,7 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
reqBody := request{Router: router{
Name: gophercloud.MaybeString(opts.Name),
AdminStateUp: opts.AdminStateUp,
Distributed: opts.Distributed,
TenantID: gophercloud.MaybeString(opts.TenantID),
}}
@ -96,6 +100,7 @@ func Get(c *gophercloud.ServiceClient, id string) GetResult {
type UpdateOpts struct {
Name string
AdminStateUp *bool
Distributed *bool
GatewayInfo *GatewayInfo
Routes []Route
}
@ -109,6 +114,7 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu
type router struct {
Name *string `json:"name,omitempty"`
AdminStateUp *bool `json:"admin_state_up,omitempty"`
Distributed *bool `json:"distributed,omitempty"`
GatewayInfo *GatewayInfo `json:"external_gateway_info,omitempty"`
Routes []Route `json:"routes"`
}
@ -120,6 +126,7 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu
reqBody := request{Router: router{
Name: gophercloud.MaybeString(opts.Name),
AdminStateUp: opts.AdminStateUp,
Distributed: opts.Distributed,
}}
if opts.GatewayInfo != nil {

View File

@ -35,6 +35,9 @@ type Router struct {
// Administrative state of the router.
AdminStateUp bool `json:"admin_state_up" mapstructure:"admin_state_up"`
// Whether router is disitrubted or not..
Distributed bool `json:"distributed" mapstructure:"distributed"`
// Human readable name for the router. Does not have to be unique.
Name string `json:"name" mapstructure:"name"`

View File

@ -95,15 +95,16 @@ type CreateOptsBuilder interface {
// CreateOpts represents the attributes used when creating a new port.
type CreateOpts struct {
NetworkID string
Name string
AdminStateUp *bool
MACAddress string
FixedIPs interface{}
DeviceID string
DeviceOwner string
TenantID string
SecurityGroups []string
NetworkID string
Name string
AdminStateUp *bool
MACAddress string
FixedIPs interface{}
DeviceID string
DeviceOwner string
TenantID string
SecurityGroups []string
AllowedAddressPairs []AddressPair
}
// ToPortCreateMap casts a CreateOpts struct to a map.
@ -139,6 +140,9 @@ func (opts CreateOpts) ToPortCreateMap() (map[string]interface{}, error) {
if opts.MACAddress != "" {
p["mac_address"] = opts.MACAddress
}
if opts.AllowedAddressPairs != nil {
p["allowed_address_pairs"] = opts.AllowedAddressPairs
}
return map[string]interface{}{"port": p}, nil
}
@ -168,12 +172,13 @@ type UpdateOptsBuilder interface {
// UpdateOpts represents the attributes used when updating an existing port.
type UpdateOpts struct {
Name string
AdminStateUp *bool
FixedIPs interface{}
DeviceID string
DeviceOwner string
SecurityGroups []string
Name string
AdminStateUp *bool
FixedIPs interface{}
DeviceID string
DeviceOwner string
SecurityGroups []string
AllowedAddressPairs []AddressPair
}
// ToPortUpdateMap casts an UpdateOpts struct to a map.
@ -198,6 +203,9 @@ func (opts UpdateOpts) ToPortUpdateMap() (map[string]interface{}, error) {
if opts.Name != "" {
p["name"] = opts.Name
}
if opts.AllowedAddressPairs != nil {
p["allowed_address_pairs"] = opts.AllowedAddressPairs
}
return map[string]interface{}{"port": p}, nil
}

View File

@ -19,7 +19,6 @@ func (r commonResult) Extract() (*Port, error) {
var res struct {
Port *Port `json:"port"`
}
err := mapstructure.Decode(r.Body, &res)
return res.Port, err
@ -51,6 +50,11 @@ type IP struct {
IPAddress string `mapstructure:"ip_address" json:"ip_address,omitempty"`
}
type AddressPair struct {
IPAddress string `mapstructure:"ip_address" json:"ip_address,omitempty"`
MACAddress string `mapstructure:"mac_address" json:"mac_address,omitempty"`
}
// Port represents a Neutron port. See package documentation for a top-level
// description of what this is.
type Port struct {
@ -78,6 +82,8 @@ type Port struct {
SecurityGroups []string `mapstructure:"security_groups" json:"security_groups"`
// Identifies the device (e.g., virtual server) using this port.
DeviceID string `mapstructure:"device_id" json:"device_id"`
// Identifies the list of IP addresses the port will recognize/accept
AllowedAddressPairs []AddressPair `mapstructure:"allowed_address_pairs" json:"allowed_address_pairs"`
}
// PortPage is the page returned by a pager when traversing over a collection

View File

@ -177,6 +177,9 @@ func (client *ProviderClient) Request(method, url string, options RequestOpts) (
}
}
// Set connection parameter to close the connection immediately when we've got the response
req.Close = true
// Issue the request.
resp, err := client.HTTPClient.Do(req)
if err != nil {
@ -246,6 +249,8 @@ func defaultOkCodes(method string) []int {
return []int{201, 202}
case method == "PUT":
return []int{201, 202}
case method == "PATCH":
return []int{200, 204}
case method == "DELETE":
return []int{202, 204}
}
@ -299,6 +304,24 @@ func (client *ProviderClient) Put(url string, JSONBody interface{}, JSONResponse
return client.Request("PUT", url, *opts)
}
func (client *ProviderClient) Patch(url string, JSONBody interface{}, JSONResponse *interface{}, opts *RequestOpts) (*http.Response, error) {
if opts == nil {
opts = &RequestOpts{}
}
if v, ok := (JSONBody).(io.ReadSeeker); ok {
opts.RawBody = v
} else if JSONBody != nil {
opts.JSONBody = JSONBody
}
if JSONResponse != nil {
opts.JSONResponse = JSONResponse
}
return client.Request("PATCH", url, *opts)
}
func (client *ProviderClient) Delete(url string, opts *RequestOpts) (*http.Response, error) {
if opts == nil {
opts = &RequestOpts{}

View File

@ -249,3 +249,38 @@ func ListEvents(client *gophercloud.ServiceClient, loadBalancerID int, opts List
return NodeEventPage{pagination.SinglePageBase(r)}
})
}
// GetByIPPort locates a load balancer node by IP and port.
func GetByIPPort(
client *gophercloud.ServiceClient,
loadBalancerID int,
address string,
port int,
) (*Node, error) {
// nil until found
var found *Node
List(client, loadBalancerID, nil).EachPage(func(page pagination.Page) (bool, error) {
lbNodes, err := ExtractNodes(page)
if err != nil {
return false, err
}
for _, trialNode := range lbNodes {
if trialNode.Address == address && trialNode.Port == port {
found = &trialNode
return false, nil
}
}
return true, nil
})
if found == nil {
return nil, errors.New("Unable to get node by IP and Port")
}
return found, nil
}