forked from barak/tarpoon
Add glide.yaml and vendor deps
This commit is contained in:
parent
db918f12ad
commit
5b3d5e81bd
18880 changed files with 5166045 additions and 1 deletions
72
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/BUILD
generated
vendored
Normal file
72
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_binary",
|
||||
"go_library",
|
||||
"go_test",
|
||||
"cgo_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"metadata.go",
|
||||
"openstack.go",
|
||||
"openstack_instances.go",
|
||||
"openstack_loadbalancer.go",
|
||||
"openstack_volumes.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api/resource:go_default_library",
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/api/v1/service:go_default_library",
|
||||
"//pkg/cloudprovider:go_default_library",
|
||||
"//pkg/types:go_default_library",
|
||||
"//pkg/util/exec:go_default_library",
|
||||
"//pkg/util/mount:go_default_library",
|
||||
"//pkg/volume:go_default_library",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:github.com/rackspace/gophercloud",
|
||||
"//vendor:github.com/rackspace/gophercloud/openstack",
|
||||
"//vendor:github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes",
|
||||
"//vendor:github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach",
|
||||
"//vendor:github.com/rackspace/gophercloud/openstack/compute/v2/flavors",
|
||||
"//vendor:github.com/rackspace/gophercloud/openstack/compute/v2/servers",
|
||||
"//vendor:github.com/rackspace/gophercloud/openstack/identity/v3/extensions/trust",
|
||||
"//vendor:github.com/rackspace/gophercloud/openstack/identity/v3/tokens",
|
||||
"//vendor:github.com/rackspace/gophercloud/openstack/networking/v2/extensions",
|
||||
"//vendor:github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips",
|
||||
"//vendor:github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members",
|
||||
"//vendor:github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors",
|
||||
"//vendor:github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools",
|
||||
"//vendor:github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips",
|
||||
"//vendor:github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners",
|
||||
"//vendor:github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers",
|
||||
"//vendor:github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors",
|
||||
"//vendor:github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools",
|
||||
"//vendor:github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups",
|
||||
"//vendor:github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules",
|
||||
"//vendor:github.com/rackspace/gophercloud/openstack/networking/v2/ports",
|
||||
"//vendor:github.com/rackspace/gophercloud/pagination",
|
||||
"//vendor:gopkg.in/gcfg.v1",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"metadata_test.go",
|
||||
"openstack_test.go",
|
||||
],
|
||||
library = "go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/util/rand:go_default_library",
|
||||
"//vendor:github.com/rackspace/gophercloud",
|
||||
],
|
||||
)
|
||||
6
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/MAINTAINERS.md
generated
vendored
Normal file
6
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/MAINTAINERS.md
generated
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# Maintainers
|
||||
|
||||
* [Angus Lees](https://github.com/anguslees)
|
||||
|
||||
|
||||
[]()
|
||||
3
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/OWNERS
generated
vendored
Normal file
3
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/OWNERS
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
assignees:
|
||||
- anguslees
|
||||
- dagnello
|
||||
156
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/metadata.go
generated
vendored
Normal file
156
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/metadata.go
generated
vendored
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package openstack
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/util/exec"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
)
|
||||
|
||||
// metadataUrl is URL to OpenStack metadata server. It's hardcoded IPv4
|
||||
// link-local address as documented in "OpenStack Cloud Administrator Guide",
|
||||
// chapter Compute - Networking with nova-network.
|
||||
// http://docs.openstack.org/admin-guide-cloud/compute-networking-nova.html#metadata-service
|
||||
const metadataUrl = "http://169.254.169.254/openstack/2012-08-10/meta_data.json"
|
||||
|
||||
// Config drive is defined as an iso9660 or vfat (deprecated) drive
|
||||
// with the "config-2" label.
|
||||
// http://docs.openstack.org/user-guide/cli-config-drive.html
|
||||
const configDriveLabel = "config-2"
|
||||
const configDrivePath = "openstack/2012-08-10/meta_data.json"
|
||||
|
||||
var ErrBadMetadata = errors.New("Invalid OpenStack metadata, got empty uuid")
|
||||
|
||||
// Assumes the "2012-08-10" meta_data.json format.
|
||||
// See http://docs.openstack.org/user-guide/cli_config_drive.html
|
||||
type Metadata struct {
|
||||
Uuid string `json:"uuid"`
|
||||
Name string `json:"name"`
|
||||
AvailabilityZone string `json:"availability_zone"`
|
||||
// .. and other fields we don't care about. Expand as necessary.
|
||||
}
|
||||
|
||||
// parseMetadataUUID reads JSON from OpenStack metadata server and parses
|
||||
// instance ID out of it.
|
||||
func parseMetadata(r io.Reader) (*Metadata, error) {
|
||||
var metadata Metadata
|
||||
json := json.NewDecoder(r)
|
||||
if err := json.Decode(&metadata); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if metadata.Uuid == "" {
|
||||
return nil, ErrBadMetadata
|
||||
}
|
||||
|
||||
return &metadata, nil
|
||||
}
|
||||
|
||||
func getMetadataFromConfigDrive() (*Metadata, error) {
|
||||
// Try to read instance UUID from config drive.
|
||||
dev := "/dev/disk/by-label/" + configDriveLabel
|
||||
if _, err := os.Stat(dev); os.IsNotExist(err) {
|
||||
out, err := exec.New().Command(
|
||||
"blkid", "-l",
|
||||
"-t", "LABEL="+configDriveLabel,
|
||||
"-o", "device",
|
||||
).CombinedOutput()
|
||||
if err != nil {
|
||||
glog.V(2).Infof("Unable to run blkid: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
dev = strings.TrimSpace(string(out))
|
||||
}
|
||||
|
||||
mntdir, err := ioutil.TempDir("", "configdrive")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer os.Remove(mntdir)
|
||||
|
||||
glog.V(4).Infof("Attempting to mount configdrive %s on %s", dev, mntdir)
|
||||
|
||||
mounter := mount.New("" /* default mount path */)
|
||||
err = mounter.Mount(dev, mntdir, "iso9660", []string{"ro"})
|
||||
if err != nil {
|
||||
err = mounter.Mount(dev, mntdir, "vfat", []string{"ro"})
|
||||
}
|
||||
if err != nil {
|
||||
glog.Errorf("Error mounting configdrive %s: %v", dev, err)
|
||||
return nil, err
|
||||
}
|
||||
defer mounter.Unmount(mntdir)
|
||||
|
||||
glog.V(4).Infof("Configdrive mounted on %s", mntdir)
|
||||
|
||||
f, err := os.Open(
|
||||
filepath.Join(mntdir, configDrivePath))
|
||||
if err != nil {
|
||||
glog.Errorf("Error reading %s on config drive: %v", configDrivePath, err)
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return parseMetadata(f)
|
||||
}
|
||||
|
||||
func getMetadataFromMetadataService() (*Metadata, error) {
|
||||
// Try to get JSON from metdata server.
|
||||
glog.V(4).Infof("Attempting to fetch metadata from %s", metadataUrl)
|
||||
resp, err := http.Get(metadataUrl)
|
||||
if err != nil {
|
||||
glog.V(3).Infof("Cannot read %s: %v", metadataUrl, err)
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
err = fmt.Errorf("Unexpected status code when reading metadata from %s: %s", metadataUrl, resp.Status)
|
||||
glog.V(3).Infof("%v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return parseMetadata(resp.Body)
|
||||
}
|
||||
|
||||
// Metadata is fixed for the current host, so cache the value process-wide
|
||||
var metadataCache *Metadata
|
||||
|
||||
func getMetadata() (*Metadata, error) {
|
||||
if metadataCache == nil {
|
||||
md, err := getMetadataFromConfigDrive()
|
||||
if err != nil {
|
||||
md, err = getMetadataFromMetadataService()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
metadataCache = md
|
||||
}
|
||||
return metadataCache, nil
|
||||
}
|
||||
86
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/metadata_test.go
generated
vendored
Normal file
86
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/metadata_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package openstack
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var FakeMetadata = Metadata{
|
||||
Uuid: "83679162-1378-4288-a2d4-70e13ec132aa",
|
||||
Name: "test",
|
||||
AvailabilityZone: "nova",
|
||||
}
|
||||
|
||||
func SetMetadataFixture(value *Metadata) {
|
||||
metadataCache = value
|
||||
}
|
||||
|
||||
func ClearMetadata() {
|
||||
metadataCache = nil
|
||||
}
|
||||
|
||||
func TestParseMetadata(t *testing.T) {
|
||||
_, err := parseMetadata(strings.NewReader("bogus"))
|
||||
if err == nil {
|
||||
t.Errorf("Should fail when bad data is provided: %s", err)
|
||||
}
|
||||
|
||||
data := strings.NewReader(`
|
||||
{
|
||||
"availability_zone": "nova",
|
||||
"files": [
|
||||
{
|
||||
"content_path": "/content/0000",
|
||||
"path": "/etc/network/interfaces"
|
||||
},
|
||||
{
|
||||
"content_path": "/content/0001",
|
||||
"path": "known_hosts"
|
||||
}
|
||||
],
|
||||
"hostname": "test.novalocal",
|
||||
"launch_index": 0,
|
||||
"name": "test",
|
||||
"meta": {
|
||||
"role": "webservers",
|
||||
"essential": "false"
|
||||
},
|
||||
"public_keys": {
|
||||
"mykey": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDBqUfVvCSez0/Wfpd8dLLgZXV9GtXQ7hnMN+Z0OWQUyebVEHey1CXuin0uY1cAJMhUq8j98SiW+cU0sU4J3x5l2+xi1bodDm1BtFWVeLIOQINpfV1n8fKjHB+ynPpe1F6tMDvrFGUlJs44t30BrujMXBe8Rq44cCk6wqyjATA3rQ== Generated by Nova\n"
|
||||
},
|
||||
"uuid": "83679162-1378-4288-a2d4-70e13ec132aa"
|
||||
}
|
||||
`)
|
||||
md, err := parseMetadata(data)
|
||||
if err != nil {
|
||||
t.Fatalf("Should succeed when provided with valid data: %s", err)
|
||||
}
|
||||
|
||||
if md.Name != "test" {
|
||||
t.Errorf("incorrect name: %s", md.Name)
|
||||
}
|
||||
|
||||
if md.Uuid != "83679162-1378-4288-a2d4-70e13ec132aa" {
|
||||
t.Errorf("incorrect uuid: %s", md.Uuid)
|
||||
}
|
||||
|
||||
if md.AvailabilityZone != "nova" {
|
||||
t.Errorf("incorrect az: %s", md.AvailabilityZone)
|
||||
}
|
||||
}
|
||||
443
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/openstack.go
generated
vendored
Normal file
443
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/openstack.go
generated
vendored
Normal file
|
|
@ -0,0 +1,443 @@
|
|||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package openstack
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gopkg.in/gcfg.v1"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/openstack"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
"github.com/rackspace/gophercloud/openstack/identity/v3/extensions/trust"
|
||||
token3 "github.com/rackspace/gophercloud/openstack/identity/v3/tokens"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
)
|
||||
|
||||
const ProviderName = "openstack"
|
||||
|
||||
var ErrNotFound = errors.New("Failed to find object")
|
||||
var ErrMultipleResults = errors.New("Multiple results where only one expected")
|
||||
var ErrNoAddressFound = errors.New("No address found for host")
|
||||
var ErrAttrNotFound = errors.New("Expected attribute not found")
|
||||
|
||||
const (
|
||||
MiB = 1024 * 1024
|
||||
GB = 1000 * 1000 * 1000
|
||||
)
|
||||
|
||||
// encoding.TextUnmarshaler interface for time.Duration
|
||||
type MyDuration struct {
|
||||
time.Duration
|
||||
}
|
||||
|
||||
func (d *MyDuration) UnmarshalText(text []byte) error {
|
||||
res, err := time.ParseDuration(string(text))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.Duration = res
|
||||
return nil
|
||||
}
|
||||
|
||||
type LoadBalancer struct {
|
||||
network *gophercloud.ServiceClient
|
||||
compute *gophercloud.ServiceClient
|
||||
opts LoadBalancerOpts
|
||||
}
|
||||
|
||||
type LoadBalancerOpts struct {
|
||||
LBVersion string `gcfg:"lb-version"` // overrides autodetection. v1 or v2
|
||||
SubnetId string `gcfg:"subnet-id"` // required
|
||||
FloatingNetworkId string `gcfg:"floating-network-id"`
|
||||
LBMethod string `gcfg:"lb-method"`
|
||||
CreateMonitor bool `gcfg:"create-monitor"`
|
||||
MonitorDelay MyDuration `gcfg:"monitor-delay"`
|
||||
MonitorTimeout MyDuration `gcfg:"monitor-timeout"`
|
||||
MonitorMaxRetries uint `gcfg:"monitor-max-retries"`
|
||||
ManageSecurityGroups bool `gcfg:"manage-security-groups"`
|
||||
NodeSecurityGroupID string `gcfg:"node-security-group"`
|
||||
}
|
||||
|
||||
type BlockStorageOpts struct {
|
||||
TrustDevicePath bool `gcfg:"trust-device-path"` // See Issue #33128
|
||||
}
|
||||
|
||||
// OpenStack is an implementation of cloud provider Interface for OpenStack.
|
||||
type OpenStack struct {
|
||||
provider *gophercloud.ProviderClient
|
||||
region string
|
||||
lbOpts LoadBalancerOpts
|
||||
bsOpts BlockStorageOpts
|
||||
// InstanceID of the server where this OpenStack object is instantiated.
|
||||
localInstanceID string
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Global struct {
|
||||
AuthUrl string `gcfg:"auth-url"`
|
||||
Username string
|
||||
UserId string `gcfg:"user-id"`
|
||||
Password string
|
||||
ApiKey string `gcfg:"api-key"`
|
||||
TenantId string `gcfg:"tenant-id"`
|
||||
TenantName string `gcfg:"tenant-name"`
|
||||
TrustId string `gcfg:"trust-id"`
|
||||
DomainId string `gcfg:"domain-id"`
|
||||
DomainName string `gcfg:"domain-name"`
|
||||
Region string
|
||||
}
|
||||
LoadBalancer LoadBalancerOpts
|
||||
BlockStorage BlockStorageOpts
|
||||
}
|
||||
|
||||
func init() {
|
||||
cloudprovider.RegisterCloudProvider(ProviderName, func(config io.Reader) (cloudprovider.Interface, error) {
|
||||
cfg, err := readConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newOpenStack(cfg)
|
||||
})
|
||||
}
|
||||
|
||||
func (cfg Config) toAuthOptions() gophercloud.AuthOptions {
|
||||
return gophercloud.AuthOptions{
|
||||
IdentityEndpoint: cfg.Global.AuthUrl,
|
||||
Username: cfg.Global.Username,
|
||||
UserID: cfg.Global.UserId,
|
||||
Password: cfg.Global.Password,
|
||||
APIKey: cfg.Global.ApiKey,
|
||||
TenantID: cfg.Global.TenantId,
|
||||
TenantName: cfg.Global.TenantName,
|
||||
DomainID: cfg.Global.DomainId,
|
||||
DomainName: cfg.Global.DomainName,
|
||||
|
||||
// Persistent service, so we need to be able to renew tokens.
|
||||
AllowReauth: true,
|
||||
}
|
||||
}
|
||||
|
||||
func readConfig(config io.Reader) (Config, error) {
|
||||
if config == nil {
|
||||
err := fmt.Errorf("no OpenStack cloud provider config file given")
|
||||
return Config{}, err
|
||||
}
|
||||
|
||||
var cfg Config
|
||||
|
||||
// Set default values for config params
|
||||
cfg.BlockStorage.TrustDevicePath = false
|
||||
|
||||
err := gcfg.ReadInto(&cfg, config)
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
func readInstanceID() (string, error) {
|
||||
// Try to find instance ID on the local filesystem (created by cloud-init)
|
||||
const instanceIDFile = "/var/lib/cloud/data/instance-id"
|
||||
idBytes, err := ioutil.ReadFile(instanceIDFile)
|
||||
if err == nil {
|
||||
instanceID := string(idBytes)
|
||||
instanceID = strings.TrimSpace(instanceID)
|
||||
glog.V(3).Infof("Got instance id from %s: %s", instanceIDFile, instanceID)
|
||||
if instanceID != "" {
|
||||
return instanceID, nil
|
||||
}
|
||||
// Fall through to metadata server lookup
|
||||
}
|
||||
|
||||
md, err := getMetadata()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return md.Uuid, nil
|
||||
}
|
||||
|
||||
func newOpenStack(cfg Config) (*OpenStack, error) {
|
||||
provider, err := openstack.NewClient(cfg.Global.AuthUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cfg.Global.TrustId != "" {
|
||||
authOptionsExt := trust.AuthOptionsExt{
|
||||
TrustID: cfg.Global.TrustId,
|
||||
AuthOptions: token3.AuthOptions{AuthOptions: cfg.toAuthOptions()},
|
||||
}
|
||||
err = trust.AuthenticateV3Trust(provider, authOptionsExt)
|
||||
} else {
|
||||
err = openstack.Authenticate(provider, cfg.toAuthOptions())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, err := readInstanceID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
os := OpenStack{
|
||||
provider: provider,
|
||||
region: cfg.Global.Region,
|
||||
lbOpts: cfg.LoadBalancer,
|
||||
bsOpts: cfg.BlockStorage,
|
||||
localInstanceID: id,
|
||||
}
|
||||
|
||||
return &os, nil
|
||||
}
|
||||
|
||||
// mapNodeNameToServerName maps a k8s NodeName to an OpenStack Server Name
|
||||
// This is a simple string cast.
|
||||
func mapNodeNameToServerName(nodeName types.NodeName) string {
|
||||
return string(nodeName)
|
||||
}
|
||||
|
||||
// mapServerToNodeName maps an OpenStack Server to a k8s NodeName
|
||||
func mapServerToNodeName(server *servers.Server) types.NodeName {
|
||||
return types.NodeName(server.Name)
|
||||
}
|
||||
|
||||
func getServerByName(client *gophercloud.ServiceClient, name types.NodeName) (*servers.Server, error) {
|
||||
opts := servers.ListOpts{
|
||||
Name: fmt.Sprintf("^%s$", regexp.QuoteMeta(mapNodeNameToServerName(name))),
|
||||
Status: "ACTIVE",
|
||||
}
|
||||
pager := servers.List(client, opts)
|
||||
|
||||
serverList := make([]servers.Server, 0, 1)
|
||||
|
||||
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
s, err := servers.ExtractServers(page)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
serverList = append(serverList, s...)
|
||||
if len(serverList) > 1 {
|
||||
return false, ErrMultipleResults
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(serverList) == 0 {
|
||||
return nil, ErrNotFound
|
||||
} else if len(serverList) > 1 {
|
||||
return nil, ErrMultipleResults
|
||||
}
|
||||
|
||||
return &serverList[0], nil
|
||||
}
|
||||
|
||||
func getAddressesByName(client *gophercloud.ServiceClient, name types.NodeName) ([]v1.NodeAddress, error) {
|
||||
srv, err := getServerByName(client, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addrs := []v1.NodeAddress{}
|
||||
|
||||
for network, netblob := range srv.Addresses {
|
||||
list, ok := netblob.([]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
var addressType v1.NodeAddressType
|
||||
|
||||
props, ok := item.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
extIPType, ok := props["OS-EXT-IPS:type"]
|
||||
if (ok && extIPType == "floating") || (!ok && network == "public") {
|
||||
addressType = v1.NodeExternalIP
|
||||
} else {
|
||||
addressType = v1.NodeInternalIP
|
||||
}
|
||||
|
||||
tmp, ok := props["addr"]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
addr, ok := tmp.(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
v1.AddToNodeAddresses(&addrs,
|
||||
v1.NodeAddress{
|
||||
Type: addressType,
|
||||
Address: addr,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// AccessIPs are usually duplicates of "public" addresses.
|
||||
if srv.AccessIPv4 != "" {
|
||||
v1.AddToNodeAddresses(&addrs,
|
||||
v1.NodeAddress{
|
||||
Type: v1.NodeExternalIP,
|
||||
Address: srv.AccessIPv4,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if srv.AccessIPv6 != "" {
|
||||
v1.AddToNodeAddresses(&addrs,
|
||||
v1.NodeAddress{
|
||||
Type: v1.NodeExternalIP,
|
||||
Address: srv.AccessIPv6,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return addrs, nil
|
||||
}
|
||||
|
||||
func getAddressByName(client *gophercloud.ServiceClient, name types.NodeName) (string, error) {
|
||||
addrs, err := getAddressesByName(client, name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else if len(addrs) == 0 {
|
||||
return "", ErrNoAddressFound
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
if addr.Type == v1.NodeInternalIP {
|
||||
return addr.Address, nil
|
||||
}
|
||||
}
|
||||
|
||||
return addrs[0].Address, nil
|
||||
}
|
||||
|
||||
func (os *OpenStack) Clusters() (cloudprovider.Clusters, bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// ProviderName returns the cloud provider ID.
|
||||
func (os *OpenStack) ProviderName() string {
|
||||
return ProviderName
|
||||
}
|
||||
|
||||
// ScrubDNS filters DNS settings for pods.
|
||||
func (os *OpenStack) ScrubDNS(nameservers, searches []string) (nsOut, srchOut []string) {
|
||||
return nameservers, searches
|
||||
}
|
||||
|
||||
func (os *OpenStack) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
|
||||
glog.V(4).Info("openstack.LoadBalancer() called")
|
||||
|
||||
// TODO: Search for and support Rackspace loadbalancer API, and others.
|
||||
network, err := openstack.NewNetworkV2(os.provider, gophercloud.EndpointOpts{
|
||||
Region: os.region,
|
||||
})
|
||||
if err != nil {
|
||||
glog.Warningf("Failed to find neutron endpoint: %v", err)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
compute, err := openstack.NewComputeV2(os.provider, gophercloud.EndpointOpts{
|
||||
Region: os.region,
|
||||
})
|
||||
if err != nil {
|
||||
glog.Warningf("Failed to find compute endpoint: %v", err)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
lbversion := os.lbOpts.LBVersion
|
||||
if lbversion == "" {
|
||||
// No version specified, try newest supported by server
|
||||
netExts, err := networkExtensions(network)
|
||||
if err != nil {
|
||||
glog.Warningf("Failed to list neutron extensions: %v", err)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if netExts["lbaasv2"] {
|
||||
lbversion = "v2"
|
||||
} else if netExts["lbaas"] {
|
||||
lbversion = "v1"
|
||||
} else {
|
||||
glog.Warningf("Failed to find neutron LBaaS extension (v1 or v2)")
|
||||
return nil, false
|
||||
}
|
||||
glog.V(3).Infof("Using LBaaS extension %v", lbversion)
|
||||
}
|
||||
|
||||
glog.V(1).Info("Claiming to support LoadBalancer")
|
||||
|
||||
if lbversion == "v2" {
|
||||
return &LbaasV2{LoadBalancer{network, compute, os.lbOpts}}, true
|
||||
} else if lbversion == "v1" {
|
||||
return &LbaasV1{LoadBalancer{network, compute, os.lbOpts}}, true
|
||||
} else {
|
||||
glog.Warningf("Config error: unrecognised lb-version \"%v\"", lbversion)
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
func isNotFound(err error) bool {
|
||||
e, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
||||
return ok && e.Actual == http.StatusNotFound
|
||||
}
|
||||
|
||||
func (os *OpenStack) Zones() (cloudprovider.Zones, bool) {
|
||||
glog.V(1).Info("Claiming to support Zones")
|
||||
|
||||
return os, true
|
||||
}
|
||||
func (os *OpenStack) GetZone() (cloudprovider.Zone, error) {
|
||||
md, err := getMetadata()
|
||||
if err != nil {
|
||||
return cloudprovider.Zone{}, err
|
||||
}
|
||||
|
||||
zone := cloudprovider.Zone{
|
||||
FailureDomain: md.AvailabilityZone,
|
||||
Region: os.region,
|
||||
}
|
||||
glog.V(1).Infof("Current zone is %v", zone)
|
||||
|
||||
return zone, nil
|
||||
}
|
||||
|
||||
func (os *OpenStack) Routes() (cloudprovider.Routes, bool) {
|
||||
return nil, false
|
||||
}
|
||||
172
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/openstack_instances.go
generated
vendored
Normal file
172
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/openstack_instances.go
generated
vendored
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package openstack
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/openstack"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/flavors"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/resource"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
)
|
||||
|
||||
type Instances struct {
|
||||
compute *gophercloud.ServiceClient
|
||||
flavor_to_resource map[string]*v1.NodeResources // keyed by flavor id
|
||||
}
|
||||
|
||||
// Instances returns an implementation of Instances for OpenStack.
|
||||
func (os *OpenStack) Instances() (cloudprovider.Instances, bool) {
|
||||
glog.V(4).Info("openstack.Instances() called")
|
||||
|
||||
compute, err := openstack.NewComputeV2(os.provider, gophercloud.EndpointOpts{
|
||||
Region: os.region,
|
||||
})
|
||||
if err != nil {
|
||||
glog.Warningf("Failed to find compute endpoint: %v", err)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
pager := flavors.ListDetail(compute, nil)
|
||||
|
||||
flavor_to_resource := make(map[string]*v1.NodeResources)
|
||||
err = pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
flavorList, err := flavors.ExtractFlavors(page)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, flavor := range flavorList {
|
||||
rsrc := v1.NodeResources{
|
||||
Capacity: v1.ResourceList{
|
||||
v1.ResourceCPU: *resource.NewQuantity(int64(flavor.VCPUs), resource.DecimalSI),
|
||||
v1.ResourceMemory: *resource.NewQuantity(int64(flavor.RAM)*MiB, resource.BinarySI),
|
||||
"openstack.org/disk": *resource.NewQuantity(int64(flavor.Disk)*GB, resource.DecimalSI),
|
||||
"openstack.org/rxTxFactor": *resource.NewMilliQuantity(int64(flavor.RxTxFactor)*1000, resource.DecimalSI),
|
||||
"openstack.org/swap": *resource.NewQuantity(int64(flavor.Swap)*MiB, resource.BinarySI),
|
||||
},
|
||||
}
|
||||
flavor_to_resource[flavor.ID] = &rsrc
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
glog.Warningf("Failed to find compute flavors: %v", err)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
glog.V(3).Infof("Found %v compute flavors", len(flavor_to_resource))
|
||||
glog.V(1).Info("Claiming to support Instances")
|
||||
|
||||
return &Instances{compute, flavor_to_resource}, true
|
||||
}
|
||||
|
||||
func (i *Instances) List(name_filter string) ([]types.NodeName, error) {
|
||||
glog.V(4).Infof("openstack List(%v) called", name_filter)
|
||||
|
||||
opts := servers.ListOpts{
|
||||
Name: name_filter,
|
||||
Status: "ACTIVE",
|
||||
}
|
||||
pager := servers.List(i.compute, opts)
|
||||
|
||||
ret := make([]types.NodeName, 0)
|
||||
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
sList, err := servers.ExtractServers(page)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for i := range sList {
|
||||
ret = append(ret, mapServerToNodeName(&sList[i]))
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
glog.V(3).Infof("Found %v instances matching %v: %v",
|
||||
len(ret), name_filter, ret)
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Implementation of Instances.CurrentNodeName
|
||||
// Note this is *not* necessarily the same as hostname.
|
||||
func (i *Instances) CurrentNodeName(hostname string) (types.NodeName, error) {
|
||||
md, err := getMetadata()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return types.NodeName(md.Name), nil
|
||||
}
|
||||
|
||||
func (i *Instances) AddSSHKeyToAllInstances(user string, keyData []byte) error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
|
||||
func (i *Instances) NodeAddresses(name types.NodeName) ([]v1.NodeAddress, error) {
|
||||
glog.V(4).Infof("NodeAddresses(%v) called", name)
|
||||
|
||||
addrs, err := getAddressesByName(i.compute, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
glog.V(4).Infof("NodeAddresses(%v) => %v", name, addrs)
|
||||
return addrs, nil
|
||||
}
|
||||
|
||||
// ExternalID returns the cloud provider ID of the specified instance (deprecated).
|
||||
func (i *Instances) ExternalID(name types.NodeName) (string, error) {
|
||||
srv, err := getServerByName(i.compute, name)
|
||||
if err != nil {
|
||||
if err == ErrNotFound {
|
||||
return "", cloudprovider.InstanceNotFound
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
return srv.ID, nil
|
||||
}
|
||||
|
||||
// InstanceID returns the kubelet's cloud provider ID.
|
||||
func (os *OpenStack) InstanceID() (string, error) {
|
||||
return os.localInstanceID, nil
|
||||
}
|
||||
|
||||
// InstanceID returns the cloud provider ID of the specified instance.
|
||||
func (i *Instances) InstanceID(name types.NodeName) (string, error) {
|
||||
srv, err := getServerByName(i.compute, name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// In the future it is possible to also return an endpoint as:
|
||||
// <endpoint>/<instanceid>
|
||||
return "/" + srv.ID, nil
|
||||
}
|
||||
|
||||
// InstanceType returns the type of the specified instance.
|
||||
func (i *Instances) InstanceType(name types.NodeName) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
1564
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/openstack_loadbalancer.go
generated
vendored
Normal file
1564
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/openstack_loadbalancer.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
319
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/openstack_test.go
generated
vendored
Normal file
319
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/openstack_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package openstack
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/rand"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
)
|
||||
|
||||
const volumeAvailableStatus = "available"
|
||||
const volumeInUseStatus = "in-use"
|
||||
const volumeCreateTimeoutSeconds = 30
|
||||
const testClusterName = "testCluster"
|
||||
|
||||
func WaitForVolumeStatus(t *testing.T, os *OpenStack, volumeName string, status string, timeoutSeconds int) {
|
||||
timeout := timeoutSeconds
|
||||
start := time.Now().Second()
|
||||
for {
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
if timeout >= 0 && time.Now().Second()-start >= timeout {
|
||||
t.Logf("Volume (%s) status did not change to %s after %v seconds\n",
|
||||
volumeName,
|
||||
status,
|
||||
timeout)
|
||||
return
|
||||
}
|
||||
|
||||
getVol, err := os.getVolume(volumeName)
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot get existing Cinder volume (%s): %v", volumeName, err)
|
||||
}
|
||||
if getVol.Status == status {
|
||||
t.Logf("Volume (%s) status changed to %s after %v seconds\n",
|
||||
volumeName,
|
||||
status,
|
||||
timeout)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadConfig(t *testing.T) {
|
||||
_, err := readConfig(nil)
|
||||
if err == nil {
|
||||
t.Errorf("Should fail when no config is provided: %s", err)
|
||||
}
|
||||
|
||||
cfg, err := readConfig(strings.NewReader(`
|
||||
[Global]
|
||||
auth-url = http://auth.url
|
||||
username = user
|
||||
[LoadBalancer]
|
||||
create-monitor = yes
|
||||
monitor-delay = 1m
|
||||
monitor-timeout = 30s
|
||||
monitor-max-retries = 3
|
||||
[BlockStorage]
|
||||
trust-device-path = yes
|
||||
`))
|
||||
if err != nil {
|
||||
t.Fatalf("Should succeed when a valid config is provided: %s", err)
|
||||
}
|
||||
if cfg.Global.AuthUrl != "http://auth.url" {
|
||||
t.Errorf("incorrect authurl: %s", cfg.Global.AuthUrl)
|
||||
}
|
||||
|
||||
if !cfg.LoadBalancer.CreateMonitor {
|
||||
t.Errorf("incorrect lb.createmonitor: %t", cfg.LoadBalancer.CreateMonitor)
|
||||
}
|
||||
if cfg.LoadBalancer.MonitorDelay.Duration != 1*time.Minute {
|
||||
t.Errorf("incorrect lb.monitordelay: %s", cfg.LoadBalancer.MonitorDelay)
|
||||
}
|
||||
if cfg.LoadBalancer.MonitorTimeout.Duration != 30*time.Second {
|
||||
t.Errorf("incorrect lb.monitortimeout: %s", cfg.LoadBalancer.MonitorTimeout)
|
||||
}
|
||||
if cfg.LoadBalancer.MonitorMaxRetries != 3 {
|
||||
t.Errorf("incorrect lb.monitormaxretries: %d", cfg.LoadBalancer.MonitorMaxRetries)
|
||||
}
|
||||
if cfg.BlockStorage.TrustDevicePath != true {
|
||||
t.Errorf("incorrect bs.trustdevicepath: %v", cfg.BlockStorage.TrustDevicePath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToAuthOptions(t *testing.T) {
|
||||
cfg := Config{}
|
||||
cfg.Global.Username = "user"
|
||||
// etc.
|
||||
|
||||
ao := cfg.toAuthOptions()
|
||||
|
||||
if !ao.AllowReauth {
|
||||
t.Errorf("Will need to be able to reauthenticate")
|
||||
}
|
||||
if ao.Username != cfg.Global.Username {
|
||||
t.Errorf("Username %s != %s", ao.Username, cfg.Global.Username)
|
||||
}
|
||||
}
|
||||
|
||||
// This allows acceptance testing against an existing OpenStack
|
||||
// install, using the standard OS_* OpenStack client environment
|
||||
// variables.
|
||||
// FIXME: it would be better to hermetically test against canned JSON
|
||||
// requests/responses.
|
||||
func configFromEnv() (cfg Config, ok bool) {
|
||||
cfg.Global.AuthUrl = os.Getenv("OS_AUTH_URL")
|
||||
|
||||
cfg.Global.TenantId = os.Getenv("OS_TENANT_ID")
|
||||
// Rax/nova _insists_ that we don't specify both tenant ID and name
|
||||
if cfg.Global.TenantId == "" {
|
||||
cfg.Global.TenantName = os.Getenv("OS_TENANT_NAME")
|
||||
}
|
||||
|
||||
cfg.Global.Username = os.Getenv("OS_USERNAME")
|
||||
cfg.Global.Password = os.Getenv("OS_PASSWORD")
|
||||
cfg.Global.ApiKey = os.Getenv("OS_API_KEY")
|
||||
cfg.Global.Region = os.Getenv("OS_REGION_NAME")
|
||||
cfg.Global.DomainId = os.Getenv("OS_DOMAIN_ID")
|
||||
cfg.Global.DomainName = os.Getenv("OS_DOMAIN_NAME")
|
||||
|
||||
ok = (cfg.Global.AuthUrl != "" &&
|
||||
cfg.Global.Username != "" &&
|
||||
(cfg.Global.Password != "" || cfg.Global.ApiKey != "") &&
|
||||
(cfg.Global.TenantId != "" || cfg.Global.TenantName != "" ||
|
||||
cfg.Global.DomainId != "" || cfg.Global.DomainName != ""))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func TestNewOpenStack(t *testing.T) {
|
||||
cfg, ok := configFromEnv()
|
||||
if !ok {
|
||||
t.Skipf("No config found in environment")
|
||||
}
|
||||
|
||||
_, err := newOpenStack(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to construct/authenticate OpenStack: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstances(t *testing.T) {
|
||||
cfg, ok := configFromEnv()
|
||||
if !ok {
|
||||
t.Skipf("No config found in environment")
|
||||
}
|
||||
|
||||
os, err := newOpenStack(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to construct/authenticate OpenStack: %s", err)
|
||||
}
|
||||
|
||||
i, ok := os.Instances()
|
||||
if !ok {
|
||||
t.Fatalf("Instances() returned false")
|
||||
}
|
||||
|
||||
srvs, err := i.List(".")
|
||||
if err != nil {
|
||||
t.Fatalf("Instances.List() failed: %s", err)
|
||||
}
|
||||
if len(srvs) == 0 {
|
||||
t.Fatalf("Instances.List() returned zero servers")
|
||||
}
|
||||
t.Logf("Found servers (%d): %s\n", len(srvs), srvs)
|
||||
|
||||
srvExternalId, err := i.ExternalID(srvs[0])
|
||||
if err != nil {
|
||||
t.Fatalf("Instances.ExternalId(%s) failed: %s", srvs[0], err)
|
||||
}
|
||||
t.Logf("Found server (%s), with external id: %s\n", srvs[0], srvExternalId)
|
||||
|
||||
srvInstanceId, err := i.InstanceID(srvs[0])
|
||||
if err != nil {
|
||||
t.Fatalf("Instance.InstanceId(%s) failed: %s", srvs[0], err)
|
||||
}
|
||||
t.Logf("Found server (%s), with instance id: %s\n", srvs[0], srvInstanceId)
|
||||
|
||||
addrs, err := i.NodeAddresses(srvs[0])
|
||||
if err != nil {
|
||||
t.Fatalf("Instances.NodeAddresses(%s) failed: %s", srvs[0], err)
|
||||
}
|
||||
t.Logf("Found NodeAddresses(%s) = %s\n", srvs[0], addrs)
|
||||
}
|
||||
|
||||
func TestLoadBalancer(t *testing.T) {
|
||||
cfg, ok := configFromEnv()
|
||||
if !ok {
|
||||
t.Skipf("No config found in environment")
|
||||
}
|
||||
|
||||
versions := []string{"v1", "v2", ""}
|
||||
|
||||
for _, v := range versions {
|
||||
t.Logf("Trying LBVersion = '%s'\n", v)
|
||||
cfg.LoadBalancer.LBVersion = v
|
||||
|
||||
os, err := newOpenStack(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to construct/authenticate OpenStack: %s", err)
|
||||
}
|
||||
|
||||
lb, ok := os.LoadBalancer()
|
||||
if !ok {
|
||||
t.Fatalf("LoadBalancer() returned false - perhaps your stack doesn't support Neutron?")
|
||||
}
|
||||
|
||||
_, exists, err := lb.GetLoadBalancer(testClusterName, &v1.Service{ObjectMeta: v1.ObjectMeta{Name: "noexist"}})
|
||||
if err != nil {
|
||||
t.Fatalf("GetLoadBalancer(\"noexist\") returned error: %s", err)
|
||||
}
|
||||
if exists {
|
||||
t.Fatalf("GetLoadBalancer(\"noexist\") returned exists")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestZones(t *testing.T) {
|
||||
SetMetadataFixture(&FakeMetadata)
|
||||
defer ClearMetadata()
|
||||
|
||||
os := OpenStack{
|
||||
provider: &gophercloud.ProviderClient{
|
||||
IdentityBase: "http://auth.url/",
|
||||
},
|
||||
region: "myRegion",
|
||||
}
|
||||
|
||||
z, ok := os.Zones()
|
||||
if !ok {
|
||||
t.Fatalf("Zones() returned false")
|
||||
}
|
||||
|
||||
zone, err := z.GetZone()
|
||||
if err != nil {
|
||||
t.Fatalf("GetZone() returned error: %s", err)
|
||||
}
|
||||
|
||||
if zone.Region != "myRegion" {
|
||||
t.Fatalf("GetZone() returned wrong region (%s)", zone.Region)
|
||||
}
|
||||
|
||||
if zone.FailureDomain != "nova" {
|
||||
t.Fatalf("GetZone() returned wrong failure domain (%s)", zone.FailureDomain)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVolumes(t *testing.T) {
|
||||
cfg, ok := configFromEnv()
|
||||
if !ok {
|
||||
t.Skipf("No config found in environment")
|
||||
}
|
||||
|
||||
os, err := newOpenStack(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to construct/authenticate OpenStack: %s", err)
|
||||
}
|
||||
|
||||
tags := map[string]string{
|
||||
"test": "value",
|
||||
}
|
||||
vol, err := os.CreateVolume("kubernetes-test-volume-"+rand.String(10), 1, "", "", &tags)
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot create a new Cinder volume: %v", err)
|
||||
}
|
||||
t.Logf("Volume (%s) created\n", vol)
|
||||
|
||||
WaitForVolumeStatus(t, os, vol, volumeAvailableStatus, volumeCreateTimeoutSeconds)
|
||||
|
||||
diskId, err := os.AttachDisk(os.localInstanceID, vol)
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot AttachDisk Cinder volume %s: %v", vol, err)
|
||||
}
|
||||
t.Logf("Volume (%s) attached, disk ID: %s\n", vol, diskId)
|
||||
|
||||
WaitForVolumeStatus(t, os, vol, volumeInUseStatus, volumeCreateTimeoutSeconds)
|
||||
|
||||
devicePath := os.GetDevicePath(diskId)
|
||||
if !strings.HasPrefix(devicePath, "/dev/disk/by-id/") {
|
||||
t.Fatalf("GetDevicePath returned and unexpected path for Cinder volume %s, returned %s", vol, devicePath)
|
||||
}
|
||||
t.Logf("Volume (%s) found at path: %s\n", vol, devicePath)
|
||||
|
||||
err = os.DetachDisk(os.localInstanceID, vol)
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot DetachDisk Cinder volume %s: %v", vol, err)
|
||||
}
|
||||
t.Logf("Volume (%s) detached\n", vol)
|
||||
|
||||
WaitForVolumeStatus(t, os, vol, volumeAvailableStatus, volumeCreateTimeoutSeconds)
|
||||
|
||||
err = os.DeleteVolume(vol)
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot delete Cinder volume %s: %v", vol, err)
|
||||
}
|
||||
t.Logf("Volume (%s) deleted\n", vol)
|
||||
|
||||
}
|
||||
287
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/openstack_volumes.go
generated
vendored
Normal file
287
vendor/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/openstack_volumes.go
generated
vendored
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package openstack
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
|
||||
"github.com/rackspace/gophercloud"
|
||||
"github.com/rackspace/gophercloud/openstack"
|
||||
"github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
|
||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach"
|
||||
"github.com/rackspace/gophercloud/pagination"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// Attaches given cinder volume to the compute running kubelet
|
||||
func (os *OpenStack) AttachDisk(instanceID string, diskName string) (string, error) {
|
||||
disk, err := os.getVolume(diskName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
cClient, err := openstack.NewComputeV2(os.provider, gophercloud.EndpointOpts{
|
||||
Region: os.region,
|
||||
})
|
||||
if err != nil || cClient == nil {
|
||||
glog.Errorf("Unable to initialize nova client for region: %s", os.region)
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(disk.Attachments) > 0 && disk.Attachments[0]["server_id"] != nil {
|
||||
if instanceID == disk.Attachments[0]["server_id"] {
|
||||
glog.V(4).Infof("Disk: %q is already attached to compute: %q", diskName, instanceID)
|
||||
return disk.ID, nil
|
||||
} else {
|
||||
errMsg := fmt.Sprintf("Disk %q is attached to a different compute: %q, should be detached before proceeding", diskName, disk.Attachments[0]["server_id"])
|
||||
glog.Errorf(errMsg)
|
||||
return "", errors.New(errMsg)
|
||||
}
|
||||
}
|
||||
// add read only flag here if possible spothanis
|
||||
_, err = volumeattach.Create(cClient, instanceID, &volumeattach.CreateOpts{
|
||||
VolumeID: disk.ID,
|
||||
}).Extract()
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to attach %s volume to %s compute", diskName, instanceID)
|
||||
return "", err
|
||||
}
|
||||
glog.V(2).Infof("Successfully attached %s volume to %s compute", diskName, instanceID)
|
||||
return disk.ID, nil
|
||||
}
|
||||
|
||||
// Detaches given cinder volume from the compute running kubelet
|
||||
func (os *OpenStack) DetachDisk(instanceID string, partialDiskId string) error {
|
||||
disk, err := os.getVolume(partialDiskId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cClient, err := openstack.NewComputeV2(os.provider, gophercloud.EndpointOpts{
|
||||
Region: os.region,
|
||||
})
|
||||
if err != nil || cClient == nil {
|
||||
glog.Errorf("Unable to initialize nova client for region: %s", os.region)
|
||||
return err
|
||||
}
|
||||
if len(disk.Attachments) > 0 && disk.Attachments[0]["server_id"] != nil && instanceID == disk.Attachments[0]["server_id"] {
|
||||
// This is a blocking call and effects kubelet's performance directly.
|
||||
// We should consider kicking it out into a separate routine, if it is bad.
|
||||
err = volumeattach.Delete(cClient, instanceID, disk.ID).ExtractErr()
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to delete volume %s from compute %s attached %v", disk.ID, instanceID, err)
|
||||
return err
|
||||
}
|
||||
glog.V(2).Infof("Successfully detached volume: %s from compute: %s", disk.ID, instanceID)
|
||||
} else {
|
||||
errMsg := fmt.Sprintf("Disk: %s has no attachments or is not attached to compute: %s", disk.Name, instanceID)
|
||||
glog.Errorf(errMsg)
|
||||
return errors.New(errMsg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Takes a partial/full disk id or diskname
|
||||
func (os *OpenStack) getVolume(diskName string) (volumes.Volume, error) {
|
||||
sClient, err := openstack.NewBlockStorageV1(os.provider, gophercloud.EndpointOpts{
|
||||
Region: os.region,
|
||||
})
|
||||
|
||||
var volume volumes.Volume
|
||||
if err != nil || sClient == nil {
|
||||
glog.Errorf("Unable to initialize cinder client for region: %s", os.region)
|
||||
return volume, err
|
||||
}
|
||||
|
||||
err = volumes.List(sClient, nil).EachPage(func(page pagination.Page) (bool, error) {
|
||||
vols, err := volumes.ExtractVolumes(page)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to extract volumes: %v", err)
|
||||
return false, err
|
||||
} else {
|
||||
for _, v := range vols {
|
||||
glog.V(4).Infof("%s %s %v", v.ID, v.Name, v.Attachments)
|
||||
if v.Name == diskName || strings.Contains(v.ID, diskName) {
|
||||
volume = v
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
// if it reached here then no disk with the given name was found.
|
||||
errmsg := fmt.Sprintf("Unable to find disk: %s in region %s", diskName, os.region)
|
||||
return false, errors.New(errmsg)
|
||||
})
|
||||
if err != nil {
|
||||
glog.Errorf("Error occurred getting volume: %s", diskName)
|
||||
return volume, err
|
||||
}
|
||||
return volume, err
|
||||
}
|
||||
|
||||
// Create a volume of given size (in GiB)
|
||||
func (os *OpenStack) CreateVolume(name string, size int, vtype, availability string, tags *map[string]string) (volumeName string, err error) {
|
||||
|
||||
sClient, err := openstack.NewBlockStorageV1(os.provider, gophercloud.EndpointOpts{
|
||||
Region: os.region,
|
||||
})
|
||||
|
||||
if err != nil || sClient == nil {
|
||||
glog.Errorf("Unable to initialize cinder client for region: %s", os.region)
|
||||
return "", err
|
||||
}
|
||||
|
||||
opts := volumes.CreateOpts{
|
||||
Name: name,
|
||||
Size: size,
|
||||
VolumeType: vtype,
|
||||
Availability: availability,
|
||||
}
|
||||
if tags != nil {
|
||||
opts.Metadata = *tags
|
||||
}
|
||||
vol, err := volumes.Create(sClient, opts).Extract()
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to create a %d GB volume: %v", size, err)
|
||||
return "", err
|
||||
}
|
||||
glog.Infof("Created volume %v", vol.ID)
|
||||
return vol.ID, err
|
||||
}
|
||||
|
||||
// GetDevicePath returns the path of an attached block storage volume, specified by its id.
|
||||
func (os *OpenStack) GetDevicePath(diskId string) string {
|
||||
// Build a list of candidate device paths
|
||||
candidateDeviceNodes := []string{
|
||||
// KVM
|
||||
fmt.Sprintf("virtio-%s", diskId[:20]),
|
||||
// ESXi
|
||||
fmt.Sprintf("wwn-0x%s", strings.Replace(diskId, "-", "", -1)),
|
||||
}
|
||||
|
||||
files, _ := ioutil.ReadDir("/dev/disk/by-id/")
|
||||
|
||||
for _, f := range files {
|
||||
for _, c := range candidateDeviceNodes {
|
||||
if c == f.Name() {
|
||||
glog.V(4).Infof("Found disk attached as %q; full devicepath: %s\n", f.Name(), path.Join("/dev/disk/by-id/", f.Name()))
|
||||
return path.Join("/dev/disk/by-id/", f.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glog.Warningf("Failed to find device for the diskid: %q\n", diskId)
|
||||
return ""
|
||||
}
|
||||
|
||||
func (os *OpenStack) DeleteVolume(volumeName string) error {
|
||||
used, err := os.diskIsUsed(volumeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if used {
|
||||
msg := fmt.Sprintf("Cannot delete the volume %q, it's still attached to a node", volumeName)
|
||||
return volume.NewDeletedVolumeInUseError(msg)
|
||||
}
|
||||
|
||||
sClient, err := openstack.NewBlockStorageV1(os.provider, gophercloud.EndpointOpts{
|
||||
Region: os.region,
|
||||
})
|
||||
|
||||
if err != nil || sClient == nil {
|
||||
glog.Errorf("Unable to initialize cinder client for region: %s", os.region)
|
||||
return err
|
||||
}
|
||||
err = volumes.Delete(sClient, volumeName).ExtractErr()
|
||||
if err != nil {
|
||||
glog.Errorf("Cannot delete volume %s: %v", volumeName, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Get device path of attached volume to the compute running kubelet, as known by cinder
|
||||
func (os *OpenStack) GetAttachmentDiskPath(instanceID string, diskName string) (string, error) {
|
||||
// See issue #33128 - Cinder does not always tell you the right device path, as such
|
||||
// we must only use this value as a last resort.
|
||||
disk, err := os.getVolume(diskName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(disk.Attachments) > 0 && disk.Attachments[0]["server_id"] != nil {
|
||||
if instanceID == disk.Attachments[0]["server_id"] {
|
||||
// Attachment[0]["device"] points to the device path
|
||||
// see http://developer.openstack.org/api-ref-blockstorage-v1.html
|
||||
return disk.Attachments[0]["device"].(string), nil
|
||||
} else {
|
||||
errMsg := fmt.Sprintf("Disk %q is attached to a different compute: %q, should be detached before proceeding", diskName, disk.Attachments[0]["server_id"])
|
||||
glog.Errorf(errMsg)
|
||||
return "", errors.New(errMsg)
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("volume %s is not attached to %s", diskName, instanceID)
|
||||
}
|
||||
|
||||
// query if a volume is attached to a compute instance
|
||||
func (os *OpenStack) DiskIsAttached(diskName, instanceID string) (bool, error) {
|
||||
disk, err := os.getVolume(diskName)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(disk.Attachments) > 0 && disk.Attachments[0]["server_id"] != nil && instanceID == disk.Attachments[0]["server_id"] {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// query if a list of volumes are attached to a compute instance
|
||||
func (os *OpenStack) DisksAreAttached(diskNames []string, instanceID string) (map[string]bool, error) {
|
||||
attached := make(map[string]bool)
|
||||
for _, diskName := range diskNames {
|
||||
attached[diskName] = false
|
||||
}
|
||||
for _, diskName := range diskNames {
|
||||
disk, err := os.getVolume(diskName)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if len(disk.Attachments) > 0 && disk.Attachments[0]["server_id"] != nil && instanceID == disk.Attachments[0]["server_id"] {
|
||||
attached[diskName] = true
|
||||
}
|
||||
}
|
||||
return attached, nil
|
||||
}
|
||||
|
||||
// diskIsUsed returns true a disk is attached to any node.
|
||||
func (os *OpenStack) diskIsUsed(diskName string) (bool, error) {
|
||||
disk, err := os.getVolume(diskName)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(disk.Attachments) > 0 {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// query if we should trust the cinder provide deviceName, See issue #33128
|
||||
func (os *OpenStack) ShouldTrustDevicePath() bool {
|
||||
return os.bsOpts.TrustDevicePath
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue