mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Migrate the controller to use TokenRequest and rotate token periodically
This commit is contained in:
		
							
								
								
									
										4
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
								
							@@ -1758,12 +1758,12 @@
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"ImportPath": "github.com/go-openapi/jsonpointer",
 | 
								"ImportPath": "github.com/go-openapi/jsonpointer",
 | 
				
			||||||
			"Comment": "v0.18.0",
 | 
								"Comment": "v0.19.0",
 | 
				
			||||||
			"Rev": "ef5f0afec364d3b9396b7b77b43dbe26bf1f8004"
 | 
								"Rev": "ef5f0afec364d3b9396b7b77b43dbe26bf1f8004"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"ImportPath": "github.com/go-openapi/jsonreference",
 | 
								"ImportPath": "github.com/go-openapi/jsonreference",
 | 
				
			||||||
			"Comment": "v0.18.0",
 | 
								"Comment": "v0.19.0",
 | 
				
			||||||
			"Rev": "8483a886a90412cd6858df4ea3483dce9c8e35a3"
 | 
								"Rev": "8483a886a90412cd6858df4ea3483dce9c8e35a3"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,6 +31,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"github.com/spf13/cobra"
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/api/core/v1"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
						"k8s.io/apimachinery/pkg/runtime/schema"
 | 
				
			||||||
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
 | 
						utilruntime "k8s.io/apimachinery/pkg/util/runtime"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/util/sets"
 | 
						"k8s.io/apimachinery/pkg/util/sets"
 | 
				
			||||||
