Skip to content
This repository was archived by the owner on Mar 22, 2018. It is now read-only.

Commit 5f19c47

Browse files
committed
Merge branch 'master' of github.com:kubernetes/kubernetes
2 parents 33ba8aa + a212b8d commit 5f19c47

6 files changed

Lines changed: 353 additions & 16 deletions

File tree

pkg/cloudprovider/providers/openstack/openstack_client.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func (os *OpenStack) NewNetworkV2() (*gophercloud.ServiceClient, error) {
2828
Region: os.region,
2929
})
3030
if err != nil {
31-
glog.Warningf("Failed to find network v2 endpoint: %v", err)
31+
glog.Warningf("Failed to find network v2 endpoint for region %s: %v", os.region, err)
3232
return nil, err
3333
}
3434
return network, nil
@@ -39,7 +39,7 @@ func (os *OpenStack) NewComputeV2() (*gophercloud.ServiceClient, error) {
3939
Region: os.region,
4040
})
4141
if err != nil {
42-
glog.Warningf("Failed to find compute v2 endpoint: %v", err)
42+
glog.Warningf("Failed to find compute v2 endpoint for region %s: %v", os.region, err)
4343
return nil, err
4444
}
4545
return compute, nil
@@ -50,7 +50,7 @@ func (os *OpenStack) NewBlockStorageV1() (*gophercloud.ServiceClient, error) {
5050
Region: os.region,
5151
})
5252
if err != nil {
53-
glog.Errorf("Unable to initialize cinder v1 client for region: %s", os.region)
53+
glog.Errorf("Unable to initialize cinder v1 client for region %s: %v", os.region, err)
5454
return nil, err
5555
}
5656
return storage, nil
@@ -61,7 +61,7 @@ func (os *OpenStack) NewBlockStorageV2() (*gophercloud.ServiceClient, error) {
6161
Region: os.region,
6262
})
6363
if err != nil {
64-
glog.Errorf("Unable to initialize cinder v2 client for region: %s", os.region)
64+
glog.Errorf("Unable to initialize cinder v2 client for region %s: %v", os.region, err)
6565
return nil, err
6666
}
6767
return storage, nil

pkg/cloudprovider/providers/openstack/openstack_loadbalancer.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ const (
6666

6767
activeStatus = "ACTIVE"
6868
errorStatus = "ERROR"
69+
70+
ServiceAnnotationLoadBalancerFloatingNetworkId = "loadbalancer.openstack.org/floating-network-id"
6971
)
7072

7173
// LoadBalancer implementation for LBaaS v1
@@ -581,6 +583,21 @@ func nodeAddressForLB(node *v1.Node) (string, error) {
581583
return addrs[0].Address, nil
582584
}
583585

586+
//getStringFromServiceAnnotation searches a given v1.Service for a specific annotationKey and either returns the annotation's value or a specified defaultSetting
587+
func getStringFromServiceAnnotation(service *v1.Service, annotationKey string, defaultSetting string) string {
588+
glog.V(4).Infof("getStringFromServiceAnnotation(%v, %v, %v)", service, annotationKey, defaultSetting)
589+
if annotationValue, ok := service.Annotations[annotationKey]; ok {
590+
//if there is an annotation for this setting, set the "setting" var to it
591+
// annotationValue can be empty, it is working as designed
592+
// it makes possible for instance provisioning loadbalancer without floatingip
593+
glog.V(4).Infof("Found a Service Annotation: %v = %v", annotationKey, annotationValue)
594+
return annotationValue
595+
}
596+
//if there is no annotation, set "settings" var to the value from cloud config
597+
glog.V(4).Infof("Could not find a Service Annotation; falling back on cloud-config setting: %v = %v", annotationKey, defaultSetting)
598+
return defaultSetting
599+
}
600+
584601
// TODO: This code currently ignores 'region' and always creates a
585602
// loadbalancer in only the current OpenStack region. We should take
586603
// a list of regions (from config) and query/create loadbalancers in
@@ -598,6 +615,9 @@ func (lbaas *LbaasV2) EnsureLoadBalancer(clusterName string, apiService *v1.Serv
598615
return nil, fmt.Errorf("no ports provided to openstack load balancer")
599616
}
600617

