2 Copyright 2017 The Kubernetes Authors.
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
8 http://www.apache.org/licenses/LICENSE-2.0
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.
25 "github.com/gophercloud/gophercloud"
26 "github.com/gophercloud/gophercloud/openstack"
29 "k8s.io/apimachinery/pkg/util/net"
30 restclient "k8s.io/client-go/rest"
34 if err := restclient.RegisterAuthProviderPlugin("openstack", newOpenstackAuthProvider); err != nil {
35 klog.Fatalf("Failed to register openstack auth plugin: %s", err)
39 // DefaultTTLDuration is the time before a token gets expired.
40 const DefaultTTLDuration = 10 * time.Minute
42 // openstackAuthProvider is an authprovider for openstack. this provider reads
43 // the environment variables to determine the client identity, and generates a
44 // token which will be inserted into the request header later.
45 type openstackAuthProvider struct {
47 tokenGetter TokenGetter
50 // TokenGetter returns a bearer token that can be inserted into request.
51 type TokenGetter interface {
52 Token() (string, error)
55 type tokenGetter struct {
56 authOpt *gophercloud.AuthOptions
59 // Token creates a token by authenticate with keystone.
60 func (t *tokenGetter) Token() (string, error) {
61 var options gophercloud.AuthOptions
64 // reads the config from the environment
65 klog.V(4).Info("reading openstack config from the environment variables")
66 options, err = openstack.AuthOptionsFromEnv()
68 return "", fmt.Errorf("failed to read openstack env vars: %s", err)
73 client, err := openstack.AuthenticatedClient(options)
75 return "", fmt.Errorf("authentication failed: %s", err)
77 return client.TokenID, nil
80 // cachedGetter caches a token until it gets expired, after the expiration, it will
81 // generate another token and cache it.
82 type cachedGetter struct {
84 tokenGetter TokenGetter
91 // Token returns the current available token, create a new one if expired.
92 func (c *cachedGetter) Token() (string, error) {
94 defer c.mutex.Unlock()
97 // no token or exceeds the TTL
98 if c.token == "" || time.Since(c.born) > c.ttl {
99 c.token, err = c.tokenGetter.Token()
101 return "", fmt.Errorf("failed to get token: %s", err)
108 // tokenRoundTripper implements the RoundTripper interface: adding the bearer token
109 // into the request header.
110 type tokenRoundTripper struct {
113 tokenGetter TokenGetter
116 var _ net.RoundTripperWrapper = &tokenRoundTripper{}
118 // RoundTrip adds the bearer token into the request.
119 func (t *tokenRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
120 // if the authorization header already present, use it.
121 if req.Header.Get("Authorization") != "" {
122 return t.RoundTripper.RoundTrip(req)
125 token, err := t.tokenGetter.Token()
127 req.Header.Set("Authorization", "Bearer "+token)
129 klog.V(4).Infof("failed to get token: %s", err)
132 return t.RoundTripper.RoundTrip(req)
135 func (t *tokenRoundTripper) WrappedRoundTripper() http.RoundTripper { return t.RoundTripper }
137 // newOpenstackAuthProvider creates an auth provider which works with openstack
139 func newOpenstackAuthProvider(_ string, config map[string]string, persister restclient.AuthProviderConfigPersister) (restclient.AuthProvider, error) {
140 var ttlDuration time.Duration
143 klog.Warningf("WARNING: in-tree openstack auth plugin is now deprecated. please use the \"client-keystone-auth\" kubectl/client-go credential plugin instead")
144 ttl, found := config["ttl"]
146 ttlDuration = DefaultTTLDuration
148 config["ttl"] = ttlDuration.String()
149 if err = persister.Persist(config); err != nil {
150 return nil, fmt.Errorf("failed to persist config: %s", err)
153 ttlDuration, err = time.ParseDuration(ttl)
155 return nil, fmt.Errorf("failed to parse ttl config: %s", err)
159 authOpt := gophercloud.AuthOptions{
160 IdentityEndpoint: config["identityEndpoint"],
161 Username: config["username"],
162 Password: config["password"],
163 DomainName: config["name"],
164 TenantID: config["tenantId"],
165 TenantName: config["tenantName"],
168 getter := tokenGetter{}
170 if (authOpt != gophercloud.AuthOptions{}) {
171 if len(authOpt.IdentityEndpoint) == 0 {
172 return nil, fmt.Errorf("empty %q in the config for openstack auth provider", "identityEndpoint")
174 getter.authOpt = &authOpt
177 return &openstackAuthProvider{
179 tokenGetter: &getter,
183 func (oap *openstackAuthProvider) WrapTransport(rt http.RoundTripper) http.RoundTripper {
184 return &tokenRoundTripper{
186 tokenGetter: &cachedGetter{
187 tokenGetter: oap.tokenGetter,
193 func (oap *openstackAuthProvider) Login() error { return nil }