@@ -39,9 +40,11 @@ import (
 | 
				
			|||||||
	"k8s.io/apiserver/pkg/server"
 | 
						"k8s.io/apiserver/pkg/server"
 | 
				
			||||||
	"k8s.io/apiserver/pkg/server/healthz"
 | 
						"k8s.io/apiserver/pkg/server/healthz"
 | 
				
			||||||
	"k8s.io/apiserver/pkg/server/mux"
 | 
						"k8s.io/apiserver/pkg/server/mux"
 | 
				
			||||||
 | 
						utilfeature "k8s.io/apiserver/pkg/util/feature"
 | 
				
			||||||
	"k8s.io/apiserver/pkg/util/term"
 | 
						"k8s.io/apiserver/pkg/util/term"
 | 
				
			||||||
	cacheddiscovery "k8s.io/client-go/discovery/cached"
 | 
						cacheddiscovery "k8s.io/client-go/discovery/cached"
 | 
				
			||||||
	"k8s.io/client-go/informers"
 | 
						"k8s.io/client-go/informers"
 | 
				
			||||||
 | 
						clientset "k8s.io/client-go/kubernetes"
 | 
				
			||||||
	restclient "k8s.io/client-go/rest"
 | 
						restclient "k8s.io/client-go/rest"
 | 
				
			||||||
	"k8s.io/client-go/restmapper"
 | 
						"k8s.io/client-go/restmapper"
 | 
				
			||||||
	"k8s.io/client-go/tools/leaderelection"
 | 
						"k8s.io/client-go/tools/leaderelection"
 | 
				
			||||||
@@ -58,6 +61,7 @@ import (
 | 
				
			|||||||
	"k8s.io/kubernetes/pkg/controller"
 | 
						"k8s.io/kubernetes/pkg/controller"
 | 
				
			||||||
	kubectrlmgrconfig "k8s.io/kubernetes/pkg/controller/apis/config"
 | 
						kubectrlmgrconfig "k8s.io/kubernetes/pkg/controller/apis/config"
 | 
				
			||||||
	serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount"
 | 
						serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/features"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/serviceaccount"
 | 
						"k8s.io/kubernetes/pkg/serviceaccount"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/util/configz"
 | 
						"k8s.io/kubernetes/pkg/util/configz"
 | 
				
			||||||
	utilflag "k8s.io/kubernetes/pkg/util/flag"
 | 
						utilflag "k8s.io/kubernetes/pkg/util/flag"
 | 
				
			||||||
@@ -199,11 +203,22 @@ func Run(c *config.CompletedConfig, stopCh <-chan struct{}) error {
 | 
				
			|||||||
				// If one isn't, we'll timeout and exit when our client builder is unable to create the tokens.
 | 
									// If one isn't, we'll timeout and exit when our client builder is unable to create the tokens.
 | 
				
			||||||
				klog.Warningf("--use-service-account-credentials was specified without providing a --service-account-private-key-file")
 | 
									klog.Warningf("--use-service-account-credentials was specified without providing a --service-account-private-key-file")
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			clientBuilder = controller.SAControllerClientBuilder{
 | 
					
 | 
				
			||||||
				ClientConfig:         restclient.AnonymousClientConfig(c.Kubeconfig),
 | 
								if shouldTurnOnDynamicClient(c.Client) {
 | 
				
			||||||
				CoreClient:           c.Client.CoreV1(),
 | 
									klog.V(1).Infof("using dynamic client builder")
 | 
				
			||||||
				AuthenticationClient: c.Client.AuthenticationV1(),
 | 
									//Dynamic builder will use TokenRequest feature and refresh service account token periodically
 | 
				
			||||||
				Namespace:            "kube-system",
 | 
									clientBuilder = controller.NewDynamicClientBuilder(
 | 
				
			||||||
 | 
										restclient.AnonymousClientConfig(c.Kubeconfig),
 | 
				
			||||||
 | 
										c.Client.CoreV1(),
 | 
				
			||||||
 | 
										"kube-system")
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									klog.V(1).Infof("using legacy client builder")
 | 
				
			||||||
 | 
									clientBuilder = controller.SAControllerClientBuilder{
 | 
				
			||||||
 | 
										ClientConfig:         restclient.AnonymousClientConfig(c.Kubeconfig),
 | 
				
			||||||
 | 
										CoreClient:           c.Client.CoreV1(),
 | 
				
			||||||
 | 
										AuthenticationClient: c.Client.AuthenticationV1(),
 | 
				
			||||||
 | 
										Namespace:            "kube-system",
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			clientBuilder = rootClientBuilder
 | 
								clientBuilder = rootClientBuilder
 | 
				
			||||||
@@ -566,3 +581,24 @@ func readCA(file string) ([]byte, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return rootCA, err
 | 
						return rootCA, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func shouldTurnOnDynamicClient(client clientset.Interface) bool {
 | 
				
			||||||
 | 
						if !utilfeature.DefaultFeatureGate.Enabled(features.TokenRequest) {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						apiResourceList, err := client.Discovery().ServerResourcesForGroupVersion(v1.SchemeGroupVersion.String())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							klog.Warningf("fetch api resource lists failed, use legacy client builder: %v", err)
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, resource := range apiResourceList.APIResources {
 | 
				
			||||||
 | 
							if resource.Name == "serviceaccounts/token" &&
 | 
				
			||||||
 | 
								resource.Group == "authentication.k8s.io" &&
 | 
				
			||||||
 | 
								sets.NewString(resource.Verbs...).Has("create") {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -160,7 +160,8 @@
 | 
				
			|||||||
        "k8s.io/client-go/util/cert",
 | 
					        "k8s.io/client-go/util/cert",
 | 
				
			||||||
        "k8s.io/client-go/util/flowcontrol",
 | 
					        "k8s.io/client-go/util/flowcontrol",
 | 
				
			||||||
        "k8s.io/client-go/util/retry",
 | 
					        "k8s.io/client-go/util/retry",
 | 
				
			||||||
        "k8s.io/client-go/util/workqueue"
 | 
					        "k8s.io/client-go/util/workqueue",
 | 
				
			||||||
 | 
						"k8s.io/client-go/transport"
 | 
				
			||||||
      ]
 | 
					      ]
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,6 +43,7 @@ go_library(
 | 
				
			|||||||
    name = "go_default_library",
 | 
					    name = "go_default_library",
 | 
				
			||||||
    srcs = [
 | 
					    srcs = [
 | 
				
			||||||
        "client_builder.go",
 | 
					        "client_builder.go",
 | 
				
			||||||
 | 
					        "client_builder_dynamic.go",
 | 
				
			||||||
        "controller_ref_manager.go",
 | 
					        "controller_ref_manager.go",
 | 
				
			||||||
        "controller_utils.go",
 | 
					        "controller_utils.go",
 | 
				
			||||||
        "doc.go",
 | 
					        "doc.go",
 | 
				
			||||||
@@ -85,10 +86,13 @@ go_library(
 | 
				
			|||||||
        "//staging/src/k8s.io/client-go/tools/cache:go_default_library",
 | 
					        "//staging/src/k8s.io/client-go/tools/cache:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/client-go/tools/record:go_default_library",
 | 
					        "//staging/src/k8s.io/client-go/tools/record:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/client-go/tools/watch:go_default_library",
 | 
					        "//staging/src/k8s.io/client-go/tools/watch:go_default_library",
 | 
				
			||||||
 | 
					        "//staging/src/k8s.io/client-go/transport:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/client-go/util/retry:go_default_library",
 | 
					        "//staging/src/k8s.io/client-go/util/retry:go_default_library",
 | 
				
			||||||
        "//vendor/github.com/golang/groupcache/lru:go_default_library",
 | 
					        "//vendor/github.com/golang/groupcache/lru:go_default_library",
 | 
				
			||||||
 | 
					        "//vendor/golang.org/x/oauth2:go_default_library",
 | 
				
			||||||
        "//vendor/k8s.io/klog:go_default_library",
 | 
					        "//vendor/k8s.io/klog:go_default_library",
 | 
				
			||||||
        "//vendor/k8s.io/utils/integer:go_default_library",
 | 
					        "//vendor/k8s.io/utils/integer:go_default_library",
 | 
				
			||||||
 | 
					        "//vendor/k8s.io/utils/pointer:go_default_library",
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -108,7 +108,7 @@ type SAControllerClientBuilder struct {
 | 
				
			|||||||
// config returns a complete clientConfig for constructing clients.  This is separate in anticipation of composition
 | 
					// config returns a complete clientConfig for constructing clients.  This is separate in anticipation of composition
 | 
				
			||||||
// which means that not all clientsets are known here
 | 
					// which means that not all clientsets are known here
 | 
				
			||||||
func (b SAControllerClientBuilder) Config(name string) (*restclient.Config, error) {
 | 
					func (b SAControllerClientBuilder) Config(name string) (*restclient.Config, error) {
 | 
				
			||||||
	sa, err := b.getOrCreateServiceAccount(name)
 | 
						sa, err := getOrCreateServiceAccount(b.CoreClient, b.Namespace, name)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -177,30 +177,6 @@ func (b SAControllerClientBuilder) Config(name string) (*restclient.Config, erro
 | 
				
			|||||||
	return clientConfig, nil
 | 
						return clientConfig, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b SAControllerClientBuilder) getOrCreateServiceAccount(name string) (*v1.ServiceAccount, error) {
 | 
					 | 
				
			||||||
	sa, err := b.CoreClient.ServiceAccounts(b.Namespace).Get(name, metav1.GetOptions{})
 | 
					 | 
				
			||||||
	if err == nil {
 | 
					 | 
				
			||||||
		return sa, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if !apierrors.IsNotFound(err) {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Create the namespace if we can't verify it exists.
 | 
					 | 
				
			||||||
	// Tolerate errors, since we don't know whether this component has namespace creation permissions.
 | 
					 | 
				
			||||||
	if _, err := b.CoreClient.Namespaces().Get(b.Namespace, metav1.GetOptions{}); err != nil {
 | 
					 | 
				
			||||||
		b.CoreClient.Namespaces().Create(&v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: b.Namespace}})
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Create the service account
 | 
					 | 
				
			||||||
	sa, err = b.CoreClient.ServiceAccounts(b.Namespace).Create(&v1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Namespace: b.Namespace, Name: name}})
 | 
					 | 
				
			||||||
	if apierrors.IsAlreadyExists(err) {
 | 
					 | 
				
			||||||
		// If we're racing to init and someone else already created it, re-fetch
 | 
					 | 
				
			||||||
		return b.CoreClient.ServiceAccounts(b.Namespace).Get(name, metav1.GetOptions{})
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return sa, err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (b SAControllerClientBuilder) getAuthenticatedConfig(sa *v1.ServiceAccount, token string) (*restclient.Config, bool, error) {
 | 
					func (b SAControllerClientBuilder) getAuthenticatedConfig(sa *v1.ServiceAccount, token string) (*restclient.Config, bool, error) {
 | 
				
			||||||
	username := apiserverserviceaccount.MakeUsername(sa.Namespace, sa.Name)
 | 
						username := apiserverserviceaccount.MakeUsername(sa.Namespace, sa.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										217
									
								
								pkg/controller/client_builder_dynamic.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								pkg/controller/client_builder_dynamic.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,217 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2018 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 controller
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"golang.org/x/oauth2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						v1authenticationapi "k8s.io/api/authentication/v1"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/util/clock"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/util/wait"
 | 
				
			||||||
 | 
						apiserverserviceaccount "k8s.io/apiserver/pkg/authentication/serviceaccount"
 | 
				
			||||||
 | 
						clientset "k8s.io/client-go/kubernetes"
 | 
				
			||||||
 | 
						v1core "k8s.io/client-go/kubernetes/typed/core/v1"
 | 
				
			||||||
 | 
						restclient "k8s.io/client-go/rest"
 | 
				
			||||||
 | 
						"k8s.io/client-go/transport"
 | 
				
			||||||
 | 
						"k8s.io/klog"
 | 
				
			||||||
 | 
						utilpointer "k8s.io/utils/pointer"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						// defaultExpirationSeconds defines the duration of a TokenRequest in seconds.
 | 
				
			||||||
 | 
						defaultExpirationSeconds = int64(3600)
 | 
				
			||||||
 | 
						// defaultLeewayPercent defines the percentage of expiration left before the client trigger a token rotation.
 | 
				
			||||||
 | 
						// range[0, 100]
 | 
				
			||||||
 | 
						defaultLeewayPercent = 20
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type DynamicControllerClientBuilder struct {
 | 
				
			||||||
 | 
						// ClientConfig is a skeleton config to clone and use as the basis for each controller client
 | 
				
			||||||
 | 
						ClientConfig *restclient.Config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// CoreClient is used to provision service accounts if needed and watch for their associated tokens
 | 
				
			||||||
 | 
						// to construct a controller client
 | 
				
			||||||
 | 
						CoreClient v1core.CoreV1Interface
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Namespace is the namespace used to host the service accounts that will back the
 | 
				
			||||||
 | 
						// controllers.  It must be highly privileged namespace which normal users cannot inspect.
 | 
				
			||||||
 | 
						Namespace string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// roundTripperFuncMap is a cache stores the corresponding roundtripper func for each
 | 
				
			||||||
 | 
						// service account
 | 
				
			||||||
 | 
						roundTripperFuncMap map[string]func(http.RoundTripper) http.RoundTripper
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// expirationSeconds defines the token expiration seconds
 | 
				
			||||||
 | 
						expirationSeconds int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// leewayPercent defines the percentage of expiration left before the client trigger a token rotation.
 | 
				
			||||||
 | 
						leewayPercent int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex sync.Mutex
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clock clock.Clock
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewDynamicClientBuilder(clientConfig *restclient.Config, coreClient v1core.CoreV1Interface, ns string) ControllerClientBuilder {
 | 
				
			||||||
 | 
						builder := &DynamicControllerClientBuilder{
 | 
				
			||||||
 | 
							ClientConfig:        clientConfig,
 | 
				
			||||||
 | 
							CoreClient:          coreClient,
 | 
				
			||||||
 | 
							Namespace:           ns,
 | 
				
			||||||
 | 
							roundTripperFuncMap: map[string]func(http.RoundTripper) http.RoundTripper{},
 | 
				
			||||||
 | 
							expirationSeconds:   defaultExpirationSeconds,
 | 
				
			||||||
 | 
							leewayPercent:       defaultLeewayPercent,
 | 
				
			||||||
 | 
							clock:               clock.RealClock{},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return builder
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// this function only for test purpose, don't call it
 | 
				
			||||||
 | 
					func NewTestDynamicClientBuilder(clientConfig *restclient.Config, coreClient v1core.CoreV1Interface, ns string, expirationSeconds int64, leewayPercent int) ControllerClientBuilder {
 | 
				
			||||||
 | 
						builder := &DynamicControllerClientBuilder{
 | 
				
			||||||
 | 
							ClientConfig:        clientConfig,
 | 
				
			||||||
 | 
							CoreClient:          coreClient,
 | 
				
			||||||
 | 
							Namespace:           ns,
 | 
				
			||||||
 | 
							roundTripperFuncMap: map[string]func(http.RoundTripper) http.RoundTripper{},
 | 
				
			||||||
 | 
							expirationSeconds:   expirationSeconds,
 | 
				
			||||||
 | 
							leewayPercent:       leewayPercent,
 | 
				
			||||||
 | 
							clock:               clock.RealClock{},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return builder
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (t *DynamicControllerClientBuilder) Config(saName string) (*restclient.Config, error) {
 | 
				
			||||||
 | 
						_, err := getOrCreateServiceAccount(t.CoreClient, t.Namespace, saName)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						configCopy := constructClient(t.Namespace, saName, t.ClientConfig)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.mutex.Lock()
 | 
				
			||||||
 | 
						defer t.mutex.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rt, ok := t.roundTripperFuncMap[saName]
 | 
				
			||||||
 | 
						if ok {
 | 
				
			||||||
 | 
							configCopy.WrapTransport = rt
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							cachedTokenSource := transport.NewCachedTokenSource(&tokenSourceImpl{
 | 
				
			||||||
 | 
								namespace:          t.Namespace,
 | 
				
			||||||
 | 
								serviceAccountName: saName,
 | 
				
			||||||
 | 
								coreClient:         t.CoreClient,
 | 
				
			||||||
 | 
								expirationSeconds:  t.expirationSeconds,
 | 
				
			||||||
 | 
								leewayPercent:      t.leewayPercent,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							configCopy.WrapTransport = transport.TokenSourceWrapTransport(cachedTokenSource)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							t.roundTripperFuncMap[saName] = configCopy.WrapTransport
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &configCopy, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (t *DynamicControllerClientBuilder) ConfigOrDie(name string) *restclient.Config {
 | 
				
			||||||
 | 
						clientConfig, err := t.Config(name)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							klog.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return clientConfig
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (t *DynamicControllerClientBuilder) Client(name string) (clientset.Interface, error) {
 | 
				
			||||||
 | 
						clientConfig, err := t.Config(name)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return clientset.NewForConfig(clientConfig)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (t *DynamicControllerClientBuilder) ClientOrDie(name string) clientset.Interface {
 | 
				
			||||||
 | 
						client, err := t.Client(name)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							klog.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return client
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type tokenSourceImpl struct {
 | 
				
			||||||
 | 
						namespace          string
 | 
				
			||||||
 | 
						serviceAccountName string
 | 
				
			||||||
 | 
						coreClient         v1core.CoreV1Interface
 | 
				
			||||||
 | 
						expirationSeconds  int64
 | 
				
			||||||
 | 
						leewayPercent      int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ts *tokenSourceImpl) Token() (*oauth2.Token, error) {
 | 
				
			||||||
 | 
						var retTokenRequest *v1authenticationapi.TokenRequest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						backoff := wait.Backoff{
 | 
				
			||||||
 | 
							Duration: 500 * time.Millisecond,
 | 
				
			||||||
 | 
							Factor:   2, // double the timeout for every failure
 | 
				
			||||||
 | 
							Steps:    4,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := wait.ExponentialBackoff(backoff, func() (bool, error) {
 | 
				
			||||||
 | 
							if _, inErr := getOrCreateServiceAccount(ts.coreClient, ts.namespace, ts.serviceAccountName); inErr != nil {
 | 
				
			||||||
 | 
								klog.Warningf("get or create service account failed: %v", inErr)
 | 
				
			||||||
 | 
								return false, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							tr, inErr := ts.coreClient.ServiceAccounts(ts.namespace).CreateToken(ts.serviceAccountName, &v1authenticationapi.TokenRequest{
 | 
				
			||||||
 | 
								Spec: v1authenticationapi.TokenRequestSpec{
 | 
				
			||||||
 | 
									ExpirationSeconds: utilpointer.Int64Ptr(ts.expirationSeconds),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							if inErr != nil {
 | 
				
			||||||
 | 
								klog.Warningf("get token failed: %v", inErr)
 | 
				
			||||||
 | 
								return false, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							retTokenRequest = tr
 | 
				
			||||||
 | 
							return true, nil
 | 
				
			||||||
 | 
						}); err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("failed to get token for %s/%s: %v", ts.namespace, ts.serviceAccountName, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if retTokenRequest.Spec.ExpirationSeconds == nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("nil pointer of expiration in token request")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lifetime := retTokenRequest.Status.ExpirationTimestamp.Time.Sub(time.Now())
 | 
				
			||||||
 | 
						if lifetime < time.Minute*10 {
 | 
				
			||||||
 | 
							// possible clock skew issue, pin to minimum token lifetime
 | 
				
			||||||
 | 
							lifetime = time.Minute * 10
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						leeway := time.Duration(int64(lifetime) * int64(ts.leewayPercent) / 100)
 | 
				
			||||||
 | 
						expiry := time.Now().Add(lifetime).Add(-1 * leeway)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &oauth2.Token{
 | 
				
			||||||
 | 
							AccessToken: retTokenRequest.Status.Token,
 | 
				
			||||||
 | 
							TokenType:   "Bearer",
 | 
				
			||||||
 | 
							Expiry:      expiry,
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func constructClient(saNamespace, saName string, config *restclient.Config) restclient.Config {
 | 
				
			||||||
 | 
						username := apiserverserviceaccount.MakeUsername(saNamespace, saName)
 | 
				
			||||||
 | 
						ret := *restclient.AnonymousClientConfig(config)
 | 
				
			||||||
 | 
						restclient.AddUserAgent(&ret, username)
 | 
				
			||||||
 | 
						return ret
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -40,6 +40,7 @@ import (
 | 
				
			|||||||
	"k8s.io/apimachinery/pkg/util/strategicpatch"
 | 
						"k8s.io/apimachinery/pkg/util/strategicpatch"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/util/wait"
 | 
						"k8s.io/apimachinery/pkg/util/wait"
 | 
				
			||||||
	clientset "k8s.io/client-go/kubernetes"
 | 
						clientset "k8s.io/client-go/kubernetes"
 | 
				
			||||||
 | 
						v1core "k8s.io/client-go/kubernetes/typed/core/v1"
 | 
				
			||||||
	"k8s.io/client-go/tools/cache"
 | 
						"k8s.io/client-go/tools/cache"
 | 
				
			||||||
	"k8s.io/client-go/tools/record"
 | 
						"k8s.io/client-go/tools/record"
 | 
				
			||||||
	clientretry "k8s.io/client-go/util/retry"
 | 
						clientretry "k8s.io/client-go/util/retry"
 | 
				
			||||||
@@ -1096,3 +1097,29 @@ func AddOrUpdateLabelsOnNode(kubeClient clientset.Interface, nodeName string, la
 | 
				
			|||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getOrCreateServiceAccount(coreClient v1core.CoreV1Interface, namespace, name string) (*v1.ServiceAccount, error) {
 | 
				
			||||||
 | 
						sa, err := coreClient.ServiceAccounts(namespace).Get(name, metav1.GetOptions{})
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							return sa, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !apierrors.IsNotFound(err) {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create the namespace if we can't verify it exists.
 | 
				
			||||||
 | 
						// Tolerate errors, since we don't know whether this component has namespace creation permissions.
 | 
				
			||||||
 | 
						if _, err := coreClient.Namespaces().Get(namespace, metav1.GetOptions{}); apierrors.IsNotFound(err) {
 | 
				
			||||||
 | 
							if _, err = coreClient.Namespaces().Create(&v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}); err != nil && !apierrors.IsAlreadyExists(err) {
 | 
				
			||||||
 | 
								klog.Warningf("create non-exist namespace %s failed:%v", namespace, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create the service account
 | 
				
			||||||
 | 
						sa, err = coreClient.ServiceAccounts(namespace).Create(&v1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Namespace: namespace, Name: name}})
 | 
				
			||||||
 | 
						if apierrors.IsAlreadyExists(err) {
 | 
				
			||||||
 | 
							// If we're racing to init and someone else already created it, re-fetch
 | 
				
			||||||
 | 
							return coreClient.ServiceAccounts(namespace).Get(name, metav1.GetOptions{})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return sa, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -413,6 +413,7 @@ func ClusterRoles() []rbacv1.ClusterRole {
 | 
				
			|||||||
				rbacv1helpers.NewRule("create").Groups(authorizationGroup).Resources("subjectaccessreviews").RuleOrDie(),
 | 
									rbacv1helpers.NewRule("create").Groups(authorizationGroup).Resources("subjectaccessreviews").RuleOrDie(),
 | 
				
			||||||
				// Needed for all shared informers
 | 
									// Needed for all shared informers
 | 
				
			||||||
				rbacv1helpers.NewRule("list", "watch").Groups("*").Resources("*").RuleOrDie(),
 | 
									rbacv1helpers.NewRule("list", "watch").Groups("*").Resources("*").RuleOrDie(),
 | 
				
			||||||
 | 
									rbacv1helpers.NewRule("create").Groups(legacyGroup).Resources("serviceaccounts/token").RuleOrDie(),
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -681,6 +681,12 @@ items:
 | 
				
			|||||||
    verbs:
 | 
					    verbs:
 | 
				
			||||||
    - list
 | 
					    - list
 | 
				
			||||||
    - watch
 | 
					    - watch
 | 
				
			||||||
 | 
					  - apiGroups:
 | 
				
			||||||
 | 
					    - ""
 | 
				
			||||||
 | 
					    resources:
 | 
				
			||||||
 | 
					    - serviceaccounts/token
 | 
				
			||||||
 | 
					    verbs:
 | 
				
			||||||
 | 
					    - create
 | 
				
			||||||
- apiVersion: rbac.authorization.k8s.io/v1
 | 
					- apiVersion: rbac.authorization.k8s.io/v1
 | 
				
			||||||
  kind: ClusterRole
 | 
					  kind: ClusterRole
 | 
				
			||||||
  metadata:
 | 
					  metadata:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -59,6 +59,15 @@ func NewCachedFileTokenSource(path string) oauth2.TokenSource {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewCachedTokenSource returns a oauth2.TokenSource reads a token from a
 | 
				
			||||||
 | 
					// designed TokenSource. The ts would provide the source of token.
 | 
				
			||||||
 | 
					func NewCachedTokenSource(ts oauth2.TokenSource) oauth2.TokenSource {
 | 
				
			||||||
 | 
						return &cachingTokenSource{
 | 
				
			||||||
 | 
							now:  time.Now,
 | 
				
			||||||
 | 
							base: ts,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type tokenSourceTransport struct {
 | 
					type tokenSourceTransport struct {
 | 
				
			||||||
	base http.RoundTripper
 | 
						base http.RoundTripper
 | 
				
			||||||
	ort  http.RoundTripper
 | 
						ort  http.RoundTripper
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,7 @@ go_test(
 | 
				
			|||||||
        "accessreview_test.go",
 | 
					        "accessreview_test.go",
 | 
				
			||||||
        "auth_test.go",
 | 
					        "auth_test.go",
 | 
				
			||||||
        "bootstraptoken_test.go",
 | 
					        "bootstraptoken_test.go",
 | 
				
			||||||
 | 
					        "dynamic_client_test.go",
 | 
				
			||||||
        "main_test.go",
 | 
					        "main_test.go",
 | 
				
			||||||
        "node_test.go",
 | 
					        "node_test.go",
 | 
				
			||||||
        "rbac_test.go",
 | 
					        "rbac_test.go",
 | 
				
			||||||
@@ -22,6 +23,7 @@ go_test(
 | 
				
			|||||||
    ],
 | 
					    ],
 | 
				
			||||||
    tags = ["integration"],
 | 
					    tags = ["integration"],
 | 
				
			||||||
    deps = [
 | 
					    deps = [
 | 
				
			||||||
 | 
					        "//cmd/kube-apiserver/app/options:go_default_library",
 | 
				
			||||||
        "//cmd/kube-apiserver/app/testing:go_default_library",
 | 
					        "//cmd/kube-apiserver/app/testing:go_default_library",
 | 
				
			||||||
        "//pkg/api/legacyscheme:go_default_library",
 | 
					        "//pkg/api/legacyscheme:go_default_library",
 | 
				
			||||||
        "//pkg/api/testapi:go_default_library",
 | 
					        "//pkg/api/testapi:go_default_library",
 | 
				
			||||||
@@ -34,8 +36,10 @@ go_test(
 | 
				
			|||||||
        "//pkg/apis/rbac:go_default_library",
 | 
					        "//pkg/apis/rbac:go_default_library",
 | 
				
			||||||
        "//pkg/auth/authorizer/abac:go_default_library",
 | 
					        "//pkg/auth/authorizer/abac:go_default_library",
 | 
				
			||||||
        "//pkg/client/clientset_generated/internalclientset:go_default_library",
 | 
					        "//pkg/client/clientset_generated/internalclientset:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/controller:go_default_library",
 | 
				
			||||||
        "//pkg/controller/serviceaccount:go_default_library",
 | 
					        "//pkg/controller/serviceaccount:go_default_library",
 | 
				
			||||||
        "//pkg/features:go_default_library",
 | 
					        "//pkg/features:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/kubeapiserver/options:go_default_library",
 | 
				
			||||||
        "//pkg/master:go_default_library",
 | 
					        "//pkg/master:go_default_library",
 | 
				
			||||||
        "//pkg/registry/rbac/clusterrole:go_default_library",
 | 
					        "//pkg/registry/rbac/clusterrole:go_default_library",
 | 
				
			||||||
        "//pkg/registry/rbac/clusterrole/storage:go_default_library",
 | 
					        "//pkg/registry/rbac/clusterrole/storage:go_default_library",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										130
									
								
								test/integration/auth/dynamic_client_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								test/integration/auth/dynamic_client_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,130 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2019 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 auth
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
 | 
						"k8s.io/apiserver/pkg/authentication/authenticator"
 | 
				
			||||||
 | 
						"k8s.io/apiserver/pkg/authorization/authorizerfactory"
 | 
				
			||||||
 | 
						utilfeature "k8s.io/apiserver/pkg/util/feature"
 | 
				
			||||||
 | 
						utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
 | 
				
			||||||
 | 
						clientset "k8s.io/client-go/kubernetes"
 | 
				
			||||||
 | 
						restclient "k8s.io/client-go/rest"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/controller"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/features"
 | 
				
			||||||
 | 
						kubeoptions "k8s.io/kubernetes/pkg/kubeapiserver/options"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/master"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/test/integration/framework"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDynamicClientBuilder(t *testing.T) {
 | 
				
			||||||
 | 
						defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TokenRequest, true)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tmpfile, err := ioutil.TempFile("/tmp", "key")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("create temp file failed: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer os.RemoveAll(tmpfile.Name())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err = ioutil.WriteFile(tmpfile.Name(), []byte(ecdsaPrivateKey), 0666); err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("write file %s failed: %v", tmpfile.Name(), err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const iss = "https://foo.bar.example.com"
 | 
				
			||||||
 | 
						aud := authenticator.Audiences{"api"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						maxExpirationDuration := time.Second * 60 * 60
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("parse duration failed: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						stopCh := make(chan struct{})
 | 
				
			||||||
 | 
						defer close(stopCh)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						baseClient, baseConfig := framework.StartTestServer(t, stopCh, framework.TestServerSetup{
 | 
				
			||||||
 | 
							ModifyServerRunOptions: func(opts *options.ServerRunOptions) {
 | 
				
			||||||
 | 
								opts.ServiceAccountSigningKeyFile = tmpfile.Name()
 | 
				
			||||||
 | 
								opts.ServiceAccountTokenMaxExpiration = maxExpirationDuration
 | 
				
			||||||
 | 
								if opts.Authentication == nil {
 | 
				
			||||||
 | 
									opts.Authentication = &kubeoptions.BuiltInAuthenticationOptions{}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								opts.Authentication.APIAudiences = aud
 | 
				
			||||||
 | 
								if opts.Authentication.ServiceAccounts == nil {
 | 
				
			||||||
 | 
									opts.Authentication.ServiceAccounts = &kubeoptions.ServiceAccountAuthenticationOptions{}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								opts.Authentication.ServiceAccounts.Issuer = iss
 | 
				
			||||||
 | 
								opts.Authentication.ServiceAccounts.KeyFiles = []string{tmpfile.Name()}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							ModifyServerConfig: func(config *master.Config) {
 | 
				
			||||||
 | 
								config.GenericConfig.Authorization.Authorizer = authorizerfactory.NewAlwaysAllowAuthorizer()
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// We want to test if the token rotation works fine here.
 | 
				
			||||||
 | 
						// To minimize the time this test would consume, we use the minimial token expiration.
 | 
				
			||||||
 | 
						// The minimial token expiration is defined in:
 | 
				
			||||||
 | 
						// pkg/apis/authentication/validation/validation.go
 | 
				
			||||||
 | 
						exp := int64(600)
 | 
				
			||||||
 | 
						leeway := 99
 | 
				
			||||||
 | 
						ns := "default"
 | 
				
			||||||
 | 
						clientBuilder := controller.NewTestDynamicClientBuilder(
 | 
				
			||||||
 | 
							restclient.AnonymousClientConfig(baseConfig),
 | 
				
			||||||
 | 
							baseClient.CoreV1(),
 | 
				
			||||||
 | 
							ns, exp, leeway)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						saName := "dt"
 | 
				
			||||||
 | 
						dymClient, err := clientBuilder.Client(saName)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("build client via dynamic client builder failed: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err = testClientBuilder(dymClient, ns, saName); err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("dynamic client get resources failed befroe deleting sa: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// We want to trigger token rotation here by deleting service account
 | 
				
			||||||
 | 
						// the dynamic client was using.
 | 
				
			||||||
 | 
						if err = dymClient.CoreV1().ServiceAccounts(ns).Delete(saName, nil); err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("delete service account %s failed: %v", saName, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						time.Sleep(time.Second * 10)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err = testClientBuilder(dymClient, ns, saName); err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("dynamic client get resources failed after deleting sa: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func testClientBuilder(dymClient clientset.Interface, ns, saName string) error {
 | 
				
			||||||
 | 
						_, err := dymClient.CoreV1().Namespaces().Get(ns, metav1.GetOptions{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = dymClient.CoreV1().ServiceAccounts(ns).Get(saName, metav1.GetOptions{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user