618+
floatingPool := getStringFromServiceAnnotation(apiService, ServiceAnnotationLoadBalancerFloatingNetworkId, lbaas.opts.FloatingNetworkId)
619+
glog.V(4).Infof("EnsureLoadBalancer using floatingPool: %v", floatingPool)
620+
601621
// Check for TCP protocol on each port
602622
// TODO: Convert all error messages to use an event recorder
603623
for _, port := range ports {
@@ -827,10 +847,10 @@ func (lbaas *LbaasV2) EnsureLoadBalancer(clusterName string, apiService *v1.Serv
827847
if err != nil && err != ErrNotFound {
828848
return nil, fmt.Errorf("Error getting floating ip for port %s: %v", portID, err)
829849
}
830-
if floatIP == nil && lbaas.opts.FloatingNetworkId != "" {
850+
if floatIP == nil && floatingPool != "" {
831851
glog.V(4).Infof("Creating floating ip for loadbalancer %s port %s", loadbalancer.ID, portID)
832852
floatIPOpts := floatingips.CreateOpts{
833-
FloatingNetworkID: lbaas.opts.FloatingNetworkId,
853+
FloatingNetworkID: floatingPool,
834854
PortID: portID,
835855
}
836856
floatIP, err = floatingips.Create(lbaas.network, floatIPOpts).Extract()

pkg/cloudprovider/providers/openstack/openstack_volumes.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -215,11 +215,7 @@ func (os *OpenStack) AttachDisk(instanceID, volumeID string) (string, error) {
215215
if err != nil {
216216
return "", err
217217
}
218-
if volume.Status != VolumeAvailableStatus {
219-
errmsg := fmt.Sprintf("volume %s status is %s, not %s, can not be attached to instance %s.", volume.Name, volume.Status, VolumeAvailableStatus, instanceID)
220-
glog.Errorf(errmsg)
221-
return "", errors.New(errmsg)
222-
}
218+
223219
cClient, err := os.NewComputeV2()
224220
if err != nil {
225221
return "", err
@@ -230,11 +226,9 @@ func (os *OpenStack) AttachDisk(instanceID, volumeID string) (string, error) {
230226
glog.V(4).Infof("Disk %s is already attached to instance %s", volumeID, instanceID)
231227
return volume.ID, nil
232228
}
233-
glog.V(2).Infof("Disk %s is attached to a different instance (%s), detaching", volumeID, volume.AttachedServerId)
234-
err = os.DetachDisk(volume.AttachedServerId, volumeID)
235-
if err != nil {
236-
return "", err
237-
}
229+
errmsg := fmt.Sprintf("Disk %s is attached to a different instance (%s)", volumeID, volume.AttachedServerId)
230+
glog.V(2).Infof(errmsg)
231+
return "", errors.New(errmsg)
238232
}
239233

240234
startTime := time.Now()
@@ -258,6 +252,12 @@ func (os *OpenStack) DetachDisk(instanceID, volumeID string) error {
258252
if err != nil {
259253
return err
260254
}
255+
if volume.Status == VolumeAvailableStatus {
256+
// "available" is fine since that means the volume is detached from instance already.
257+
glog.V(2).Infof("volume: %s has been detached from compute: %s ", volume.ID, instanceID)
258+
return nil
259+
}
260+
261261
if volume.Status != VolumeInUseStatus {
262262
errmsg := fmt.Sprintf("can not detach volume %s, its status is %s.", volume.Name, volume.Status)
263263
glog.Errorf(errmsg)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package(default_visibility = ["//visibility:public"])
2+
3+
licenses(["notice"])
4+
5+
load(
6+
"@io_bazel_rules_go//go:def.bzl",
7+
"go_library",
8+
"go_test",
9+
)
10+
11+
go_test(
12+
name = "go_default_test",
13+
srcs = ["openstack_test.go"],
14+
library = ":go_default_library",
15+
tags = ["automanaged"],
16+
)
17+
18+
go_library(
19+
name = "go_default_library",
20+
srcs = ["openstack.go"],
21+
tags = ["automanaged"],
22+
deps = [
23+
"//vendor/github.com/golang/glog:go_default_library",
24+
"//vendor/github.com/gophercloud/gophercloud/openstack:go_default_library",
25+
"//vendor/k8s.io/client-go/rest:go_default_library",
26+
],
27+
)
28+
29+
filegroup(
30+
name = "package-srcs",
31+
srcs = glob(["**"]),
32+
tags = ["automanaged"],
33+
visibility = ["//visibility:private"],
34+
)
35+
36+
filegroup(
37+
name = "all-srcs",
38+
srcs = [":package-srcs"],
39+
tags = ["automanaged"],
40+
)
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/*
2+
Copyright 2017 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package openstack
18+
19+
import (
20+
"fmt"
21+
"net/http"
22+
"sync"
23+
"time"
24+
25+
"github.com/golang/glog"
26+
"github.com/gophercloud/gophercloud/openstack"
27+
28+
restclient "k8s.io/client-go/rest"
29+
)
30+
31+
func init() {
32+
if err := restclient.RegisterAuthProviderPlugin("openstack", newOpenstackAuthProvider); err != nil {
33+
glog.Fatalf("Failed to register openstack auth plugin: %s", err)
34+
}
35+
}
36+
37+
// DefaultTTLDuration is the time before a token gets expired.
38+
const DefaultTTLDuration = 10 * time.Minute
39+
40+
// openstackAuthProvider is an authprovider for openstack. this provider reads
41+
// the environment variables to determine the client identity, and generates a
42+
// token which will be inserted into the request header later.
43+
type openstackAuthProvider struct {
44+
ttl time.Duration
45+
46+
tokenGetter TokenGetter
47+
}
48+
49+
// TokenGetter returns a bearer token that can be inserted into request.
50+
type TokenGetter interface {
51+
Token() (string, error)
52+
}
53+
54+
type tokenGetter struct{}
55+
56+
// Token creates a token by authenticate with keystone.
57+
func (*tokenGetter) Token() (string, error) {
58+
options, err := openstack.AuthOptionsFromEnv()
59+
if err != nil {
60+
return "", fmt.Errorf("failed to read openstack env vars: %s", err)
61+
}
62+
client, err := openstack.AuthenticatedClient(options)
63+
if err != nil {
64+
return "", fmt.Errorf("authentication failed: %s", err)
65+
}
66+
return client.TokenID, nil
67+
}
68+
69+
// cachedGetter caches a token until it gets expired, after the expiration, it will
70+
// generate another token and cache it.
71+
type cachedGetter struct {
72+
mutex sync.Mutex
73+
tokenGetter TokenGetter
74+
75+
token string
76+
born time.Time
77+
ttl time.Duration
78+
}
79+
80+
// Token returns the current available token, create a new one if expired.
81+
func (c *cachedGetter) Token() (string, error) {
82+
c.mutex.Lock()
83+
defer c.mutex.Unlock()
84+
85+
var err error
86+
// no token or exceeds the TTL
87+
if c.token == "" || time.Now().Sub(c.born) > c.ttl {
88+
c.token, err = c.tokenGetter.Token()
89+
if err != nil {
90+
return "", fmt.Errorf("failed to get token: %s", err)
91+
}
92+
c.born = time.Now()
93+
}
94+
return c.token, nil
95+
}
96+
97+
// tokenRoundTripper implements the RoundTripper interface: adding the bearer token
98+
// into the request header.
99+
type tokenRoundTripper struct {
100+
http.RoundTripper
101+
102+
tokenGetter TokenGetter
103+
}
104+
105+
// RoundTrip adds the bearer token into the request.
106+
func (t *tokenRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
107+
// if the authorization header already present, use it.
108+
if req.Header.Get("Authorization") != "" {
109+
return t.RoundTripper.RoundTrip(req)
110+
}
111+
112+
token, err := t.tokenGetter.Token()
113+
if err == nil {
114+
req.Header.Set("Authorization", "Bearer "+token)
115+
} else {
116+
glog.V(4).Infof("failed to get token: %s", err)
117+
}
118+
119+
return t.RoundTripper.RoundTrip(req)
120+
}
121+
122+
// newOpenstackAuthProvider creates an auth provider which works with openstack
123+
// environment.
124+
func newOpenstackAuthProvider(clusterAddress string, config map[string]string, persister restclient.AuthProviderConfigPersister) (restclient.AuthProvider, error) {
125+
var ttlDuration time.Duration
126+
var err error
127+
128+
ttl, found := config["ttl"]
129+
if !found {
130+
ttlDuration = DefaultTTLDuration
131+
// persist to config
132+
config["ttl"] = ttlDuration.String()
133+
if err = persister.Persist(config); err != nil {
134+
return nil, fmt.Errorf("failed to persist config: %s", err)
135+
}
136+
} else {
137+
ttlDuration, err = time.ParseDuration(ttl)
138+
if err != nil {
139+
return nil, fmt.Errorf("failed to parse ttl config: %s", err)
140+
}
141+
}
142+
143+
// TODO: read/persist client configuration(OS_XXX env vars) in config
144+
145+
return &openstackAuthProvider{
146+
ttl: ttlDuration,
147+
tokenGetter: &tokenGetter{},
148+
}, nil
149+
}
150+
151+
func (oap *openstackAuthProvider) WrapTransport(rt http.RoundTripper) http.RoundTripper {
152+
return &tokenRoundTripper{
153+
RoundTripper: rt,
154+
tokenGetter: &cachedGetter{
155+
tokenGetter: oap.tokenGetter,
156+
ttl: oap.ttl,
157+
},
158+
}
159+
}
160+
161+
func (oap *openstackAuthProvider) Login() error { return nil }

0 commit comments

Comments
 (0)