Commit a552ee61 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #46672 from smarterclayton/initializer_with_config

Automatic merge from submit-queue (batch tested with PRs 46967, 46992, 43338, 46717, 46672) Select initializers from the dynamic configuration Continues #36721 kubernetes/features#209
parents 5ece6470 772ab8e1
...@@ -25,6 +25,7 @@ go_library( ...@@ -25,6 +25,7 @@ go_library(
"//pkg/apis/extensions:go_default_library", "//pkg/apis/extensions:go_default_library",
"//pkg/apis/networking:go_default_library", "//pkg/apis/networking:go_default_library",
"//pkg/capabilities:go_default_library", "//pkg/capabilities:go_default_library",
"//pkg/client/clientset_generated/clientset:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/informers/informers_generated/internalversion:go_default_library", "//pkg/client/informers/informers_generated/internalversion:go_default_library",
"//pkg/cloudprovider:go_default_library", "//pkg/cloudprovider:go_default_library",
......
...@@ -61,6 +61,7 @@ import ( ...@@ -61,6 +61,7 @@ import (
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/networking" "k8s.io/kubernetes/pkg/apis/networking"
"k8s.io/kubernetes/pkg/capabilities" "k8s.io/kubernetes/pkg/capabilities"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion" informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
"k8s.io/kubernetes/pkg/cloudprovider" "k8s.io/kubernetes/pkg/cloudprovider"
...@@ -380,6 +381,10 @@ func BuildGenericConfig(s *options.ServerRunOptions) (*genericapiserver.Config, ...@@ -380,6 +381,10 @@ func BuildGenericConfig(s *options.ServerRunOptions) (*genericapiserver.Config,
// TODO: get rid of KUBE_API_VERSIONS or define sane behaviour if set // TODO: get rid of KUBE_API_VERSIONS or define sane behaviour if set
glog.Errorf("Failed to create clientset with KUBE_API_VERSIONS=%q. KUBE_API_VERSIONS is only for testing. Things will break.", kubeAPIVersions) glog.Errorf("Failed to create clientset with KUBE_API_VERSIONS=%q. KUBE_API_VERSIONS is only for testing. Things will break.", kubeAPIVersions)
} }
externalClient, err := clientset.NewForConfig(genericConfig.LoopbackClientConfig)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to create external clientset: %v", err)
}
sharedInformers := informers.NewSharedInformerFactory(client, 10*time.Minute) sharedInformers := informers.NewSharedInformerFactory(client, 10*time.Minute)
genericConfig.Authenticator, genericConfig.OpenAPIConfig.SecurityDefinitions, err = BuildAuthenticator(s, storageFactory, client, sharedInformers) genericConfig.Authenticator, genericConfig.OpenAPIConfig.SecurityDefinitions, err = BuildAuthenticator(s, storageFactory, client, sharedInformers)
...@@ -398,6 +403,7 @@ func BuildGenericConfig(s *options.ServerRunOptions) (*genericapiserver.Config, ...@@ -398,6 +403,7 @@ func BuildGenericConfig(s *options.ServerRunOptions) (*genericapiserver.Config,
pluginInitializer, err := BuildAdmissionPluginInitializer( pluginInitializer, err := BuildAdmissionPluginInitializer(
s, s,
client, client,
externalClient,
sharedInformers, sharedInformers,
genericConfig.Authorizer, genericConfig.Authorizer,
) )
...@@ -415,7 +421,7 @@ func BuildGenericConfig(s *options.ServerRunOptions) (*genericapiserver.Config, ...@@ -415,7 +421,7 @@ func BuildGenericConfig(s *options.ServerRunOptions) (*genericapiserver.Config,
} }
// BuildAdmissionPluginInitializer constructs the admission plugin initializer // BuildAdmissionPluginInitializer constructs the admission plugin initializer
func BuildAdmissionPluginInitializer(s *options.ServerRunOptions, client internalclientset.Interface, sharedInformers informers.SharedInformerFactory, apiAuthorizer authorizer.Authorizer) (admission.PluginInitializer, error) { func BuildAdmissionPluginInitializer(s *options.ServerRunOptions, client internalclientset.Interface, externalClient clientset.Interface, sharedInformers informers.SharedInformerFactory, apiAuthorizer authorizer.Authorizer) (admission.PluginInitializer, error) {
var cloudConfig []byte var cloudConfig []byte
if s.CloudProvider.CloudConfigFile != "" { if s.CloudProvider.CloudConfigFile != "" {
...@@ -433,7 +439,7 @@ func BuildAdmissionPluginInitializer(s *options.ServerRunOptions, client interna ...@@ -433,7 +439,7 @@ func BuildAdmissionPluginInitializer(s *options.ServerRunOptions, client interna
// do not require us to open watches for all items tracked by quota. // do not require us to open watches for all items tracked by quota.
quotaRegistry := quotainstall.NewRegistry(nil, nil) quotaRegistry := quotainstall.NewRegistry(nil, nil)
pluginInitializer := kubeapiserveradmission.NewPluginInitializer(client, sharedInformers, apiAuthorizer, cloudConfig, restMapper, quotaRegistry) pluginInitializer := kubeapiserveradmission.NewPluginInitializer(client, externalClient, sharedInformers, apiAuthorizer, cloudConfig, restMapper, quotaRegistry)
// Read client cert/key for plugins that need to make calls out // Read client cert/key for plugins that need to make calls out
if len(s.ProxyClientCertFile) > 0 && len(s.ProxyClientKeyFile) > 0 { if len(s.ProxyClientCertFile) > 0 && len(s.ProxyClientKeyFile) > 0 {
......
...@@ -258,7 +258,7 @@ function create-federation-api-objects { ...@@ -258,7 +258,7 @@ function create-federation-api-objects {
export FEDERATION_APISERVER_KEY_BASE64="${FEDERATION_APISERVER_KEY_BASE64}" export FEDERATION_APISERVER_KEY_BASE64="${FEDERATION_APISERVER_KEY_BASE64}"
# Enable the NamespaceLifecycle admission control by default. # Enable the NamespaceLifecycle admission control by default.
export FEDERATION_ADMISSION_CONTROL="${FEDERATION_ADMISSION_CONTROL:-Initializers,NamespaceLifecycle}" export FEDERATION_ADMISSION_CONTROL="${FEDERATION_ADMISSION_CONTROL:-NamespaceLifecycle}"
for file in federation-etcd-pvc.yaml federation-apiserver-{deployment,secrets}.yaml federation-controller-manager-deployment.yaml; do for file in federation-etcd-pvc.yaml federation-apiserver-{deployment,secrets}.yaml federation-controller-manager-deployment.yaml; do
echo "Creating manifest: ${file}" echo "Creating manifest: ${file}"
......
...@@ -41,6 +41,7 @@ go_library( ...@@ -41,6 +41,7 @@ go_library(
"//pkg/apis/extensions:go_default_library", "//pkg/apis/extensions:go_default_library",
"//pkg/apis/extensions/install:go_default_library", "//pkg/apis/extensions/install:go_default_library",
"//pkg/apis/extensions/v1beta1:go_default_library", "//pkg/apis/extensions/v1beta1:go_default_library",
"//pkg/client/clientset_generated/clientset:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/informers/informers_generated/internalversion:go_default_library", "//pkg/client/informers/informers_generated/internalversion:go_default_library",
"//pkg/cloudprovider/providers:go_default_library", "//pkg/cloudprovider/providers:go_default_library",
......
...@@ -42,6 +42,7 @@ import ( ...@@ -42,6 +42,7 @@ import (
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
apiv1 "k8s.io/kubernetes/pkg/api/v1" apiv1 "k8s.io/kubernetes/pkg/api/v1"
extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion" informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
"k8s.io/kubernetes/pkg/generated/openapi" "k8s.io/kubernetes/pkg/generated/openapi"
...@@ -180,6 +181,10 @@ func NonBlockingRun(s *options.ServerRunOptions, stopCh <-chan struct{}) error { ...@@ -180,6 +181,10 @@ func NonBlockingRun(s *options.ServerRunOptions, stopCh <-chan struct{}) error {
if err != nil { if err != nil {
return fmt.Errorf("failed to create clientset: %v", err) return fmt.Errorf("failed to create clientset: %v", err)
} }
externalClient, err := clientset.NewForConfig(genericConfig.LoopbackClientConfig)
if err != nil {
return fmt.Errorf("failed to create external clientset: %v", err)
}
sharedInformers := informers.NewSharedInformerFactory(client, 10*time.Minute) sharedInformers := informers.NewSharedInformerFactory(client, 10*time.Minute)
authorizationConfig := s.Authorization.ToAuthorizationConfig(sharedInformers) authorizationConfig := s.Authorization.ToAuthorizationConfig(sharedInformers)
...@@ -199,7 +204,7 @@ func NonBlockingRun(s *options.ServerRunOptions, stopCh <-chan struct{}) error { ...@@ -199,7 +204,7 @@ func NonBlockingRun(s *options.ServerRunOptions, stopCh <-chan struct{}) error {
// NOTE: we do not provide informers to the quota registry because admission level decisions // NOTE: we do not provide informers to the quota registry because admission level decisions
// do not require us to open watches for all items tracked by quota. // do not require us to open watches for all items tracked by quota.
quotaRegistry := quotainstall.NewRegistry(nil, nil) quotaRegistry := quotainstall.NewRegistry(nil, nil)
pluginInitializer := kubeapiserveradmission.NewPluginInitializer(client, sharedInformers, apiAuthorizer, cloudConfig, nil, quotaRegistry) pluginInitializer := kubeapiserveradmission.NewPluginInitializer(client, externalClient, sharedInformers, apiAuthorizer, cloudConfig, nil, quotaRegistry)
err = s.Admission.ApplyTo( err = s.Admission.ApplyTo(
genericConfig, genericConfig,
......
...@@ -692,7 +692,7 @@ func createAPIServer(clientset client.Interface, namespace, name, federationName ...@@ -692,7 +692,7 @@ func createAPIServer(clientset client.Interface, namespace, name, federationName
"--client-ca-file": "/etc/federation/apiserver/ca.crt", "--client-ca-file": "/etc/federation/apiserver/ca.crt",
"--tls-cert-file": "/etc/federation/apiserver/server.crt", "--tls-cert-file": "/etc/federation/apiserver/server.crt",
"--tls-private-key-file": "/etc/federation/apiserver/server.key", "--tls-private-key-file": "/etc/federation/apiserver/server.key",
"--admission-control": "Initializers,NamespaceLifecycle", "--admission-control": "NamespaceLifecycle",
} }
if advertiseAddress != "" { if advertiseAddress != "" {
......
...@@ -869,7 +869,7 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na ...@@ -869,7 +869,7 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na
fmt.Sprintf("--secure-port=%d", apiServerSecurePort), fmt.Sprintf("--secure-port=%d", apiServerSecurePort),
"--tls-cert-file=/etc/federation/apiserver/server.crt", "--tls-cert-file=/etc/federation/apiserver/server.crt",
"--tls-private-key-file=/etc/federation/apiserver/server.key", "--tls-private-key-file=/etc/federation/apiserver/server.key",
"--admission-control=Initializers,NamespaceLifecycle", "--admission-control=NamespaceLifecycle",
fmt.Sprintf("--advertise-address=%s", address), fmt.Sprintf("--advertise-address=%s", address),
} }
......
...@@ -34,7 +34,7 @@ function run_federation_apiserver() { ...@@ -34,7 +34,7 @@ function run_federation_apiserver() {
kube::log::status "Starting federation-apiserver" kube::log::status "Starting federation-apiserver"
# Admission Controllers to invoke prior to persisting objects in cluster # Admission Controllers to invoke prior to persisting objects in cluster
ADMISSION_CONTROL="Initializers,NamespaceLifecycle" ADMISSION_CONTROL="NamespaceLifecycle"
"${KUBE_OUTPUT_HOSTBIN}/federation-apiserver" \ "${KUBE_OUTPUT_HOSTBIN}/federation-apiserver" \
--insecure-port="${API_PORT}" \ --insecure-port="${API_PORT}" \
......
...@@ -26,6 +26,7 @@ go_library( ...@@ -26,6 +26,7 @@ go_library(
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
"//pkg/apis/admissionregistration:go_default_library", "//pkg/apis/admissionregistration:go_default_library",
"//pkg/client/clientset_generated/clientset:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/informers/informers_generated/internalversion:go_default_library", "//pkg/client/informers/informers_generated/internalversion:go_default_library",
"//pkg/quota:go_default_library", "//pkg/quota:go_default_library",
......
...@@ -29,6 +29,8 @@ go_library( ...@@ -29,6 +29,8 @@ go_library(
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
"//pkg/apis/admissionregistration/v1alpha1:go_default_library", "//pkg/apis/admissionregistration/v1alpha1:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
......
...@@ -30,6 +30,11 @@ const ( ...@@ -30,6 +30,11 @@ const (
defaultFailureThreshold = 5 defaultFailureThreshold = 5
) )
var (
ErrNotReady = fmt.Errorf("configuration is not ready")
ErrDisabled = fmt.Errorf("disabled")
)
type getFunc func() (runtime.Object, error) type getFunc func() (runtime.Object, error)
// When running, poller calls `get` every `interval`. If `get` is // When running, poller calls `get` every `interval`. If `get` is
...@@ -52,7 +57,8 @@ type poller struct { ...@@ -52,7 +57,8 @@ type poller struct {
ready bool ready bool
mergedConfiguration runtime.Object mergedConfiguration runtime.Object
// lock much be hold when reading ready or mergedConfiguration // lock much be hold when reading ready or mergedConfiguration
lock sync.RWMutex lock sync.RWMutex
lastErr error
} }
func newPoller(get getFunc) *poller { func newPoller(get getFunc) *poller {
...@@ -63,6 +69,12 @@ func newPoller(get getFunc) *poller { ...@@ -63,6 +69,12 @@ func newPoller(get getFunc) *poller {
} }
} }
func (a *poller) lastError(err error) {
a.lock.Lock()
defer a.lock.Unlock()
a.lastErr = err
}
func (a *poller) notReady() { func (a *poller) notReady() {
a.lock.Lock() a.lock.Lock()
defer a.lock.Unlock() defer a.lock.Unlock()
...@@ -73,7 +85,10 @@ func (a *poller) configuration() (runtime.Object, error) { ...@@ -73,7 +85,10 @@ func (a *poller) configuration() (runtime.Object, error) {
a.lock.RLock() a.lock.RLock()
defer a.lock.RUnlock() defer a.lock.RUnlock()
if !a.ready { if !a.ready {
return nil, fmt.Errorf("configuration is not ready") if a.lastErr != nil {
return nil, a.lastErr
}
return nil, ErrNotReady
} }
return a.mergedConfiguration, nil return a.mergedConfiguration, nil
} }
...@@ -83,6 +98,7 @@ func (a *poller) setConfigurationAndReady(value runtime.Object) { ...@@ -83,6 +98,7 @@ func (a *poller) setConfigurationAndReady(value runtime.Object) {
defer a.lock.Unlock() defer a.lock.Unlock()
a.mergedConfiguration = value a.mergedConfiguration = value
a.ready = true a.ready = true
a.lastErr = nil
} }
func (a *poller) Run(stopCh <-chan struct{}) { func (a *poller) Run(stopCh <-chan struct{}) {
...@@ -93,6 +109,7 @@ func (a *poller) sync() { ...@@ -93,6 +109,7 @@ func (a *poller) sync() {
configuration, err := a.get() configuration, err := a.get()
if err != nil { if err != nil {
a.failures++ a.failures++
a.lastError(err)
if a.failures >= a.failureThreshold { if a.failures >= a.failureThreshold {
a.notReady() a.notReady()
} }
......
...@@ -33,6 +33,20 @@ type ExternalAdmissionHookConfigurationManager struct { ...@@ -33,6 +33,20 @@ type ExternalAdmissionHookConfigurationManager struct {
*poller *poller
} }
func NewExternalAdmissionHookConfigurationManager(c ExternalAdmissionHookConfigurationLister) *ExternalAdmissionHookConfigurationManager {
getFn := func() (runtime.Object, error) {
list, err := c.List(metav1.ListOptions{})
if err != nil {
return nil, err
}
return mergeExternalAdmissionHookConfigurations(list), nil
}
return &ExternalAdmissionHookConfigurationManager{
newPoller(getFn),
}
}
// ExternalAdmissionHooks returns the merged ExternalAdmissionHookConfiguration. // ExternalAdmissionHooks returns the merged ExternalAdmissionHookConfiguration.
func (im *ExternalAdmissionHookConfigurationManager) ExternalAdmissionHooks() (*v1alpha1.ExternalAdmissionHookConfiguration, error) { func (im *ExternalAdmissionHookConfigurationManager) ExternalAdmissionHooks() (*v1alpha1.ExternalAdmissionHookConfiguration, error) {
configuration, err := im.poller.configuration() configuration, err := im.poller.configuration()
...@@ -46,17 +60,8 @@ func (im *ExternalAdmissionHookConfigurationManager) ExternalAdmissionHooks() (* ...@@ -46,17 +60,8 @@ func (im *ExternalAdmissionHookConfigurationManager) ExternalAdmissionHooks() (*
return externalAdmissionHookConfiguration, nil return externalAdmissionHookConfiguration, nil
} }
func NewExternalAdmissionHookConfigurationManager(c ExternalAdmissionHookConfigurationLister) *ExternalAdmissionHookConfigurationManager { func (im *ExternalAdmissionHookConfigurationManager) Run(stopCh <-chan struct{}) {
getFn := func() (runtime.Object, error) { im.poller.Run(stopCh)
list, err := c.List(metav1.ListOptions{})
if err != nil {
return nil, err
}
return mergeExternalAdmissionHookConfigurations(list), nil
}
return &ExternalAdmissionHookConfigurationManager{
newPoller(getFn)}
} }
func mergeExternalAdmissionHookConfigurations( func mergeExternalAdmissionHookConfigurations(
......
...@@ -21,6 +21,9 @@ import ( ...@@ -21,6 +21,9 @@ import (
"reflect" "reflect"
"sort" "sort"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1" "k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1"
...@@ -34,6 +37,23 @@ type InitializerConfigurationManager struct { ...@@ -34,6 +37,23 @@ type InitializerConfigurationManager struct {
*poller *poller
} }
func NewInitializerConfigurationManager(c InitializerConfigurationLister) *InitializerConfigurationManager {
getFn := func() (runtime.Object, error) {
list, err := c.List(metav1.ListOptions{})
if err != nil {
if errors.IsNotFound(err) || errors.IsForbidden(err) {
glog.V(5).Infof("Initializers are disabled due to an error: %v", err)
return nil, ErrDisabled
}
return nil, err
}
return mergeInitializerConfigurations(list), nil
}
return &InitializerConfigurationManager{
newPoller(getFn),
}
}
// Initializers returns the merged InitializerConfiguration. // Initializers returns the merged InitializerConfiguration.
func (im *InitializerConfigurationManager) Initializers() (*v1alpha1.InitializerConfiguration, error) { func (im *InitializerConfigurationManager) Initializers() (*v1alpha1.InitializerConfiguration, error) {
configuration, err := im.poller.configuration() configuration, err := im.poller.configuration()
...@@ -47,16 +67,8 @@ func (im *InitializerConfigurationManager) Initializers() (*v1alpha1.Initializer ...@@ -47,16 +67,8 @@ func (im *InitializerConfigurationManager) Initializers() (*v1alpha1.Initializer
return initializerConfiguration, nil return initializerConfiguration, nil
} }
func NewInitializerConfigurationManager(c InitializerConfigurationLister) *InitializerConfigurationManager { func (im *InitializerConfigurationManager) Run(stopCh <-chan struct{}) {
getFn := func() (runtime.Object, error) { im.poller.Run(stopCh)
list, err := c.List(metav1.ListOptions{})
if err != nil {
return nil, err
}
return mergeInitializerConfigurations(list), nil
}
return &InitializerConfigurationManager{
newPoller(getFn)}
} }
func mergeInitializerConfigurations(initializerConfigurationList *v1alpha1.InitializerConfigurationList) *v1alpha1.InitializerConfiguration { func mergeInitializerConfigurations(initializerConfigurationList *v1alpha1.InitializerConfigurationList) *v1alpha1.InitializerConfiguration {
......
...@@ -57,7 +57,7 @@ var _ WantsAuthorizer = &WantAuthorizerAdmission{} ...@@ -57,7 +57,7 @@ var _ WantsAuthorizer = &WantAuthorizerAdmission{}
// TestWantsAuthorizer ensures that the authorizer is injected when the WantsAuthorizer // TestWantsAuthorizer ensures that the authorizer is injected when the WantsAuthorizer
// interface is implemented. // interface is implemented.
func TestWantsAuthorizer(t *testing.T) { func TestWantsAuthorizer(t *testing.T) {
initializer := NewPluginInitializer(nil, nil, &TestAuthorizer{}, nil, nil, nil) initializer := NewPluginInitializer(nil, nil, nil, &TestAuthorizer{}, nil, nil, nil)
wantAuthorizerAdmission := &WantAuthorizerAdmission{} wantAuthorizerAdmission := &WantAuthorizerAdmission{}
initializer.Initialize(wantAuthorizerAdmission) initializer.Initialize(wantAuthorizerAdmission)
if wantAuthorizerAdmission.auth == nil { if wantAuthorizerAdmission.auth == nil {
...@@ -76,7 +76,7 @@ func (self *WantsCloudConfigAdmissionPlugin) SetCloudConfig(cloudConfig []byte) ...@@ -76,7 +76,7 @@ func (self *WantsCloudConfigAdmissionPlugin) SetCloudConfig(cloudConfig []byte)
func TestCloudConfigAdmissionPlugin(t *testing.T) { func TestCloudConfigAdmissionPlugin(t *testing.T) {
cloudConfig := []byte("cloud-configuration") cloudConfig := []byte("cloud-configuration")
initializer := NewPluginInitializer(nil, nil, &TestAuthorizer{}, cloudConfig, nil, nil) initializer := NewPluginInitializer(nil, nil, nil, &TestAuthorizer{}, cloudConfig, nil, nil)
wantsCloudConfigAdmission := &WantsCloudConfigAdmissionPlugin{} wantsCloudConfigAdmission := &WantsCloudConfigAdmissionPlugin{}
initializer.Initialize(wantsCloudConfigAdmission) initializer.Initialize(wantsCloudConfigAdmission)
......
...@@ -23,6 +23,7 @@ import ( ...@@ -23,6 +23,7 @@ import (
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/authorization/authorizer" "k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/kubernetes/pkg/apis/admissionregistration" "k8s.io/kubernetes/pkg/apis/admissionregistration"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion" informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
"k8s.io/kubernetes/pkg/quota" "k8s.io/kubernetes/pkg/quota"
...@@ -36,6 +37,12 @@ type WantsInternalKubeClientSet interface { ...@@ -36,6 +37,12 @@ type WantsInternalKubeClientSet interface {
admission.Validator admission.Validator
} }
// WantsExternalKubeClientSet defines a function which sets ClientSet for admission plugins that need it
type WantsExternalKubeClientSet interface {
SetExternalKubeClientSet(clientset.Interface)
admission.Validator
}
// WantsInternalKubeInformerFactory defines a function which sets InformerFactory for admission plugins that need it // WantsInternalKubeInformerFactory defines a function which sets InformerFactory for admission plugins that need it
type WantsInternalKubeInformerFactory interface { type WantsInternalKubeInformerFactory interface {
SetInternalKubeInformerFactory(informers.SharedInformerFactory) SetInternalKubeInformerFactory(informers.SharedInformerFactory)
...@@ -95,6 +102,7 @@ type WebhookSource interface { ...@@ -95,6 +102,7 @@ type WebhookSource interface {
type PluginInitializer struct { type PluginInitializer struct {
internalClient internalclientset.Interface internalClient internalclientset.Interface
externalClient clientset.Interface
informers informers.SharedInformerFactory informers informers.SharedInformerFactory
authorizer authorizer.Authorizer authorizer authorizer.Authorizer
cloudConfig []byte cloudConfig []byte
...@@ -113,14 +121,18 @@ var _ admission.PluginInitializer = &PluginInitializer{} ...@@ -113,14 +121,18 @@ var _ admission.PluginInitializer = &PluginInitializer{}
// NewPluginInitializer constructs new instance of PluginInitializer // NewPluginInitializer constructs new instance of PluginInitializer
// TODO: switch these parameters to use the builder pattern or just make them // TODO: switch these parameters to use the builder pattern or just make them
// all public, this construction method is pointless boilerplate. // all public, this construction method is pointless boilerplate.
func NewPluginInitializer(internalClient internalclientset.Interface, func NewPluginInitializer(
internalClient internalclientset.Interface,
externalClient clientset.Interface,
sharedInformers informers.SharedInformerFactory, sharedInformers informers.SharedInformerFactory,
authz authorizer.Authorizer, authz authorizer.Authorizer,
cloudConfig []byte, cloudConfig []byte,
restMapper meta.RESTMapper, restMapper meta.RESTMapper,
quotaRegistry quota.Registry) *PluginInitializer { quotaRegistry quota.Registry,
) *PluginInitializer {
return &PluginInitializer{ return &PluginInitializer{
internalClient: internalClient, internalClient: internalClient,
externalClient: externalClient,
informers: sharedInformers, informers: sharedInformers,
authorizer: authz, authorizer: authz,
cloudConfig: cloudConfig, cloudConfig: cloudConfig,
...@@ -157,6 +169,10 @@ func (i *PluginInitializer) Initialize(plugin admission.Interface) { ...@@ -157,6 +169,10 @@ func (i *PluginInitializer) Initialize(plugin admission.Interface) {
wants.SetInternalKubeClientSet(i.internalClient) wants.SetInternalKubeClientSet(i.internalClient)
} }
if wants, ok := plugin.(WantsExternalKubeClientSet); ok {
wants.SetExternalKubeClientSet(i.externalClient)
}
if wants, ok := plugin.(WantsInternalKubeInformerFactory); ok { if wants, ok := plugin.(WantsInternalKubeInformerFactory); ok {
wants.SetInternalKubeInformerFactory(i.informers) wants.SetInternalKubeInformerFactory(i.informers)
} }
......
...@@ -74,7 +74,7 @@ func newGCPermissionsEnforcement() *gcPermissionsEnforcement { ...@@ -74,7 +74,7 @@ func newGCPermissionsEnforcement() *gcPermissionsEnforcement {
Handler: admission.NewHandler(admission.Create, admission.Update), Handler: admission.NewHandler(admission.Create, admission.Update),
whiteList: whiteList, whiteList: whiteList,
} }
pluginInitializer := kubeadmission.NewPluginInitializer(nil, nil, fakeAuthorizer{}, nil, api.Registry.RESTMapper(), nil) pluginInitializer := kubeadmission.NewPluginInitializer(nil, nil, nil, fakeAuthorizer{}, nil, api.Registry.RESTMapper(), nil)
pluginInitializer.Initialize(gcAdmit) pluginInitializer.Initialize(gcAdmit)
return gcAdmit return gcAdmit
} }
......
...@@ -5,6 +5,7 @@ licenses(["notice"]) ...@@ -5,6 +5,7 @@ licenses(["notice"])
load( load(
"@io_bazel_rules_go//go:def.bzl", "@io_bazel_rules_go//go:def.bzl",
"go_library", "go_library",
"go_test",
) )
go_library( go_library(
...@@ -12,6 +13,10 @@ go_library( ...@@ -12,6 +13,10 @@ go_library(
srcs = ["initialization.go"], srcs = ["initialization.go"],
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
"//pkg/api:go_default_library",
"//pkg/apis/admissionregistration/v1alpha1:go_default_library",
"//pkg/client/clientset_generated/clientset:go_default_library",
"//pkg/kubeapiserver/admission/configuration:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
...@@ -19,6 +24,7 @@ go_library( ...@@ -19,6 +24,7 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library", "//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library", "//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
], ],
...@@ -36,3 +42,14 @@ filegroup( ...@@ -36,3 +42,14 @@ filegroup(
srcs = [":package-srcs"], srcs = [":package-srcs"],
tags = ["automanaged"], tags = ["automanaged"],
) )
go_test(
name = "go_default_test",
srcs = ["initialization_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/apis/admissionregistration/v1alpha1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
],
)
/*
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 initialization
import (
"reflect"
"testing"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1"
)
func newInitializer(name string, rules ...v1alpha1.Rule) *v1alpha1.InitializerConfiguration {
return addInitializer(&v1alpha1.InitializerConfiguration{}, name, rules...)
}
func addInitializer(base *v1alpha1.InitializerConfiguration, name string, rules ...v1alpha1.Rule) *v1alpha1.InitializerConfiguration {
base.Initializers = append(base.Initializers, v1alpha1.Initializer{
Name: name,
Rules: rules,
})
return base
}
func TestFindInitializers(t *testing.T) {
type args struct {
initializers *v1alpha1.InitializerConfiguration
gvr schema.GroupVersionResource
}
tests := []struct {
name string
args args
want []string
}{
{
name: "empty",
args: args{
gvr: schema.GroupVersionResource{},
initializers: newInitializer("1"),
},
},
{
name: "everything",
args: args{
gvr: schema.GroupVersionResource{},
initializers: newInitializer("1", v1alpha1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"*"}}),
},
want: []string{"1"},
},
{
name: "empty group",
args: args{
gvr: schema.GroupVersionResource{},
initializers: newInitializer("1", v1alpha1.Rule{APIGroups: []string{""}, APIVersions: []string{"*"}, Resources: []string{"*"}}),
},
want: []string{"1"},
},
{
name: "pod",
args: args{
gvr: schema.GroupVersionResource{Resource: "pods"},
initializers: addInitializer(
newInitializer("1", v1alpha1.Rule{APIGroups: []string{""}, APIVersions: []string{"*"}, Resources: []string{"pods"}}),
"2", v1alpha1.Rule{APIGroups: []string{""}, APIVersions: []string{"*"}, Resources: []string{"pods"}},
),
},
want: []string{"1", "2"},
},
{
name: "multiple matches",
args: args{
gvr: schema.GroupVersionResource{Resource: "pods"},
initializers: newInitializer("1", v1alpha1.Rule{APIGroups: []string{""}, APIVersions: []string{"*"}, Resources: []string{"pods"}}),
},
want: []string{"1"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := findInitializers(tt.args.initializers, tt.args.gvr); !reflect.DeepEqual(got, tt.want) {
t.Errorf("findInitializers() = %v, want %v", got, tt.want)
}
})
}
}
...@@ -595,7 +595,7 @@ func newHandlerForTest(c clientset.Interface) (admission.Interface, informers.Sh ...@@ -595,7 +595,7 @@ func newHandlerForTest(c clientset.Interface) (admission.Interface, informers.Sh
if err != nil { if err != nil {
return nil, f, err return nil, f, err
} }
pluginInitializer := kubeadmission.NewPluginInitializer(c, f, nil, nil, nil, nil) pluginInitializer := kubeadmission.NewPluginInitializer(c, nil, f, nil, nil, nil, nil)
pluginInitializer.Initialize(handler) pluginInitializer.Initialize(handler)
err = admission.Validate(handler) err = admission.Validate(handler)
return handler, f, err return handler, f, err
......
...@@ -38,7 +38,7 @@ import ( ...@@ -38,7 +38,7 @@ import (
func newHandlerForTest(c clientset.Interface) (admission.Interface, informers.SharedInformerFactory, error) { func newHandlerForTest(c clientset.Interface) (admission.Interface, informers.SharedInformerFactory, error) {
f := informers.NewSharedInformerFactory(c, 5*time.Minute) f := informers.NewSharedInformerFactory(c, 5*time.Minute)
handler := NewProvision() handler := NewProvision()
pluginInitializer := kubeadmission.NewPluginInitializer(c, f, nil, nil, nil, nil) pluginInitializer := kubeadmission.NewPluginInitializer(c, nil, f, nil, nil, nil, nil)
pluginInitializer.Initialize(handler) pluginInitializer.Initialize(handler)
err := admission.Validate(handler) err := admission.Validate(handler)
return handler, f, err return handler, f, err
......
...@@ -37,7 +37,7 @@ import ( ...@@ -37,7 +37,7 @@ import (
func newHandlerForTest(c clientset.Interface) (admission.Interface, informers.SharedInformerFactory, error) { func newHandlerForTest(c clientset.Interface) (admission.Interface, informers.SharedInformerFactory, error) {
f := informers.NewSharedInformerFactory(c, 5*time.Minute) f := informers.NewSharedInformerFactory(c, 5*time.Minute)
handler := NewExists() handler := NewExists()
pluginInitializer := kubeadmission.NewPluginInitializer(c, f, nil, nil, nil, nil) pluginInitializer := kubeadmission.NewPluginInitializer(c, nil, f, nil, nil, nil, nil)
pluginInitializer.Initialize(handler) pluginInitializer.Initialize(handler)
err := admission.Validate(handler) err := admission.Validate(handler)
return handler, f, err return handler, f, err
......
...@@ -191,7 +191,7 @@ func TestHandles(t *testing.T) { ...@@ -191,7 +191,7 @@ func TestHandles(t *testing.T) {
func newHandlerForTest(c clientset.Interface) (*podNodeSelector, informers.SharedInformerFactory, error) { func newHandlerForTest(c clientset.Interface) (*podNodeSelector, informers.SharedInformerFactory, error) {
f := informers.NewSharedInformerFactory(c, 5*time.Minute) f := informers.NewSharedInformerFactory(c, 5*time.Minute)
handler := NewPodNodeSelector(nil) handler := NewPodNodeSelector(nil)
pluginInitializer := kubeadmission.NewPluginInitializer(c, f, nil, nil, nil, nil) pluginInitializer := kubeadmission.NewPluginInitializer(c, nil, f, nil, nil, nil, nil)
pluginInitializer.Initialize(handler) pluginInitializer.Initialize(handler)
err := admission.Validate(handler) err := admission.Validate(handler)
return handler, f, err return handler, f, err
......
...@@ -193,7 +193,7 @@ func newHandlerForTest(c clientset.Interface) (*podTolerationsPlugin, informers. ...@@ -193,7 +193,7 @@ func newHandlerForTest(c clientset.Interface) (*podTolerationsPlugin, informers.
return nil, nil, err return nil, nil, err
} }
handler := NewPodTolerationsPlugin(pluginConfig) handler := NewPodTolerationsPlugin(pluginConfig)
pluginInitializer := kubeadmission.NewPluginInitializer(c, f, nil, nil, nil, nil) pluginInitializer := kubeadmission.NewPluginInitializer(c, nil, f, nil, nil, nil, nil)
pluginInitializer.Initialize(handler) pluginInitializer.Initialize(handler)
err = admission.Validate(handler) err = admission.Validate(handler)
return handler, f, err return handler, f, err
......
...@@ -41,7 +41,7 @@ type AdmissionOptions struct { ...@@ -41,7 +41,7 @@ type AdmissionOptions struct {
func NewAdmissionOptions() *AdmissionOptions { func NewAdmissionOptions() *AdmissionOptions {
options := &AdmissionOptions{ options := &AdmissionOptions{
Plugins: &admission.Plugins{}, Plugins: &admission.Plugins{},
PluginNames: []string{"Initializers"}, PluginNames: []string{},
} }
server.RegisterAllAdmissionPlugins(options.Plugins) server.RegisterAllAdmissionPlugins(options.Plugins)
return options return options
......
...@@ -13,6 +13,9 @@ go_library( ...@@ -13,6 +13,9 @@ go_library(
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
"//pkg/api/v1:go_default_library", "//pkg/api/v1:go_default_library",
"//pkg/apis/admissionregistration/v1alpha1:go_default_library",
"//pkg/client/clientset_generated/clientset:go_default_library",
"//pkg/client/retry:go_default_library",
"//test/e2e/framework:go_default_library", "//test/e2e/framework:go_default_library",
"//vendor/github.com/onsi/ginkgo:go_default_library", "//vendor/github.com/onsi/ginkgo:go_default_library",
"//vendor/github.com/onsi/gomega:go_default_library", "//vendor/github.com/onsi/gomega:go_default_library",
......
...@@ -23,10 +23,14 @@ import ( ...@@ -23,10 +23,14 @@ import (
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
clientretry "k8s.io/kubernetes/pkg/client/retry"
"k8s.io/kubernetes/test/e2e/framework" "k8s.io/kubernetes/test/e2e/framework"
) )
...@@ -65,10 +69,19 @@ var _ = framework.KubeDescribe("Initializers", func() { ...@@ -65,10 +69,19 @@ var _ = framework.KubeDescribe("Initializers", func() {
// verify that we can update an initializing pod // verify that we can update an initializing pod
pod, err := c.Core().Pods(ns).Get(podName, metav1.GetOptions{}) pod, err := c.Core().Pods(ns).Get(podName, metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred())
pod.Annotations = map[string]string{"update-1": "test"} pod.Annotations = map[string]string{"update-1": "test"}
pod, err = c.Core().Pods(ns).Update(pod) pod, err = c.Core().Pods(ns).Update(pod)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
// verify the list call filters out uninitialized pods
pods, err := c.Core().Pods(ns).List(metav1.ListOptions{IncludeUninitialized: true})
Expect(err).NotTo(HaveOccurred())
Expect(pods.Items).To(HaveLen(1))
pods, err = c.Core().Pods(ns).List(metav1.ListOptions{})
Expect(err).NotTo(HaveOccurred())
Expect(pods.Items).To(HaveLen(0))
// clear initializers // clear initializers
pod.Initializers = nil pod.Initializers = nil
pod, err = c.Core().Pods(ns).Update(pod) pod, err = c.Core().Pods(ns).Update(pod)
...@@ -93,17 +106,120 @@ var _ = framework.KubeDescribe("Initializers", func() { ...@@ -93,17 +106,120 @@ var _ = framework.KubeDescribe("Initializers", func() {
} }
}) })
It("should dynamically register and apply initializers to pods [Serial]", func() {
ns := f.Namespace.Name
c := f.ClientSet
podName := "uninitialized-pod"
framework.Logf("Creating pod %s", podName)
// create and register an initializer
initializerName := "pod.test.e2e.kubernetes.io"
initializerConfigName := "e2e-test-initializer"
_, err := c.AdmissionregistrationV1alpha1().InitializerConfigurations().Create(&v1alpha1.InitializerConfiguration{
ObjectMeta: metav1.ObjectMeta{Name: initializerConfigName},
Initializers: []v1alpha1.Initializer{
{
Name: initializerName,
Rules: []v1alpha1.Rule{
{APIGroups: []string{""}, APIVersions: []string{"*"}, Resources: []string{"pods"}},
},
},
},
})
Expect(err).NotTo(HaveOccurred())
// we must remove the initializer when the test is complete and ensure no pods are pending for that initializer
defer func() {
if err := c.AdmissionregistrationV1alpha1().InitializerConfigurations().Delete(initializerConfigName, nil); err != nil && !errors.IsNotFound(err) {
framework.Logf("got error on deleting %s", initializerConfigName)
}
// poller configuration is 1 second, wait at least that long
time.Sleep(3 * time.Second)
// clear our initializer from anyone who got it
removeInitializersFromAllPods(c, initializerName)
}()
// poller configuration is 1 second, wait at least that long
time.Sleep(3 * time.Second)
// run create that blocks
ch := make(chan struct{})
go func() {
defer close(ch)
_, err := c.Core().Pods(ns).Create(newPod(podName))
Expect(err).NotTo(HaveOccurred())
}()
// wait until the pod shows up uninitialized
By("Waiting until the pod is visible to a client")
var pod *v1.Pod
err = wait.PollImmediate(2*time.Second, 15*time.Second, func() (bool, error) {
pod, err = c.Core().Pods(ns).Get(podName, metav1.GetOptions{IncludeUninitialized: true})
if errors.IsNotFound(err) {
return false, nil
}
if err != nil {
return false, err
}
return true, nil
})
Expect(err).NotTo(HaveOccurred())
Expect(pod.Initializers).NotTo(BeNil())
Expect(pod.Initializers.Pending).To(HaveLen(1))
Expect(pod.Initializers.Pending[0].Name).To(Equal(initializerName))
// pretend we are an initializer
By("Completing initialization")
pod.Initializers = nil
pod, err = c.Core().Pods(ns).Update(pod)
Expect(err).NotTo(HaveOccurred())
// ensure create call returns
<-ch
// pod should now start running
err = framework.WaitForPodRunningInNamespace(c, pod)
Expect(err).NotTo(HaveOccurred())
// bypass initialization by explicitly passing an empty pending list
By("Setting an empty initializer as an admin to bypass initialization")
podName = "preinitialized-pod"
pod = newUninitializedPod(podName)
pod.Initializers.Pending = nil
pod, err = c.Core().Pods(ns).Create(pod)
Expect(err).NotTo(HaveOccurred())
Expect(pod.Initializers).To(BeNil())
// bypass initialization for mirror pods
By("Creating a mirror pod that bypasses initialization")
podName = "mirror-pod"
pod = newPod(podName)
pod.Annotations = map[string]string{
v1.MirrorPodAnnotationKey: "true",
}
pod.Spec.NodeName = "node-does-not-yet-exist"
pod, err = c.Core().Pods(ns).Create(pod)
Expect(err).NotTo(HaveOccurred())
Expect(pod.Initializers).To(BeNil())
Expect(pod.Annotations[v1.MirrorPodAnnotationKey]).To(Equal("true"))
})
}) })
func newUninitializedPod(podName string) *v1.Pod { func newUninitializedPod(podName string) *v1.Pod {
pod := newPod(podName)
pod.Initializers = &metav1.Initializers{
Pending: []metav1.Initializer{{Name: "Test"}},
}
return pod
}
func newPod(podName string) *v1.Pod {
containerName := fmt.Sprintf("%s-container", podName) containerName := fmt.Sprintf("%s-container", podName)
port := 8080 port := 8080
pod := &v1.Pod{ pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: podName, Name: podName,
Initializers: &metav1.Initializers{
Pending: []metav1.Initializer{{Name: "Test"}},
},
}, },
Spec: v1.PodSpec{ Spec: v1.PodSpec{
Containers: []v1.Container{ Containers: []v1.Container{
...@@ -119,3 +235,48 @@ func newUninitializedPod(podName string) *v1.Pod { ...@@ -119,3 +235,48 @@ func newUninitializedPod(podName string) *v1.Pod {
} }
return pod return pod
} }
// removeInitializersFromAllPods walks all pods and ensures they don't have the provided initializer,
// to guarantee completing the test doesn't block the entire cluster.
func removeInitializersFromAllPods(c clientset.Interface, initializerName string) {
pods, err := c.Core().Pods("").List(metav1.ListOptions{IncludeUninitialized: true})
if err != nil {
return
}
for _, p := range pods.Items {
if p.Initializers == nil {
continue
}
err := clientretry.RetryOnConflict(clientretry.DefaultRetry, func() error {
pod, err := c.Core().Pods(p.Namespace).Get(p.Name, metav1.GetOptions{IncludeUninitialized: true})
if err != nil {
if errors.IsNotFound(err) {
return nil
}
return err
}
if pod.Initializers == nil {
return nil
}
var updated []metav1.Initializer
for _, pending := range pod.Initializers.Pending {
if pending.Name != initializerName {
updated = append(updated, pending)
}
}
if len(updated) == len(pod.Initializers.Pending) {
return nil
}
pod.Initializers.Pending = updated
if len(updated) == 0 {
pod.Initializers = nil
}
framework.Logf("Found initializer on pod %s in ns %s", pod.Name, pod.Namespace)
_, err = c.Core().Pods(p.Namespace).Update(pod)
return err
})
if err != nil {
framework.Logf("Unable to remove initializer from pod %s in ns %s: %v", p.Name, p.Namespace, err)
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment