k8s.io/apiserver@v0.31.1/pkg/admission/configuration/configuration_manager.go (about)

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package configuration
    18  
    19  import (
    20  	"fmt"
    21  	"sync"
    22  	"time"
    23  
    24  	"k8s.io/apimachinery/pkg/runtime"
    25  	"k8s.io/apimachinery/pkg/util/wait"
    26  )
    27  
    28  const (
    29  	defaultInterval             = 1 * time.Second
    30  	defaultFailureThreshold     = 5
    31  	defaultBootstrapRetries     = 5
    32  	defaultBootstrapGraceperiod = 5 * time.Second
    33  )
    34  
    35  var (
    36  	ErrNotReady = fmt.Errorf("configuration is not ready")
    37  	ErrDisabled = fmt.Errorf("disabled")
    38  )
    39  
    40  type getFunc func() (runtime.Object, error)
    41  
    42  // When running, poller calls `get` every `interval`. If `get` is
    43  // successful, `Ready()` returns ready and `configuration()` returns the
    44  // `mergedConfiguration`; if `get` has failed more than `failureThreshold ` times,
    45  // `Ready()` returns not ready and `configuration()` returns nil configuration.
    46  // In an HA setup, the poller is consistent only if the `get` is
    47  // doing consistent read.
    48  type poller struct {
    49  	// a function to consistently read the latest configuration
    50  	get getFunc
    51  	// consistent read interval
    52  	// read-only
    53  	interval time.Duration
    54  	// if the number of consecutive read failure equals or exceeds the failureThreshold , the
    55  	// configuration is regarded as not ready.
    56  	// read-only
    57  	failureThreshold int
    58  	// number of consecutive failures so far.
    59  	failures int
    60  	// If the poller has passed the bootstrap phase. The poller is considered
    61  	// bootstrapped either bootstrapGracePeriod after the first call of
    62  	// configuration(), or when setConfigurationAndReady() is called, whichever
    63  	// comes first.
    64  	bootstrapped bool
    65  	// configuration() retries bootstrapRetries times if poller is not bootstrapped
    66  	// read-only
    67  	bootstrapRetries int
    68  	// Grace period for bootstrapping
    69  	// read-only
    70  	bootstrapGracePeriod time.Duration
    71  	once                 sync.Once
    72  	// if the configuration is regarded as ready.
    73  	ready               bool
    74  	mergedConfiguration runtime.Object
    75  	lastErr             error
    76  	// lock must be hold when reading/writing the data fields of poller.
    77  	lock sync.RWMutex
    78  }
    79  
    80  func newPoller(get getFunc) *poller {
    81  	p := poller{
    82  		get:                  get,
    83  		interval:             defaultInterval,
    84  		failureThreshold:     defaultFailureThreshold,
    85  		bootstrapRetries:     defaultBootstrapRetries,
    86  		bootstrapGracePeriod: defaultBootstrapGraceperiod,
    87  	}
    88  	return &p
    89  }
    90  
    91  func (a *poller) lastError(err error) {
    92  	a.lock.Lock()
    93  	defer a.lock.Unlock()
    94  	a.lastErr = err
    95  }
    96  
    97  func (a *poller) notReady() {
    98  	a.lock.Lock()
    99  	defer a.lock.Unlock()
   100  	a.ready = false
   101  }
   102  
   103  func (a *poller) bootstrapping() {
   104  	// bootstrapGracePeriod is read-only, so no lock is required
   105  	timer := time.NewTimer(a.bootstrapGracePeriod)
   106  	go func() {
   107  		defer timer.Stop()
   108  		<-timer.C
   109  		a.lock.Lock()
   110  		defer a.lock.Unlock()
   111  		a.bootstrapped = true
   112  	}()
   113  }
   114  
   115  // If the poller is not bootstrapped yet, the configuration() gets a few chances
   116  // to retry. This hides transient failures during system startup.
   117  func (a *poller) configuration() (runtime.Object, error) {
   118  	a.once.Do(a.bootstrapping)
   119  	a.lock.RLock()
   120  	defer a.lock.RUnlock()
   121  	retries := 1
   122  	if !a.bootstrapped {
   123  		retries = a.bootstrapRetries
   124  	}
   125  	for count := 0; count < retries; count++ {
   126  		if count > 0 {
   127  			a.lock.RUnlock()
   128  			time.Sleep(a.interval)
   129  			a.lock.RLock()
   130  		}
   131  		if a.ready {
   132  			return a.mergedConfiguration, nil
   133  		}
   134  	}
   135  	if a.lastErr != nil {
   136  		return nil, a.lastErr
   137  	}
   138  	return nil, ErrNotReady
   139  }
   140  
   141  func (a *poller) setConfigurationAndReady(value runtime.Object) {
   142  	a.lock.Lock()
   143  	defer a.lock.Unlock()
   144  	a.bootstrapped = true
   145  	a.mergedConfiguration = value
   146  	a.ready = true
   147  	a.lastErr = nil
   148  }
   149  
   150  func (a *poller) Run(stopCh <-chan struct{}) {
   151  	go wait.Until(a.sync, a.interval, stopCh)
   152  }
   153  
   154  func (a *poller) sync() {
   155  	configuration, err := a.get()
   156  	if err != nil {
   157  		a.failures++
   158  		a.lastError(err)
   159  		if a.failures >= a.failureThreshold {
   160  			a.notReady()
   161  		}
   162  		return
   163  	}
   164  	a.failures = 0
   165  	a.setConfigurationAndReady(configuration)
   166  }