diff --git a/plugin/pkg/client/auth/openstack/openstack.go b/plugin/pkg/client/auth/openstack/openstack.go deleted file mode 100644 index fab5104e..00000000 --- a/plugin/pkg/client/auth/openstack/openstack.go +++ /dev/null @@ -1,193 +0,0 @@ -/* -Copyright 2017 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 ( - "fmt" - "net/http" - "sync" - "time" - - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack" - "k8s.io/klog" - - "k8s.io/apimachinery/pkg/util/net" - restclient "k8s.io/client-go/rest" -) - -func init() { - if err := restclient.RegisterAuthProviderPlugin("openstack", newOpenstackAuthProvider); err != nil { - klog.Fatalf("Failed to register openstack auth plugin: %s", err) - } -} - -// DefaultTTLDuration is the time before a token gets expired. -const DefaultTTLDuration = 10 * time.Minute - -// openstackAuthProvider is an authprovider for openstack. this provider reads -// the environment variables to determine the client identity, and generates a -// token which will be inserted into the request header later. -type openstackAuthProvider struct { - ttl time.Duration - tokenGetter TokenGetter -} - -// TokenGetter returns a bearer token that can be inserted into request. -type TokenGetter interface { - Token() (string, error) -} - -type tokenGetter struct { - authOpt *gophercloud.AuthOptions -} - -// Token creates a token by authenticate with keystone. -func (t *tokenGetter) Token() (string, error) { - var options gophercloud.AuthOptions - var err error - if t.authOpt == nil { - // reads the config from the environment - klog.V(4).Info("reading openstack config from the environment variables") - options, err = openstack.AuthOptionsFromEnv() - if err != nil { - return "", fmt.Errorf("failed to read openstack env vars: %s", err) - } - } else { - options = *t.authOpt - } - client, err := openstack.AuthenticatedClient(options) - if err != nil { - return "", fmt.Errorf("authentication failed: %s", err) - } - return client.TokenID, nil -} - -// cachedGetter caches a token until it gets expired, after the expiration, it will -// generate another token and cache it. -type cachedGetter struct { - mutex sync.Mutex - tokenGetter TokenGetter - - token string - born time.Time - ttl time.Duration -} - -// Token returns the current available token, create a new one if expired. -func (c *cachedGetter) Token() (string, error) { - c.mutex.Lock() - defer c.mutex.Unlock() - - var err error - // no token or exceeds the TTL - if c.token == "" || time.Since(c.born) > c.ttl { - c.token, err = c.tokenGetter.Token() - if err != nil { - return "", fmt.Errorf("failed to get token: %s", err) - } - c.born = time.Now() - } - return c.token, nil -} - -// tokenRoundTripper implements the RoundTripper interface: adding the bearer token -// into the request header. -type tokenRoundTripper struct { - http.RoundTripper - - tokenGetter TokenGetter -} - -var _ net.RoundTripperWrapper = &tokenRoundTripper{} - -// RoundTrip adds the bearer token into the request. -func (t *tokenRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { - // if the authorization header already present, use it. - if req.Header.Get("Authorization") != "" { - return t.RoundTripper.RoundTrip(req) - } - - token, err := t.tokenGetter.Token() - if err == nil { - req.Header.Set("Authorization", "Bearer "+token) - } else { - klog.V(4).Infof("failed to get token: %s", err) - } - - return t.RoundTripper.RoundTrip(req) -} - -func (t *tokenRoundTripper) WrappedRoundTripper() http.RoundTripper { return t.RoundTripper } - -// newOpenstackAuthProvider creates an auth provider which works with openstack -// environment. -func newOpenstackAuthProvider(_ string, config map[string]string, persister restclient.AuthProviderConfigPersister) (restclient.AuthProvider, error) { - var ttlDuration time.Duration - var err error - - klog.Warningf("WARNING: in-tree openstack auth plugin is now deprecated. please use the \"client-keystone-auth\" kubectl/client-go credential plugin instead") - ttl, found := config["ttl"] - if !found { - ttlDuration = DefaultTTLDuration - // persist to config - config["ttl"] = ttlDuration.String() - if err = persister.Persist(config); err != nil { - return nil, fmt.Errorf("failed to persist config: %s", err) - } - } else { - ttlDuration, err = time.ParseDuration(ttl) - if err != nil { - return nil, fmt.Errorf("failed to parse ttl config: %s", err) - } - } - - authOpt := gophercloud.AuthOptions{ - IdentityEndpoint: config["identityEndpoint"], - Username: config["username"], - Password: config["password"], - DomainName: config["name"], - TenantID: config["tenantId"], - TenantName: config["tenantName"], - } - - getter := tokenGetter{} - // not empty - if (authOpt != gophercloud.AuthOptions{}) { - if len(authOpt.IdentityEndpoint) == 0 { - return nil, fmt.Errorf("empty %q in the config for openstack auth provider", "identityEndpoint") - } - getter.authOpt = &authOpt - } - - return &openstackAuthProvider{ - ttl: ttlDuration, - tokenGetter: &getter, - }, nil -} - -func (oap *openstackAuthProvider) WrapTransport(rt http.RoundTripper) http.RoundTripper { - return &tokenRoundTripper{ - RoundTripper: rt, - tokenGetter: &cachedGetter{ - tokenGetter: oap.tokenGetter, - ttl: oap.ttl, - }, - } -} - -func (oap *openstackAuthProvider) Login() error { return nil } diff --git a/plugin/pkg/client/auth/openstack/openstack_stub.go b/plugin/pkg/client/auth/openstack/openstack_stub.go new file mode 100644 index 00000000..57e6b94a --- /dev/null +++ b/plugin/pkg/client/auth/openstack/openstack_stub.go @@ -0,0 +1,36 @@ +/* +Copyright 2020 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" + + "k8s.io/client-go/rest" + "k8s.io/klog" +) + +func init() { + if err := rest.RegisterAuthProviderPlugin("openstack", newOpenstackAuthProvider); err != nil { + klog.Fatalf("Failed to register openstack auth plugin: %s", err) + } +} + +func newOpenstackAuthProvider(_ string, _ map[string]string, _ rest.AuthProviderConfigPersister) (rest.AuthProvider, error) { + return nil, errors.New(`The openstack auth plugin has been removed. +Please use the "client-keystone-auth" kubectl/client-go credential plugin instead. +See https://github.com/kubernetes/cloud-provider-openstack/blob/master/docs/using-client-keystone-auth.md for further details`) +} diff --git a/plugin/pkg/client/auth/openstack/openstack_test.go b/plugin/pkg/client/auth/openstack/openstack_test.go deleted file mode 100644 index 24d55b9f..00000000 --- a/plugin/pkg/client/auth/openstack/openstack_test.go +++ /dev/null @@ -1,173 +0,0 @@ -/* -Copyright 2017 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 ( - "math/rand" - "net/http" - "testing" - "time" -) - -// testTokenGetter is a simple random token getter. -type testTokenGetter struct{} - -const LetterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" - -func RandStringBytes(n int) string { - b := make([]byte, n) - for i := range b { - b[i] = LetterBytes[rand.Intn(len(LetterBytes))] - } - return string(b) -} - -func (*testTokenGetter) Token() (string, error) { - return RandStringBytes(32), nil -} - -// testRoundTripper is mocked roundtripper which responds with unauthorized when -// there is no authorization header, otherwise returns status ok. -type testRoundTripper struct{} - -func (trt *testRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { - authHeader := req.Header.Get("Authorization") - if authHeader == "" || authHeader == "Bearer " { - return &http.Response{ - StatusCode: http.StatusUnauthorized, - }, nil - } - return &http.Response{StatusCode: http.StatusOK}, nil -} - -func TestOpenstackAuthProvider(t *testing.T) { - trt := &tokenRoundTripper{ - RoundTripper: &testRoundTripper{}, - } - - tests := []struct { - name string - ttl time.Duration - interval time.Duration - same bool - }{ - { - name: "normal", - ttl: 2 * time.Second, - interval: 1 * time.Second, - same: true, - }, - { - name: "expire", - ttl: 1 * time.Second, - interval: 2 * time.Second, - same: false, - }, - } - - for _, test := range tests { - trt.tokenGetter = &cachedGetter{ - tokenGetter: &testTokenGetter{}, - ttl: test.ttl, - } - - req, err := http.NewRequest(http.MethodPost, "https://test-api-server.com", nil) - if err != nil { - t.Errorf("failed to new request: %s", err) - } - trt.RoundTrip(req) - header := req.Header.Get("Authorization") - if header == "" { - t.Errorf("expect to see token in header, but is absent") - } - - time.Sleep(test.interval) - - req, err = http.NewRequest(http.MethodPost, "https://test-api-server.com", nil) - if err != nil { - t.Errorf("failed to new request: %s", err) - } - trt.RoundTrip(req) - newHeader := req.Header.Get("Authorization") - if newHeader == "" { - t.Errorf("expect to see token in header, but is absent") - } - - same := newHeader == header - if same != test.same { - t.Errorf("expect to get %t when compare header, but saw %t", test.same, same) - } - } - -} - -type fakePersister struct{} - -func (i *fakePersister) Persist(map[string]string) error { - return nil -} - -func TestNewOpenstackAuthProvider(t *testing.T) { - tests := []struct { - name string - config map[string]string - expectError bool - }{ - { - name: "normal config without openstack configurations", - config: map[string]string{ - "ttl": "1s", - "foo": "bar", - }, - }, - { - name: "openstack auth provider: missing identityEndpoint", - config: map[string]string{ - "ttl": "1s", - "foo": "bar", - "username": "xyz", - "password": "123", - "tenantName": "admin", - }, - expectError: true, - }, - { - name: "openstack auth provider", - config: map[string]string{ - "ttl": "1s", - "foo": "bar", - "identityEndpoint": "http://controller:35357/v3", - "username": "xyz", - "password": "123", - "tenantName": "admin", - }, - }, - } - - for _, test := range tests { - _, err := newOpenstackAuthProvider("test", test.config, &fakePersister{}) - if err != nil { - if !test.expectError { - t.Errorf("unexpected error: %v", err) - } - } else { - if test.expectError { - t.Error("expect error, but nil") - } - } - } -}