Unverified Commit 95fc76b2 authored by Hussein Galal's avatar Hussein Galal Committed by GitHub

Update to v1.19.8 (#2948)

* Update to v1.19.8 Signed-off-by: 's avatargalal-hussein <hussein.galal.ahmed.11@gmail.com> * Update to v1.19.8 Signed-off-by: 's avatargalal-hussein <hussein.galal.ahmed.11@gmail.com>
parent 870f2e50
...@@ -32,31 +32,31 @@ replace ( ...@@ -32,31 +32,31 @@ replace (
google.golang.org/genproto => google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63 google.golang.org/genproto => google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63
google.golang.org/grpc => google.golang.org/grpc v1.27.1 google.golang.org/grpc => google.golang.org/grpc v1.27.1
gopkg.in/square/go-jose.v2 => gopkg.in/square/go-jose.v2 v2.2.2 gopkg.in/square/go-jose.v2 => gopkg.in/square/go-jose.v2 v2.2.2
k8s.io/api => github.com/k3s-io/kubernetes/staging/src/k8s.io/api v1.19.7-k3s1 k8s.io/api => github.com/k3s-io/kubernetes/staging/src/k8s.io/api v1.19.8-k3s1
k8s.io/apiextensions-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiextensions-apiserver v1.19.7-k3s1 k8s.io/apiextensions-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiextensions-apiserver v1.19.8-k3s1
k8s.io/apimachinery => github.com/k3s-io/kubernetes/staging/src/k8s.io/apimachinery v1.19.7-k3s1 k8s.io/apimachinery => github.com/k3s-io/kubernetes/staging/src/k8s.io/apimachinery v1.19.8-k3s1
k8s.io/apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiserver v1.19.7-k3s1 k8s.io/apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiserver v1.19.8-k3s1
k8s.io/cli-runtime => github.com/k3s-io/kubernetes/staging/src/k8s.io/cli-runtime v1.19.7-k3s1 k8s.io/cli-runtime => github.com/k3s-io/kubernetes/staging/src/k8s.io/cli-runtime v1.19.8-k3s1
k8s.io/client-go => github.com/k3s-io/kubernetes/staging/src/k8s.io/client-go v1.19.7-k3s1 k8s.io/client-go => github.com/k3s-io/kubernetes/staging/src/k8s.io/client-go v1.19.8-k3s1
k8s.io/cloud-provider => github.com/k3s-io/kubernetes/staging/src/k8s.io/cloud-provider v1.19.7-k3s1 k8s.io/cloud-provider => github.com/k3s-io/kubernetes/staging/src/k8s.io/cloud-provider v1.19.8-k3s1
k8s.io/cluster-bootstrap => github.com/k3s-io/kubernetes/staging/src/k8s.io/cluster-bootstrap v1.19.7-k3s1 k8s.io/cluster-bootstrap => github.com/k3s-io/kubernetes/staging/src/k8s.io/cluster-bootstrap v1.19.8-k3s1
k8s.io/code-generator => github.com/k3s-io/kubernetes/staging/src/k8s.io/code-generator v1.19.7-k3s1 k8s.io/code-generator => github.com/k3s-io/kubernetes/staging/src/k8s.io/code-generator v1.19.8-k3s1
k8s.io/component-base => github.com/k3s-io/kubernetes/staging/src/k8s.io/component-base v1.19.7-k3s1 k8s.io/component-base => github.com/k3s-io/kubernetes/staging/src/k8s.io/component-base v1.19.8-k3s1
k8s.io/cri-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/cri-api v1.19.7-k3s1 k8s.io/cri-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/cri-api v1.19.8-k3s1
k8s.io/csi-translation-lib => github.com/k3s-io/kubernetes/staging/src/k8s.io/csi-translation-lib v1.19.7-k3s1 k8s.io/csi-translation-lib => github.com/k3s-io/kubernetes/staging/src/k8s.io/csi-translation-lib v1.19.8-k3s1
k8s.io/kube-aggregator => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-aggregator v1.19.7-k3s1 k8s.io/kube-aggregator => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-aggregator v1.19.8-k3s1
k8s.io/kube-controller-manager => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-controller-manager v1.19.7-k3s1 k8s.io/kube-controller-manager => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-controller-manager v1.19.8-k3s1
k8s.io/kube-proxy => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-proxy v1.19.7-k3s1 k8s.io/kube-proxy => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-proxy v1.19.8-k3s1
k8s.io/kube-scheduler => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-scheduler v1.19.7-k3s1 k8s.io/kube-scheduler => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-scheduler v1.19.8-k3s1
k8s.io/kubectl => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubectl v1.19.7-k3s1 k8s.io/kubectl => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubectl v1.19.8-k3s1
k8s.io/kubelet => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubelet v1.19.7-k3s1 k8s.io/kubelet => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubelet v1.19.8-k3s1
k8s.io/kubernetes => github.com/k3s-io/kubernetes v1.19.7-k3s1 k8s.io/kubernetes => github.com/k3s-io/kubernetes v1.19.8-k3s1
k8s.io/legacy-cloud-providers => github.com/k3s-io/kubernetes/staging/src/k8s.io/legacy-cloud-providers v1.19.7-k3s1 k8s.io/legacy-cloud-providers => github.com/k3s-io/kubernetes/staging/src/k8s.io/legacy-cloud-providers v1.19.8-k3s1
k8s.io/metrics => github.com/k3s-io/kubernetes/staging/src/k8s.io/metrics v1.19.7-k3s1 k8s.io/metrics => github.com/k3s-io/kubernetes/staging/src/k8s.io/metrics v1.19.8-k3s1
k8s.io/node-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/node-api v1.19.7-k3s1 k8s.io/node-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/node-api v1.19.8-k3s1
k8s.io/sample-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-apiserver v1.19.7-k3s1 k8s.io/sample-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-apiserver v1.19.8-k3s1
k8s.io/sample-cli-plugin => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-cli-plugin v1.19.7-k3s1 k8s.io/sample-cli-plugin => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-cli-plugin v1.19.8-k3s1
k8s.io/sample-controller => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-controller v1.19.7-k3s1 k8s.io/sample-controller => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-controller v1.19.8-k3s1
mvdan.cc/unparam => mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34 mvdan.cc/unparam => mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34
) )
...@@ -112,6 +112,6 @@ require ( ...@@ -112,6 +112,6 @@ require (
k8s.io/component-base v0.19.0 k8s.io/component-base v0.19.0
k8s.io/cri-api v0.19.0 k8s.io/cri-api v0.19.0
k8s.io/klog v1.0.0 k8s.io/klog v1.0.0
k8s.io/kubernetes v1.19.7 k8s.io/kubernetes v1.19.8
sigs.k8s.io/yaml v1.2.0 sigs.k8s.io/yaml v1.2.0
) )
...@@ -527,22 +527,7 @@ func (i *RealFsInfo) GetDeviceInfoByFsUUID(uuid string) (*DeviceInfo, error) { ...@@ -527,22 +527,7 @@ func (i *RealFsInfo) GetDeviceInfoByFsUUID(uuid string) (*DeviceInfo, error) {
return &DeviceInfo{deviceName, p.major, p.minor}, nil return &DeviceInfo{deviceName, p.major, p.minor}, nil
} }
func (i *RealFsInfo) GetDirFsDevice(dir string) (*DeviceInfo, error) { func (i *RealFsInfo) mountInfoFromDir(dir string) (*mount.MountInfo, bool) {
buf := new(syscall.Stat_t)
err := syscall.Stat(dir, buf)
if err != nil {
return nil, fmt.Errorf("stat failed on %s with error: %s", dir, err)
}
// The type Dev in Stat_t is 32bit on mips.
major := major(uint64(buf.Dev)) // nolint: unconvert
minor := minor(uint64(buf.Dev)) // nolint: unconvert
for device, partition := range i.partitions {
if partition.major == major && partition.minor == minor {
return &DeviceInfo{device, major, minor}, nil
}
}
mount, found := i.mounts[dir] mount, found := i.mounts[dir]
// try the parent dir if not found until we reach the root dir // try the parent dir if not found until we reach the root dir
// this is an issue on btrfs systems where the directory is not // this is an issue on btrfs systems where the directory is not
...@@ -551,6 +536,7 @@ func (i *RealFsInfo) GetDirFsDevice(dir string) (*DeviceInfo, error) { ...@@ -551,6 +536,7 @@ func (i *RealFsInfo) GetDirFsDevice(dir string) (*DeviceInfo, error) {
pathdir, _ := filepath.Split(dir) pathdir, _ := filepath.Split(dir)
// break when we reach root // break when we reach root
if pathdir == "/" { if pathdir == "/" {
mount, found = i.mounts["/"]
break break
} }
// trim "/" from the new parent path otherwise the next possible // trim "/" from the new parent path otherwise the next possible
...@@ -558,9 +544,28 @@ func (i *RealFsInfo) GetDirFsDevice(dir string) (*DeviceInfo, error) { ...@@ -558,9 +544,28 @@ func (i *RealFsInfo) GetDirFsDevice(dir string) (*DeviceInfo, error) {
dir = strings.TrimSuffix(pathdir, "/") dir = strings.TrimSuffix(pathdir, "/")
mount, found = i.mounts[dir] mount, found = i.mounts[dir]
} }
return &mount, found
}
func (i *RealFsInfo) GetDirFsDevice(dir string) (*DeviceInfo, error) {
buf := new(syscall.Stat_t)
err := syscall.Stat(dir, buf)
if err != nil {
return nil, fmt.Errorf("stat failed on %s with error: %s", dir, err)
}
// The type Dev in Stat_t is 32bit on mips.
major := major(uint64(buf.Dev)) // nolint: unconvert
minor := minor(uint64(buf.Dev)) // nolint: unconvert
for device, partition := range i.partitions {
if partition.major == major && partition.minor == minor {
return &DeviceInfo{device, major, minor}, nil
}
}
mount, found := i.mountInfoFromDir(dir)
if found && mount.FsType == "btrfs" && mount.Major == 0 && strings.HasPrefix(mount.Source, "/dev/") { if found && mount.FsType == "btrfs" && mount.Major == 0 && strings.HasPrefix(mount.Source, "/dev/") {
major, minor, err := getBtrfsMajorMinorIds(&mount) major, minor, err := getBtrfsMajorMinorIds(mount)
if err != nil { if err != nil {
klog.Warningf("%s", err) klog.Warningf("%s", err)
} else { } else {
......
...@@ -604,3 +604,32 @@ func poller(interval, timeout time.Duration) WaitFunc { ...@@ -604,3 +604,32 @@ func poller(interval, timeout time.Duration) WaitFunc {
return ch return ch
}) })
} }
// ExponentialBackoffWithContext works with a request context and a Backoff. It ensures that the retry wait never
// exceeds the deadline specified by the request context.
func ExponentialBackoffWithContext(ctx context.Context, backoff Backoff, condition ConditionFunc) error {
for backoff.Steps > 0 {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
if ok, err := runConditionWithCrashProtection(condition); err != nil || ok {
return err
}
if backoff.Steps == 1 {
break
}
waitBeforeRetry := backoff.Step()
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(waitBeforeRetry):
}
}
return ErrWaitTimeout
}
...@@ -78,6 +78,7 @@ go_test( ...@@ -78,6 +78,7 @@ go_test(
"//staging/src/k8s.io/api/flowcontrol/v1alpha1:go_default_library", "//staging/src/k8s.io/api/flowcontrol/v1alpha1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/apis/flowcontrol/bootstrap:go_default_library", "//staging/src/k8s.io/apiserver/pkg/apis/flowcontrol/bootstrap:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library", "//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library", "//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
......
...@@ -34,6 +34,7 @@ import ( ...@@ -34,6 +34,7 @@ import (
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
apitypes "k8s.io/apimachinery/pkg/types" apitypes "k8s.io/apimachinery/pkg/types"
apierrors "k8s.io/apimachinery/pkg/util/errors" apierrors "k8s.io/apimachinery/pkg/util/errors"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
fcboot "k8s.io/apiserver/pkg/apis/flowcontrol/bootstrap" fcboot "k8s.io/apiserver/pkg/apis/flowcontrol/bootstrap"
"k8s.io/apiserver/pkg/authentication/user" "k8s.io/apiserver/pkg/authentication/user"
...@@ -244,14 +245,28 @@ func (cfgCtlr *configController) updateObservations() { ...@@ -244,14 +245,28 @@ func (cfgCtlr *configController) updateObservations() {
} }
} }
// used from the unit tests only.
func (cfgCtlr *configController) getPriorityLevelState(plName string) *priorityLevelState {
cfgCtlr.lock.Lock()
defer cfgCtlr.lock.Unlock()
return cfgCtlr.priorityLevelStates[plName]
}
func (cfgCtlr *configController) Run(stopCh <-chan struct{}) error { func (cfgCtlr *configController) Run(stopCh <-chan struct{}) error {
defer utilruntime.HandleCrash()
// Let the config worker stop when we are done
defer cfgCtlr.configQueue.ShutDown() defer cfgCtlr.configQueue.ShutDown()
klog.Info("Starting API Priority and Fairness config controller") klog.Info("Starting API Priority and Fairness config controller")
if ok := cache.WaitForCacheSync(stopCh, cfgCtlr.plInformerSynced, cfgCtlr.fsInformerSynced); !ok { if ok := cache.WaitForCacheSync(stopCh, cfgCtlr.plInformerSynced, cfgCtlr.fsInformerSynced); !ok {
return fmt.Errorf("Never achieved initial sync") return fmt.Errorf("Never achieved initial sync")
} }
klog.Info("Running API Priority and Fairness config worker") klog.Info("Running API Priority and Fairness config worker")
wait.Until(cfgCtlr.runWorker, time.Second, stopCh) go wait.Until(cfgCtlr.runWorker, time.Second, stopCh)
<-stopCh
klog.Info("Shutting down API Priority and Fairness config worker") klog.Info("Shutting down API Priority and Fairness config worker")
return nil return nil
} }
......
...@@ -112,21 +112,28 @@ func (cfgCtlr *configController) Handle(ctx context.Context, requestDigest Reque ...@@ -112,21 +112,28 @@ func (cfgCtlr *configController) Handle(ctx context.Context, requestDigest Reque
} }
klog.V(7).Infof("Handle(%#+v) => fsName=%q, distMethod=%#+v, plName=%q, isExempt=%v, queued=%v", requestDigest, fs.Name, fs.Spec.DistinguisherMethod, pl.Name, isExempt, queued) klog.V(7).Infof("Handle(%#+v) => fsName=%q, distMethod=%#+v, plName=%q, isExempt=%v, queued=%v", requestDigest, fs.Name, fs.Spec.DistinguisherMethod, pl.Name, isExempt, queued)
var executed bool var executed bool
idle := req.Finish(func() { idle, panicking := true, true
defer func() {
klog.V(7).Infof("Handle(%#+v) => fsName=%q, distMethod=%#+v, plName=%q, isExempt=%v, queued=%v, Finish() => panicking=%v idle=%v",
requestDigest, fs.Name, fs.Spec.DistinguisherMethod, pl.Name, isExempt, queued, panicking, idle)
if idle {
cfgCtlr.maybeReap(pl.Name)
}
}()
idle = req.Finish(func() {
if queued { if queued {
metrics.ObserveWaitingDuration(pl.Name, fs.Name, strconv.FormatBool(req != nil), time.Since(startWaitingTime)) metrics.ObserveWaitingDuration(pl.Name, fs.Name, strconv.FormatBool(req != nil), time.Since(startWaitingTime))
} }
metrics.AddDispatch(pl.Name, fs.Name) metrics.AddDispatch(pl.Name, fs.Name)
executed = true executed = true
startExecutionTime := time.Now() startExecutionTime := time.Now()
defer func() {
metrics.ObserveExecutionDuration(pl.Name, fs.Name, time.Since(startExecutionTime))
}()
execFn() execFn()
metrics.ObserveExecutionDuration(pl.Name, fs.Name, time.Since(startExecutionTime))
}) })
if queued && !executed { if queued && !executed {
metrics.ObserveWaitingDuration(pl.Name, fs.Name, strconv.FormatBool(req != nil), time.Since(startWaitingTime)) metrics.ObserveWaitingDuration(pl.Name, fs.Name, strconv.FormatBool(req != nil), time.Since(startWaitingTime))
} }
klog.V(7).Infof("Handle(%#+v) => fsName=%q, distMethod=%#+v, plName=%q, isExempt=%v, queued=%v, Finish() => idle=%v", requestDigest, fs.Name, fs.Spec.DistinguisherMethod, pl.Name, isExempt, queued, idle) panicking = false
if idle {
cfgCtlr.maybeReap(pl.Name)
}
} }
...@@ -316,8 +316,15 @@ func (req *request) Finish(execFn func()) bool { ...@@ -316,8 +316,15 @@ func (req *request) Finish(execFn func()) bool {
if !exec { if !exec {
return idle return idle
} }
execFn() func() {
return req.qs.finishRequestAndDispatchAsMuchAsPossible(req) defer func() {
idle = req.qs.finishRequestAndDispatchAsMuchAsPossible(req)
}()
execFn()
}()
return idle
} }
func (req *request) wait() (bool, bool) { func (req *request) wait() (bool, bool) {
......
...@@ -64,13 +64,16 @@ func (spg SampleAndWaterMarkPairGenerator) metrics() Registerables { ...@@ -64,13 +64,16 @@ func (spg SampleAndWaterMarkPairGenerator) metrics() Registerables {
// SampleAndWaterMarkObserverGenerator creates TimedObservers that // SampleAndWaterMarkObserverGenerator creates TimedObservers that
// populate histograms of samples and low- and high-water-marks. The // populate histograms of samples and low- and high-water-marks. The
// generator has a samplePeriod, and the histograms get an observation // generator has a samplePeriod, and the histograms get an observation
// every samplePeriod. // every samplePeriod. The sampling windows are quantized based on
// the monotonic rather than wall-clock times. The `t0` field is
// there so to provide a baseline for monotonic clock differences.
type SampleAndWaterMarkObserverGenerator struct { type SampleAndWaterMarkObserverGenerator struct {
*sampleAndWaterMarkObserverGenerator *sampleAndWaterMarkObserverGenerator
} }
type sampleAndWaterMarkObserverGenerator struct { type sampleAndWaterMarkObserverGenerator struct {
clock clock.PassiveClock clock clock.PassiveClock
t0 time.Time
samplePeriod time.Duration samplePeriod time.Duration
samples *compbasemetrics.HistogramVec samples *compbasemetrics.HistogramVec
waterMarks *compbasemetrics.HistogramVec waterMarks *compbasemetrics.HistogramVec
...@@ -83,6 +86,7 @@ func NewSampleAndWaterMarkHistogramsGenerator(clock clock.PassiveClock, samplePe ...@@ -83,6 +86,7 @@ func NewSampleAndWaterMarkHistogramsGenerator(clock clock.PassiveClock, samplePe
return SampleAndWaterMarkObserverGenerator{ return SampleAndWaterMarkObserverGenerator{
&sampleAndWaterMarkObserverGenerator{ &sampleAndWaterMarkObserverGenerator{
clock: clock, clock: clock,
t0: clock.Now(),
samplePeriod: samplePeriod, samplePeriod: samplePeriod,
samples: compbasemetrics.NewHistogramVec(sampleOpts, labelNames), samples: compbasemetrics.NewHistogramVec(sampleOpts, labelNames),
waterMarks: compbasemetrics.NewHistogramVec(waterMarkOpts, append([]string{labelNameMark}, labelNames...)), waterMarks: compbasemetrics.NewHistogramVec(waterMarkOpts, append([]string{labelNameMark}, labelNames...)),
...@@ -90,7 +94,7 @@ func NewSampleAndWaterMarkHistogramsGenerator(clock clock.PassiveClock, samplePe ...@@ -90,7 +94,7 @@ func NewSampleAndWaterMarkHistogramsGenerator(clock clock.PassiveClock, samplePe
} }
func (swg *sampleAndWaterMarkObserverGenerator) quantize(when time.Time) int64 { func (swg *sampleAndWaterMarkObserverGenerator) quantize(when time.Time) int64 {
return when.UnixNano() / int64(swg.samplePeriod) return int64(when.Sub(swg.t0) / swg.samplePeriod)
} }
// Generate makes a new TimedObserver // Generate makes a new TimedObserver
...@@ -156,31 +160,44 @@ func (saw *sampleAndWaterMarkHistograms) SetX1(x1 float64) { ...@@ -156,31 +160,44 @@ func (saw *sampleAndWaterMarkHistograms) SetX1(x1 float64) {
} }
func (saw *sampleAndWaterMarkHistograms) innerSet(updateXOrX1 func()) { func (saw *sampleAndWaterMarkHistograms) innerSet(updateXOrX1 func()) {
saw.Lock() when, whenInt, acc, wellOrdered := func() (time.Time, int64, sampleAndWaterMarkAccumulator, bool) {
when := saw.clock.Now() saw.Lock()
whenInt := saw.quantize(when) defer saw.Unlock()
acc := saw.sampleAndWaterMarkAccumulator // Moved these variables here to tiptoe around https://github.com/golang/go/issues/43570 for #97685
wellOrdered := !when.Before(acc.lastSet) when := saw.clock.Now()
if wellOrdered { whenInt := saw.quantize(when)
acc := saw.sampleAndWaterMarkAccumulator
wellOrdered := !when.Before(acc.lastSet)
updateXOrX1() updateXOrX1()
saw.relX = saw.x / saw.x1 saw.relX = saw.x / saw.x1
if acc.lastSetInt < whenInt { if wellOrdered {
saw.loRelX, saw.hiRelX = acc.relX, acc.relX if acc.lastSetInt < whenInt {
saw.lastSetInt = whenInt saw.loRelX, saw.hiRelX = acc.relX, acc.relX
saw.lastSetInt = whenInt
}
saw.lastSet = when
} }
// `wellOrdered` should always be true because we are using
// monotonic clock readings and they never go backwards. Yet
// very small backwards steps (under 1 microsecond) have been
// observed
// (https://github.com/kubernetes/kubernetes/issues/96459).
// In the backwards case, treat the current reading as if it
// had occurred at time `saw.lastSet` and log an error. It
// would be wrong to update `saw.lastSet` in this case because
// that plants a time bomb for future updates to
// `saw.lastSetInt`.
if saw.relX < saw.loRelX { if saw.relX < saw.loRelX {
saw.loRelX = saw.relX saw.loRelX = saw.relX
} else if saw.relX > saw.hiRelX { } else if saw.relX > saw.hiRelX {
saw.hiRelX = saw.relX saw.hiRelX = saw.relX
} }
saw.lastSet = when return when, whenInt, acc, wellOrdered
} }()
saw.Unlock()
if !wellOrdered { if !wellOrdered {
lastSetS := acc.lastSet.Format(time.RFC3339Nano) lastSetS := acc.lastSet.String()
whenS := when.Format(time.RFC3339Nano) whenS := when.String()
klog.Fatalf("Time went backwards from %s to %s for labelValues=%#+v", lastSetS, whenS, saw.labelValues) klog.Errorf("Time went backwards from %s to %s for labelValues=%#+v", lastSetS, whenS, saw.labelValues)
panic(append([]string{lastSetS, whenS}, saw.labelValues...))
} }
for acc.lastSetInt < whenInt { for acc.lastSetInt < whenInt {
saw.samples.WithLabelValues(saw.labelValues...).Observe(acc.relX) saw.samples.WithLabelValues(saw.labelValues...).Observe(acc.relX)
......
...@@ -131,20 +131,27 @@ func WithExponentialBackoff(ctx context.Context, initialBackoff time.Duration, w ...@@ -131,20 +131,27 @@ func WithExponentialBackoff(ctx context.Context, initialBackoff time.Duration, w
Steps: 5, Steps: 5,
} }
var err error // having a webhook error allows us to track the last actual webhook error for requests that
wait.ExponentialBackoff(backoff, func() (bool, error) { // are later cancelled or time out.
err = webhookFn() var webhookErr error
if ctx.Err() != nil { err := wait.ExponentialBackoffWithContext(ctx, backoff, func() (bool, error) {
// we timed out or were cancelled, we should not retry webhookErr = webhookFn()
return true, err if shouldRetry(webhookErr) {
}
if shouldRetry(err) {
return false, nil return false, nil
} }
if err != nil { if webhookErr != nil {
return false, err return false, webhookErr
} }
return true, nil return true, nil
}) })
return err
switch {
// we check for webhookErr first, if webhookErr is set it's the most important error to return.
case webhookErr != nil:
return webhookErr
case err != nil:
return fmt.Errorf("webhook call failed: %s", err.Error())
default:
return nil
}
} }
...@@ -99,14 +99,14 @@ func (w *WebhookTokenAuthenticator) AuthenticateToken(ctx context.Context, token ...@@ -99,14 +99,14 @@ func (w *WebhookTokenAuthenticator) AuthenticateToken(ctx context.Context, token
} }
var ( var (
result *authenticationv1.TokenReview result *authenticationv1.TokenReview
err error
auds authenticator.Audiences auds authenticator.Audiences
) )
webhook.WithExponentialBackoff(ctx, w.initialBackoff, func() error { // WithExponentialBackoff will return tokenreview create error (tokenReviewErr) if any.
result, err = w.tokenReview.Create(ctx, r, metav1.CreateOptions{}) if err := webhook.WithExponentialBackoff(ctx, w.initialBackoff, func() error {
return err var tokenReviewErr error
}, webhook.DefaultShouldRetry) result, tokenReviewErr = w.tokenReview.Create(ctx, r, metav1.CreateOptions{})
if err != nil { return tokenReviewErr
}, webhook.DefaultShouldRetry); err != nil {
// An error here indicates bad configuration or an outage. Log for debugging. // An error here indicates bad configuration or an outage. Log for debugging.
klog.Errorf("Failed to make webhook authenticator request: %v", err) klog.Errorf("Failed to make webhook authenticator request: %v", err)
return nil, false, err return nil, false, err
......
...@@ -186,19 +186,17 @@ func (w *WebhookAuthorizer) Authorize(ctx context.Context, attr authorizer.Attri ...@@ -186,19 +186,17 @@ func (w *WebhookAuthorizer) Authorize(ctx context.Context, attr authorizer.Attri
if entry, ok := w.responseCache.Get(string(key)); ok { if entry, ok := w.responseCache.Get(string(key)); ok {
r.Status = entry.(authorizationv1.SubjectAccessReviewStatus) r.Status = entry.(authorizationv1.SubjectAccessReviewStatus)
} else { } else {
var ( var result *authorizationv1.SubjectAccessReview
result *authorizationv1.SubjectAccessReview // WithExponentialBackoff will return SAR create error (sarErr) if any.
err error if err := webhook.WithExponentialBackoff(ctx, w.initialBackoff, func() error {
) var sarErr error
webhook.WithExponentialBackoff(ctx, w.initialBackoff, func() error { result, sarErr = w.subjectAccessReview.Create(ctx, r, metav1.CreateOptions{})
result, err = w.subjectAccessReview.Create(ctx, r, metav1.CreateOptions{}) return sarErr
return err }, webhook.DefaultShouldRetry); err != nil {
}, webhook.DefaultShouldRetry)
if err != nil {
// An error here indicates bad configuration or an outage. Log for debugging.
klog.Errorf("Failed to make webhook authorizer request: %v", err) klog.Errorf("Failed to make webhook authorizer request: %v", err)
return w.decisionOnError, "", err return w.decisionOnError, "", err
} }
r.Status = result.Status r.Status = result.Status
if shouldCache(attr) { if shouldCache(attr) {
if r.Status.Allowed { if r.Status.Allowed {
......
...@@ -3,8 +3,8 @@ package version ...@@ -3,8 +3,8 @@ package version
var ( var (
gitMajor = "1" gitMajor = "1"
gitMinor = "19" gitMinor = "19"
gitVersion = "v1.19.7-k3s1" gitVersion = "v1.19.8-k3s1"
gitCommit = "88e736c5fcf301a7bfb91b4cbc7f72f4d34daed2" gitCommit = "71b31507f0011b9fd9a4311fabeb17e43d7ff0e6"
gitTreeState = "clean" gitTreeState = "clean"
buildDate = "2021-01-13T19:33:02Z" buildDate = "2021-02-17T22:28:17Z"
) )
...@@ -3,8 +3,8 @@ package version ...@@ -3,8 +3,8 @@ package version
var ( var (
gitMajor = "1" gitMajor = "1"
gitMinor = "19" gitMinor = "19"
gitVersion = "v1.19.7-k3s1" gitVersion = "v1.19.8-k3s1"
gitCommit = "88e736c5fcf301a7bfb91b4cbc7f72f4d34daed2" gitCommit = "71b31507f0011b9fd9a4311fabeb17e43d7ff0e6"
gitTreeState = "clean" gitTreeState = "clean"
buildDate = "2021-01-13T19:33:02Z" buildDate = "2021-02-17T22:28:17Z"
) )
...@@ -46,6 +46,7 @@ go_test( ...@@ -46,6 +46,7 @@ go_test(
"azure_file_test.go", "azure_file_test.go",
"gce_pd_test.go", "gce_pd_test.go",
"in_tree_volume_test.go", "in_tree_volume_test.go",
"openstack_cinder_test.go",
"vsphere_volume_test.go", "vsphere_volume_test.go",
], ],
embed = [":go_default_library"], embed = [":go_default_library"],
......
...@@ -98,7 +98,7 @@ func (t *awsElasticBlockStoreCSITranslator) TranslateInTreeInlineVolumeToCSI(vol ...@@ -98,7 +98,7 @@ func (t *awsElasticBlockStoreCSITranslator) TranslateInTreeInlineVolumeToCSI(vol
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
// Must be unique per disk as it is used as the unique part of the // Must be unique per disk as it is used as the unique part of the
// staging path // staging path
Name: fmt.Sprintf("%s-%s", AWSEBSDriverName, ebsSource.VolumeID), Name: fmt.Sprintf("%s-%s", AWSEBSDriverName, volumeHandle),
}, },
Spec: v1.PersistentVolumeSpec{ Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{ PersistentVolumeSource: v1.PersistentVolumeSource{
......
...@@ -37,11 +37,11 @@ const ( ...@@ -37,11 +37,11 @@ const (
volumeIDTemplate = "%s#%s#%s#%s" volumeIDTemplate = "%s#%s#%s#%s"
// Parameter names defined in azure file CSI driver, refer to // Parameter names defined in azure file CSI driver, refer to
// https://github.com/kubernetes-sigs/azurefile-csi-driver/blob/master/docs/driver-parameters.md // https://github.com/kubernetes-sigs/azurefile-csi-driver/blob/master/docs/driver-parameters.md
azureFileShareName = "shareName" shareNameField = "sharename"
secretNameField = "secretname"
secretNameTemplate = "azure-storage-account-%s-secret" secretNamespaceField = "secretnamespace"
defaultSecretNamespace = "default" secretNameTemplate = "azure-storage-account-%s-secret"
defaultSecretNamespace = "default"
resourceGroupAnnotation = "kubernetes.io/azure-file-resource-group" resourceGroupAnnotation = "kubernetes.io/azure-file-resource-group"
) )
...@@ -90,7 +90,7 @@ func (t *azureFileCSITranslator) TranslateInTreeInlineVolumeToCSI(volume *v1.Vol ...@@ -90,7 +90,7 @@ func (t *azureFileCSITranslator) TranslateInTreeInlineVolumeToCSI(volume *v1.Vol
Driver: AzureFileDriverName, Driver: AzureFileDriverName,
VolumeHandle: fmt.Sprintf(volumeIDTemplate, "", accountName, azureSource.ShareName, ""), VolumeHandle: fmt.Sprintf(volumeIDTemplate, "", accountName, azureSource.ShareName, ""),
ReadOnly: azureSource.ReadOnly, ReadOnly: azureSource.ReadOnly,
VolumeAttributes: map[string]string{azureFileShareName: azureSource.ShareName}, VolumeAttributes: map[string]string{shareNameField: azureSource.ShareName},
NodeStageSecretRef: &v1.SecretReference{ NodeStageSecretRef: &v1.SecretReference{
Name: azureSource.SecretName, Name: azureSource.SecretName,
Namespace: defaultSecretNamespace, Namespace: defaultSecretNamespace,
...@@ -135,7 +135,7 @@ func (t *azureFileCSITranslator) TranslateInTreePVToCSI(pv *v1.PersistentVolume) ...@@ -135,7 +135,7 @@ func (t *azureFileCSITranslator) TranslateInTreePVToCSI(pv *v1.PersistentVolume)
Namespace: defaultSecretNamespace, Namespace: defaultSecretNamespace,
}, },
ReadOnly: azureSource.ReadOnly, ReadOnly: azureSource.ReadOnly,
VolumeAttributes: map[string]string{azureFileShareName: azureSource.ShareName}, VolumeAttributes: map[string]string{shareNameField: azureSource.ShareName},
VolumeHandle: volumeID, VolumeHandle: volumeID,
} }
) )
...@@ -163,31 +163,48 @@ func (t *azureFileCSITranslator) TranslateCSIPVToInTree(pv *v1.PersistentVolume) ...@@ -163,31 +163,48 @@ func (t *azureFileCSITranslator) TranslateCSIPVToInTree(pv *v1.PersistentVolume)
ReadOnly: csiSource.ReadOnly, ReadOnly: csiSource.ReadOnly,
} }
for k, v := range csiSource.VolumeAttributes {
switch strings.ToLower(k) {
case shareNameField:
azureSource.ShareName = v
case secretNameField:
azureSource.SecretName = v
case secretNamespaceField:
ns := v
azureSource.SecretNamespace = &ns
}
}
resourceGroup := "" resourceGroup := ""
if csiSource.NodeStageSecretRef != nil && csiSource.NodeStageSecretRef.Name != "" { if csiSource.NodeStageSecretRef != nil && csiSource.NodeStageSecretRef.Name != "" {
azureSource.SecretName = csiSource.NodeStageSecretRef.Name azureSource.SecretName = csiSource.NodeStageSecretRef.Name
azureSource.SecretNamespace = &csiSource.NodeStageSecretRef.Namespace azureSource.SecretNamespace = &csiSource.NodeStageSecretRef.Namespace
if csiSource.VolumeAttributes != nil { }
if shareName, ok := csiSource.VolumeAttributes[azureFileShareName]; ok { if azureSource.ShareName == "" || azureSource.SecretName == "" {
azureSource.ShareName = shareName
}
}
} else {
rg, storageAccount, fileShareName, _, err := getFileShareInfo(csiSource.VolumeHandle) rg, storageAccount, fileShareName, _, err := getFileShareInfo(csiSource.VolumeHandle)
if err != nil { if err != nil {
return nil, err return nil, err
} }
azureSource.ShareName = fileShareName if azureSource.ShareName == "" {
azureSource.SecretName = fmt.Sprintf(secretNameTemplate, storageAccount) azureSource.ShareName = fileShareName
}
if azureSource.SecretName == "" {
azureSource.SecretName = fmt.Sprintf(secretNameTemplate, storageAccount)
}
resourceGroup = rg resourceGroup = rg
} }
if azureSource.SecretNamespace == nil {
ns := defaultSecretNamespace
azureSource.SecretNamespace = &ns
}
pv.Spec.CSI = nil pv.Spec.CSI = nil
pv.Spec.AzureFile = azureSource pv.Spec.AzureFile = azureSource
if pv.ObjectMeta.Annotations == nil {
pv.ObjectMeta.Annotations = map[string]string{}
}
if resourceGroup != "" { if resourceGroup != "" {
if pv.ObjectMeta.Annotations == nil {
pv.ObjectMeta.Annotations = map[string]string{}
}
pv.ObjectMeta.Annotations[resourceGroupAnnotation] = resourceGroup pv.ObjectMeta.Annotations[resourceGroupAnnotation] = resourceGroup
} }
......
...@@ -18,6 +18,7 @@ package plugins ...@@ -18,6 +18,7 @@ package plugins
import ( import (
"fmt" "fmt"
"strings"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
storage "k8s.io/api/storage/v1" storage "k8s.io/api/storage/v1"
...@@ -45,6 +46,30 @@ func NewOpenStackCinderCSITranslator() InTreePlugin { ...@@ -45,6 +46,30 @@ func NewOpenStackCinderCSITranslator() InTreePlugin {
// TranslateInTreeStorageClassParametersToCSI translates InTree Cinder storage class parameters to CSI storage class // TranslateInTreeStorageClassParametersToCSI translates InTree Cinder storage class parameters to CSI storage class
func (t *osCinderCSITranslator) TranslateInTreeStorageClassToCSI(sc *storage.StorageClass) (*storage.StorageClass, error) { func (t *osCinderCSITranslator) TranslateInTreeStorageClassToCSI(sc *storage.StorageClass) (*storage.StorageClass, error) {
var (
params = map[string]string{}
)
for k, v := range sc.Parameters {
switch strings.ToLower(k) {
case fsTypeKey:
params[csiFsTypeKey] = v
default:
// All other parameters are supported by the CSI driver.
// This includes also "availability", therefore do not translate it to sc.AllowedTopologies
params[k] = v
}
}
if len(sc.AllowedTopologies) > 0 {
newTopologies, err := translateAllowedTopologies(sc.AllowedTopologies, CinderTopologyKey)
if err != nil {
return nil, fmt.Errorf("failed translating allowed topologies: %v", err)
}
sc.AllowedTopologies = newTopologies
}
sc.Parameters = params
return sc, nil return sc, nil
} }
......
...@@ -41,7 +41,10 @@ go_library( ...@@ -41,7 +41,10 @@ go_library(
go_test( go_test(
name = "go_default_test", name = "go_default_test",
srcs = ["available_controller_test.go"], srcs = [
"available_controller_test.go",
"metrics_test.go",
],
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ deps = [
"//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library",
...@@ -50,6 +53,7 @@ go_test( ...@@ -50,6 +53,7 @@ go_test(
"//staging/src/k8s.io/client-go/testing:go_default_library", "//staging/src/k8s.io/client-go/testing:go_default_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/util/workqueue:go_default_library", "//staging/src/k8s.io/client-go/util/workqueue:go_default_library",
"//staging/src/k8s.io/component-base/metrics/testutil:go_default_library",
"//staging/src/k8s.io/kube-aggregator/pkg/apis/apiregistration/v1:go_default_library", "//staging/src/k8s.io/kube-aggregator/pkg/apis/apiregistration/v1:go_default_library",
"//staging/src/k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/fake:go_default_library", "//staging/src/k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/fake:go_default_library",
"//staging/src/k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/typed/apiregistration/v1:go_default_library", "//staging/src/k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/typed/apiregistration/v1:go_default_library",
......
...@@ -43,6 +43,7 @@ import ( ...@@ -43,6 +43,7 @@ import (
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
"k8s.io/client-go/transport" "k8s.io/client-go/transport"
"k8s.io/client-go/util/workqueue" "k8s.io/client-go/util/workqueue"
"k8s.io/component-base/metrics/legacyregistry"
"k8s.io/klog/v2" "k8s.io/klog/v2"
apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
apiregistrationv1apihelper "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1/helper" apiregistrationv1apihelper "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1/helper"
...@@ -52,6 +53,9 @@ import ( ...@@ -52,6 +53,9 @@ import (
"k8s.io/kube-aggregator/pkg/controllers" "k8s.io/kube-aggregator/pkg/controllers"
) )
// making sure we only register metrics once into legacy registry
var registerIntoLegacyRegistryOnce sync.Once
type certKeyFunc func() ([]byte, []byte) type certKeyFunc func() ([]byte, []byte)
// ServiceResolver knows how to convert a service reference into an actual location. // ServiceResolver knows how to convert a service reference into an actual location.
...@@ -93,6 +97,9 @@ type AvailableConditionController struct { ...@@ -93,6 +97,9 @@ type AvailableConditionController struct {
// NOTE: the cache works because we assume that the transports constructed // NOTE: the cache works because we assume that the transports constructed
// by the controller only vary on the dynamic cert/key. // by the controller only vary on the dynamic cert/key.
tlsCache *tlsTransportCache tlsCache *tlsTransportCache
// metrics registered into legacy registry
metrics *availabilityMetrics
} }
type tlsTransportCache struct { type tlsTransportCache struct {
...@@ -163,6 +170,7 @@ func NewAvailableConditionController( ...@@ -163,6 +170,7 @@ func NewAvailableConditionController(
"AvailableConditionController"), "AvailableConditionController"),
proxyCurrentCertKeyContent: proxyCurrentCertKeyContent, proxyCurrentCertKeyContent: proxyCurrentCertKeyContent,
tlsCache: &tlsTransportCache{transports: make(map[tlsCacheKey]http.RoundTripper)}, tlsCache: &tlsTransportCache{transports: make(map[tlsCacheKey]http.RoundTripper)},
metrics: newAvailabilityMetrics(),
} }
if egressSelector != nil { if egressSelector != nil {
...@@ -203,12 +211,22 @@ func NewAvailableConditionController( ...@@ -203,12 +211,22 @@ func NewAvailableConditionController(
c.syncFn = c.sync c.syncFn = c.sync
// TODO: decouple from legacyregistry
var err error
registerIntoLegacyRegistryOnce.Do(func() {
err = c.metrics.Register(legacyregistry.Register, legacyregistry.CustomRegister)
})
if err != nil {
return nil, err
}
return c, nil return c, nil
} }
func (c *AvailableConditionController) sync(key string) error { func (c *AvailableConditionController) sync(key string) error {
originalAPIService, err := c.apiServiceLister.Get(key) originalAPIService, err := c.apiServiceLister.Get(key)
if apierrors.IsNotFound(err) { if apierrors.IsNotFound(err) {
c.metrics.ForgetAPIService(key)
return nil return nil
} }
if err != nil { if err != nil {
...@@ -259,7 +277,7 @@ func (c *AvailableConditionController) sync(key string) error { ...@@ -259,7 +277,7 @@ func (c *AvailableConditionController) sync(key string) error {
// local API services are always considered available // local API services are always considered available
if apiService.Spec.Service == nil { if apiService.Spec.Service == nil {
apiregistrationv1apihelper.SetAPIServiceCondition(apiService, apiregistrationv1apihelper.NewLocalAvailableAPIServiceCondition()) apiregistrationv1apihelper.SetAPIServiceCondition(apiService, apiregistrationv1apihelper.NewLocalAvailableAPIServiceCondition())
_, err := updateAPIServiceStatus(c.apiServiceClient, originalAPIService, apiService) _, err := c.updateAPIServiceStatus(originalAPIService, apiService)
return err return err
} }
...@@ -269,14 +287,14 @@ func (c *AvailableConditionController) sync(key string) error { ...@@ -269,14 +287,14 @@ func (c *AvailableConditionController) sync(key string) error {
availableCondition.Reason = "ServiceNotFound" availableCondition.Reason = "ServiceNotFound"
availableCondition.Message = fmt.Sprintf("service/%s in %q is not present", apiService.Spec.Service.Name, apiService.Spec.Service.Namespace) availableCondition.Message = fmt.Sprintf("service/%s in %q is not present", apiService.Spec.Service.Name, apiService.Spec.Service.Namespace)
apiregistrationv1apihelper.SetAPIServiceCondition(apiService, availableCondition) apiregistrationv1apihelper.SetAPIServiceCondition(apiService, availableCondition)
_, err := updateAPIServiceStatus(c.apiServiceClient, originalAPIService, apiService) _, err := c.updateAPIServiceStatus(originalAPIService, apiService)
return err return err
} else if err != nil { } else if err != nil {
availableCondition.Status = apiregistrationv1.ConditionUnknown availableCondition.Status = apiregistrationv1.ConditionUnknown
availableCondition.Reason = "ServiceAccessError" availableCondition.Reason = "ServiceAccessError"
availableCondition.Message = fmt.Sprintf("service/%s in %q cannot be checked due to: %v", apiService.Spec.Service.Name, apiService.Spec.Service.Namespace, err) availableCondition.Message = fmt.Sprintf("service/%s in %q cannot be checked due to: %v", apiService.Spec.Service.Name, apiService.Spec.Service.Namespace, err)
apiregistrationv1apihelper.SetAPIServiceCondition(apiService, availableCondition) apiregistrationv1apihelper.SetAPIServiceCondition(apiService, availableCondition)
_, err := updateAPIServiceStatus(c.apiServiceClient, originalAPIService, apiService) _, err := c.updateAPIServiceStatus(originalAPIService, apiService)
return err return err
} }
...@@ -297,7 +315,7 @@ func (c *AvailableConditionController) sync(key string) error { ...@@ -297,7 +315,7 @@ func (c *AvailableConditionController) sync(key string) error {
availableCondition.Reason = "ServicePortError" availableCondition.Reason = "ServicePortError"
availableCondition.Message = fmt.Sprintf("service/%s in %q is not listening on port %d", apiService.Spec.Service.Name, apiService.Spec.Service.Namespace, *apiService.Spec.Service.Port) availableCondition.Message = fmt.Sprintf("service/%s in %q is not listening on port %d", apiService.Spec.Service.Name, apiService.Spec.Service.Namespace, *apiService.Spec.Service.Port)
apiregistrationv1apihelper.SetAPIServiceCondition(apiService, availableCondition) apiregistrationv1apihelper.SetAPIServiceCondition(apiService, availableCondition)
_, err := updateAPIServiceStatus(c.apiServiceClient, originalAPIService, apiService) _, err := c.updateAPIServiceStatus(originalAPIService, apiService)
return err return err
} }
...@@ -307,14 +325,14 @@ func (c *AvailableConditionController) sync(key string) error { ...@@ -307,14 +325,14 @@ func (c *AvailableConditionController) sync(key string) error {
availableCondition.Reason = "EndpointsNotFound" availableCondition.Reason = "EndpointsNotFound"
availableCondition.Message = fmt.Sprintf("cannot find endpoints for service/%s in %q", apiService.Spec.Service.Name, apiService.Spec.Service.Namespace) availableCondition.Message = fmt.Sprintf("cannot find endpoints for service/%s in %q", apiService.Spec.Service.Name, apiService.Spec.Service.Namespace)
apiregistrationv1apihelper.SetAPIServiceCondition(apiService, availableCondition) apiregistrationv1apihelper.SetAPIServiceCondition(apiService, availableCondition)
_, err := updateAPIServiceStatus(c.apiServiceClient, originalAPIService, apiService) _, err := c.updateAPIServiceStatus(originalAPIService, apiService)
return err return err
} else if err != nil { } else if err != nil {
availableCondition.Status = apiregistrationv1.ConditionUnknown availableCondition.Status = apiregistrationv1.ConditionUnknown
availableCondition.Reason = "EndpointsAccessError" availableCondition.Reason = "EndpointsAccessError"
availableCondition.Message = fmt.Sprintf("service/%s in %q cannot be checked due to: %v", apiService.Spec.Service.Name, apiService.Spec.Service.Namespace, err) availableCondition.Message = fmt.Sprintf("service/%s in %q cannot be checked due to: %v", apiService.Spec.Service.Name, apiService.Spec.Service.Namespace, err)
apiregistrationv1apihelper.SetAPIServiceCondition(apiService, availableCondition) apiregistrationv1apihelper.SetAPIServiceCondition(apiService, availableCondition)
_, err := updateAPIServiceStatus(c.apiServiceClient, originalAPIService, apiService) _, err := c.updateAPIServiceStatus(originalAPIService, apiService)
return err return err
} }
hasActiveEndpoints := false hasActiveEndpoints := false
...@@ -335,7 +353,7 @@ func (c *AvailableConditionController) sync(key string) error { ...@@ -335,7 +353,7 @@ func (c *AvailableConditionController) sync(key string) error {
availableCondition.Reason = "MissingEndpoints" availableCondition.Reason = "MissingEndpoints"
availableCondition.Message = fmt.Sprintf("endpoints for service/%s in %q have no addresses with port name %q", apiService.Spec.Service.Name, apiService.Spec.Service.Namespace, portName) availableCondition.Message = fmt.Sprintf("endpoints for service/%s in %q have no addresses with port name %q", apiService.Spec.Service.Name, apiService.Spec.Service.Namespace, portName)
apiregistrationv1apihelper.SetAPIServiceCondition(apiService, availableCondition) apiregistrationv1apihelper.SetAPIServiceCondition(apiService, availableCondition)
_, err := updateAPIServiceStatus(c.apiServiceClient, originalAPIService, apiService) _, err := c.updateAPIServiceStatus(originalAPIService, apiService)
return err return err
} }
} }
...@@ -413,7 +431,7 @@ func (c *AvailableConditionController) sync(key string) error { ...@@ -413,7 +431,7 @@ func (c *AvailableConditionController) sync(key string) error {
availableCondition.Reason = "FailedDiscoveryCheck" availableCondition.Reason = "FailedDiscoveryCheck"
availableCondition.Message = lastError.Error() availableCondition.Message = lastError.Error()
apiregistrationv1apihelper.SetAPIServiceCondition(apiService, availableCondition) apiregistrationv1apihelper.SetAPIServiceCondition(apiService, availableCondition)
_, updateErr := updateAPIServiceStatus(c.apiServiceClient, originalAPIService, apiService) _, updateErr := c.updateAPIServiceStatus(originalAPIService, apiService)
if updateErr != nil { if updateErr != nil {
return updateErr return updateErr
} }
...@@ -426,26 +444,26 @@ func (c *AvailableConditionController) sync(key string) error { ...@@ -426,26 +444,26 @@ func (c *AvailableConditionController) sync(key string) error {
availableCondition.Reason = "Passed" availableCondition.Reason = "Passed"
availableCondition.Message = "all checks passed" availableCondition.Message = "all checks passed"
apiregistrationv1apihelper.SetAPIServiceCondition(apiService, availableCondition) apiregistrationv1apihelper.SetAPIServiceCondition(apiService, availableCondition)
_, err = updateAPIServiceStatus(c.apiServiceClient, originalAPIService, apiService) _, err = c.updateAPIServiceStatus(originalAPIService, apiService)
return err return err
} }
// updateAPIServiceStatus only issues an update if a change is detected. We have a tight resync loop to quickly detect dead // updateAPIServiceStatus only issues an update if a change is detected. We have a tight resync loop to quickly detect dead
// apiservices. Doing that means we don't want to quickly issue no-op updates. // apiservices. Doing that means we don't want to quickly issue no-op updates.
func updateAPIServiceStatus(client apiregistrationclient.APIServicesGetter, originalAPIService, newAPIService *apiregistrationv1.APIService) (*apiregistrationv1.APIService, error) { func (c *AvailableConditionController) updateAPIServiceStatus(originalAPIService, newAPIService *apiregistrationv1.APIService) (*apiregistrationv1.APIService, error) {
// update this metric on every sync operation to reflect the actual state // update this metric on every sync operation to reflect the actual state
setUnavailableGauge(newAPIService) c.setUnavailableGauge(newAPIService)
if equality.Semantic.DeepEqual(originalAPIService.Status, newAPIService.Status) { if equality.Semantic.DeepEqual(originalAPIService.Status, newAPIService.Status) {
return newAPIService, nil return newAPIService, nil
} }
newAPIService, err := client.APIServices().UpdateStatus(context.TODO(), newAPIService, metav1.UpdateOptions{}) newAPIService, err := c.apiServiceClient.APIServices().UpdateStatus(context.TODO(), newAPIService, metav1.UpdateOptions{})
if err != nil { if err != nil {
return nil, err return nil, err
} }
setUnavailableCounter(originalAPIService, newAPIService) c.setUnavailableCounter(originalAPIService, newAPIService)
return newAPIService, nil return newAPIService, nil
} }
...@@ -630,17 +648,17 @@ func (c *AvailableConditionController) deleteEndpoints(obj interface{}) { ...@@ -630,17 +648,17 @@ func (c *AvailableConditionController) deleteEndpoints(obj interface{}) {
} }
// setUnavailableGauge set the metrics so that it reflect the current state base on availability of the given service // setUnavailableGauge set the metrics so that it reflect the current state base on availability of the given service
func setUnavailableGauge(newAPIService *apiregistrationv1.APIService) { func (c *AvailableConditionController) setUnavailableGauge(newAPIService *apiregistrationv1.APIService) {
if apiregistrationv1apihelper.IsAPIServiceConditionTrue(newAPIService, apiregistrationv1.Available) { if apiregistrationv1apihelper.IsAPIServiceConditionTrue(newAPIService, apiregistrationv1.Available) {
unavailableGauge.WithLabelValues(newAPIService.Name).Set(0.0) c.metrics.SetAPIServiceAvailable(newAPIService.Name)
return return
} }
unavailableGauge.WithLabelValues(newAPIService.Name).Set(1.0) c.metrics.SetAPIServiceUnavailable(newAPIService.Name)
} }
// setUnavailableCounter increases the metrics only if the given service is unavailable and its APIServiceCondition has changed // setUnavailableCounter increases the metrics only if the given service is unavailable and its APIServiceCondition has changed
func setUnavailableCounter(originalAPIService, newAPIService *apiregistrationv1.APIService) { func (c *AvailableConditionController) setUnavailableCounter(originalAPIService, newAPIService *apiregistrationv1.APIService) {
wasAvailable := apiregistrationv1apihelper.IsAPIServiceConditionTrue(originalAPIService, apiregistrationv1.Available) wasAvailable := apiregistrationv1apihelper.IsAPIServiceConditionTrue(originalAPIService, apiregistrationv1.Available)
isAvailable := apiregistrationv1apihelper.IsAPIServiceConditionTrue(newAPIService, apiregistrationv1.Available) isAvailable := apiregistrationv1apihelper.IsAPIServiceConditionTrue(newAPIService, apiregistrationv1.Available)
statusChanged := isAvailable != wasAvailable statusChanged := isAvailable != wasAvailable
...@@ -650,6 +668,6 @@ func setUnavailableCounter(originalAPIService, newAPIService *apiregistrationv1. ...@@ -650,6 +668,6 @@ func setUnavailableCounter(originalAPIService, newAPIService *apiregistrationv1.
if newCondition := apiregistrationv1apihelper.GetAPIServiceConditionByType(newAPIService, apiregistrationv1.Available); newCondition != nil { if newCondition := apiregistrationv1apihelper.GetAPIServiceConditionByType(newAPIService, apiregistrationv1.Available); newCondition != nil {
reason = newCondition.Reason reason = newCondition.Reason
} }
unavailableCounter.WithLabelValues(newAPIService.Name, reason).Inc() c.metrics.UnavailableCounter(newAPIService.Name, reason).Inc()
} }
} }
...@@ -17,8 +17,9 @@ limitations under the License. ...@@ -17,8 +17,9 @@ limitations under the License.
package apiserver package apiserver
import ( import (
"sync"
"k8s.io/component-base/metrics" "k8s.io/component-base/metrics"
"k8s.io/component-base/metrics/legacyregistry"
) )
/* /*
...@@ -30,25 +31,120 @@ import ( ...@@ -30,25 +31,120 @@ import (
* the metric stability policy. * the metric stability policy.
*/ */
var ( var (
unavailableCounter = metrics.NewCounterVec( unavailableGaugeDesc = metrics.NewDesc(
&metrics.CounterOpts{ "aggregator_unavailable_apiservice",
Name: "aggregator_unavailable_apiservice_total", "Gauge of APIServices which are marked as unavailable broken down by APIService name.",
Help: "Counter of APIServices which are marked as unavailable broken down by APIService name and reason.",
StabilityLevel: metrics.ALPHA,
},
[]string{"name", "reason"},
)
unavailableGauge = metrics.NewGaugeVec(
&metrics.GaugeOpts{
Name: "aggregator_unavailable_apiservice",
Help: "Gauge of APIServices which are marked as unavailable broken down by APIService name.",
StabilityLevel: metrics.ALPHA,
},
[]string{"name"}, []string{"name"},
nil,
metrics.ALPHA,
"",
) )
) )
func init() { type availabilityMetrics struct {
legacyregistry.MustRegister(unavailableCounter) unavailableCounter *metrics.CounterVec
legacyregistry.MustRegister(unavailableGauge)
*availabilityCollector
}
func newAvailabilityMetrics() *availabilityMetrics {
return &availabilityMetrics{
unavailableCounter: metrics.NewCounterVec(
&metrics.CounterOpts{
Name: "aggregator_unavailable_apiservice_total",
Help: "Counter of APIServices which are marked as unavailable broken down by APIService name and reason.",
StabilityLevel: metrics.ALPHA,
},
[]string{"name", "reason"},
),
availabilityCollector: newAvailabilityCollector(),
}
}
// Register registers apiservice availability metrics.
func (m *availabilityMetrics) Register(
registrationFunc func(metrics.Registerable) error,
customRegistrationFunc func(metrics.StableCollector) error,
) error {
err := registrationFunc(m.unavailableCounter)
if err != nil {
return err
}
err = customRegistrationFunc(m.availabilityCollector)
if err != nil {
return err
}
return nil
}
// UnavailableCounter returns a counter to track apiservices marked as unavailable.
func (m *availabilityMetrics) UnavailableCounter(apiServiceName, reason string) metrics.CounterMetric {
return m.unavailableCounter.WithLabelValues(apiServiceName, reason)
}
type availabilityCollector struct {
metrics.BaseStableCollector
mtx sync.RWMutex
availabilities map[string]bool
}
// Check if apiServiceStatusCollector implements necessary interface.
var _ metrics.StableCollector = &availabilityCollector{}
func newAvailabilityCollector() *availabilityCollector {
return &availabilityCollector{
availabilities: make(map[string]bool),
}
}
// DescribeWithStability implements the metrics.StableCollector interface.
func (c *availabilityCollector) DescribeWithStability(ch chan<- *metrics.Desc) {
ch <- unavailableGaugeDesc
}
// CollectWithStability implements the metrics.StableCollector interface.
func (c *availabilityCollector) CollectWithStability(ch chan<- metrics.Metric) {
c.mtx.RLock()
defer c.mtx.RUnlock()
for apiServiceName, isAvailable := range c.availabilities {
gaugeValue := 1.0
if isAvailable {
gaugeValue = 0.0
}
ch <- metrics.NewLazyConstMetric(
unavailableGaugeDesc,
metrics.GaugeValue,
gaugeValue,
apiServiceName,
)
}
}
// SetAPIServiceAvailable sets the given apiservice availability gauge to available.
func (c *availabilityCollector) SetAPIServiceAvailable(apiServiceKey string) {
c.setAPIServiceAvailability(apiServiceKey, true)
}
// SetAPIServiceUnavailable sets the given apiservice availability gauge to unavailable.
func (c *availabilityCollector) SetAPIServiceUnavailable(apiServiceKey string) {
c.setAPIServiceAvailability(apiServiceKey, false)
}
func (c *availabilityCollector) setAPIServiceAvailability(apiServiceKey string, availability bool) {
c.mtx.Lock()
defer c.mtx.Unlock()
c.availabilities[apiServiceKey] = availability
}
// ForgetAPIService removes the availability gauge of the given apiservice.
func (c *availabilityCollector) ForgetAPIService(apiServiceKey string) {
c.mtx.Lock()
defer c.mtx.Unlock()
delete(c.availabilities, apiServiceKey)
} }
...@@ -54,20 +54,20 @@ func (o *DeprecatedOptions) AddFlags(fs *pflag.FlagSet, cfg *kubeschedulerconfig ...@@ -54,20 +54,20 @@ func (o *DeprecatedOptions) AddFlags(fs *pflag.FlagSet, cfg *kubeschedulerconfig
fs.StringVar(&o.PolicyConfigMapNamespace, "policy-configmap-namespace", o.PolicyConfigMapNamespace, "DEPRECATED: the namespace where policy ConfigMap is located. The kube-system namespace will be used if this is not provided or is empty. Note: The scheduler will fail if this is combined with Plugin configs") fs.StringVar(&o.PolicyConfigMapNamespace, "policy-configmap-namespace", o.PolicyConfigMapNamespace, "DEPRECATED: the namespace where policy ConfigMap is located. The kube-system namespace will be used if this is not provided or is empty. Note: The scheduler will fail if this is combined with Plugin configs")
fs.BoolVar(&o.UseLegacyPolicyConfig, "use-legacy-policy-config", o.UseLegacyPolicyConfig, "DEPRECATED: when set to true, scheduler will ignore policy ConfigMap and uses policy config file. Note: The scheduler will fail if this is combined with Plugin configs") fs.BoolVar(&o.UseLegacyPolicyConfig, "use-legacy-policy-config", o.UseLegacyPolicyConfig, "DEPRECATED: when set to true, scheduler will ignore policy ConfigMap and uses policy config file. Note: The scheduler will fail if this is combined with Plugin configs")
fs.BoolVar(&cfg.EnableProfiling, "profiling", cfg.EnableProfiling, "DEPRECATED: enable profiling via web interface host:port/debug/pprof/") fs.BoolVar(&cfg.EnableProfiling, "profiling", cfg.EnableProfiling, "DEPRECATED: enable profiling via web interface host:port/debug/pprof/. This parameter is ignored if a config file is specified in --config.")
fs.BoolVar(&cfg.EnableContentionProfiling, "contention-profiling", cfg.EnableContentionProfiling, "DEPRECATED: enable lock contention profiling, if profiling is enabled") fs.BoolVar(&cfg.EnableContentionProfiling, "contention-profiling", cfg.EnableContentionProfiling, "DEPRECATED: enable lock contention profiling, if profiling is enabled. This parameter is ignored if a config file is specified in --config.")
fs.StringVar(&cfg.ClientConnection.Kubeconfig, "kubeconfig", cfg.ClientConnection.Kubeconfig, "DEPRECATED: path to kubeconfig file with authorization and master location information.") fs.StringVar(&cfg.ClientConnection.Kubeconfig, "kubeconfig", cfg.ClientConnection.Kubeconfig, "DEPRECATED: path to kubeconfig file with authorization and master location information. This parameter is ignored if a config file is specified in --config.")
fs.StringVar(&cfg.ClientConnection.ContentType, "kube-api-content-type", cfg.ClientConnection.ContentType, "DEPRECATED: content type of requests sent to apiserver.") fs.StringVar(&cfg.ClientConnection.ContentType, "kube-api-content-type", cfg.ClientConnection.ContentType, "DEPRECATED: content type of requests sent to apiserver.. This parameter is ignored if a config file is specified in --config.")
fs.Float32Var(&cfg.ClientConnection.QPS, "kube-api-qps", cfg.ClientConnection.QPS, "DEPRECATED: QPS to use while talking with kubernetes apiserver") fs.Float32Var(&cfg.ClientConnection.QPS, "kube-api-qps", cfg.ClientConnection.QPS, "DEPRECATED: QPS to use while talking with kubernetes apiserver. This parameter is ignored if a config file is specified in --config.")
fs.Int32Var(&cfg.ClientConnection.Burst, "kube-api-burst", cfg.ClientConnection.Burst, "DEPRECATED: burst to use while talking with kubernetes apiserver") fs.Int32Var(&cfg.ClientConnection.Burst, "kube-api-burst", cfg.ClientConnection.Burst, "DEPRECATED: burst to use while talking with kubernetes apiserver. This parameter is ignored if a config file is specified in --config.")
fs.StringVar(&cfg.LeaderElection.ResourceNamespace, "lock-object-namespace", cfg.LeaderElection.ResourceNamespace, "DEPRECATED: define the namespace of the lock object. Will be removed in favor of leader-elect-resource-namespace.") fs.StringVar(&cfg.LeaderElection.ResourceNamespace, "lock-object-namespace", cfg.LeaderElection.ResourceNamespace, "DEPRECATED: define the namespace of the lock object. Will be removed in favor of leader-elect-resource-namespace. This parameter is ignored if a config file is specified in --config.")
fs.StringVar(&cfg.LeaderElection.ResourceName, "lock-object-name", cfg.LeaderElection.ResourceName, "DEPRECATED: define the name of the lock object. Will be removed in favor of leader-elect-resource-name") fs.StringVar(&cfg.LeaderElection.ResourceName, "lock-object-name", cfg.LeaderElection.ResourceName, "DEPRECATED: define the name of the lock object. Will be removed in favor of leader-elect-resource-name. This parameter is ignored if a config file is specified in --config.")
fs.Int32Var(&o.HardPodAffinitySymmetricWeight, "hard-pod-affinity-symmetric-weight", o.HardPodAffinitySymmetricWeight, fs.Int32Var(&o.HardPodAffinitySymmetricWeight, "hard-pod-affinity-symmetric-weight", o.HardPodAffinitySymmetricWeight,
"DEPRECATED: RequiredDuringScheduling affinity is not symmetric, but there is an implicit PreferredDuringScheduling affinity rule corresponding "+ "DEPRECATED: RequiredDuringScheduling affinity is not symmetric, but there is an implicit PreferredDuringScheduling affinity rule corresponding "+
"to every RequiredDuringScheduling affinity rule. --hard-pod-affinity-symmetric-weight represents the weight of implicit PreferredDuringScheduling affinity rule. Must be in the range 0-100."+ "to every RequiredDuringScheduling affinity rule. --hard-pod-affinity-symmetric-weight represents the weight of implicit PreferredDuringScheduling affinity rule. Must be in the range 0-100."+
"This option was moved to the policy configuration file") "This parameter is ignored if a config file is specified in --config.")
fs.StringVar(&o.SchedulerName, "scheduler-name", o.SchedulerName, "DEPRECATED: name of the scheduler, used to select which pods will be processed by this scheduler, based on pod's \"spec.schedulerName\".") fs.StringVar(&o.SchedulerName, "scheduler-name", o.SchedulerName, "DEPRECATED: name of the scheduler, used to select which pods will be processed by this scheduler, based on pod's \"spec.schedulerName\".This parameter is ignored if a config file is specified in --config.")
// MarkDeprecated hides the flag from the help. We don't want that: // MarkDeprecated hides the flag from the help. We don't want that:
// fs.MarkDeprecated("hard-pod-affinity-symmetric-weight", "This option was moved to the policy configuration file") // fs.MarkDeprecated("hard-pod-affinity-symmetric-weight", "This option was moved to the policy configuration file")
} }
......
...@@ -44,10 +44,10 @@ func (o *CombinedInsecureServingOptions) AddFlags(fs *pflag.FlagSet) { ...@@ -44,10 +44,10 @@ func (o *CombinedInsecureServingOptions) AddFlags(fs *pflag.FlagSet) {
return return
} }
fs.StringVar(&o.BindAddress, "address", o.BindAddress, "DEPRECATED: the IP address on which to listen for the --port port (set to 0.0.0.0 for all IPv4 interfaces and :: for all IPv6 interfaces). See --bind-address instead.") fs.StringVar(&o.BindAddress, "address", o.BindAddress, "DEPRECATED: the IP address on which to listen for the --port port (set to 0.0.0.0 for all IPv4 interfaces and :: for all IPv6 interfaces). See --bind-address instead. This parameter is ignored if a config file is specified in --config.")
// MarkDeprecated hides the flag from the help. We don't want that: // MarkDeprecated hides the flag from the help. We don't want that:
// fs.MarkDeprecated("address", "see --bind-address instead.") // fs.MarkDeprecated("address", "see --bind-address instead.")
fs.IntVar(&o.BindPort, "port", o.BindPort, "DEPRECATED: the port on which to serve HTTP insecurely without authentication and authorization. If 0, don't serve plain HTTP at all. See --secure-port instead.") fs.IntVar(&o.BindPort, "port", o.BindPort, "DEPRECATED: the port on which to serve HTTP insecurely without authentication and authorization. If 0, don't serve plain HTTP at all. See --secure-port instead. This parameter is ignored if a config file is specified in --config.")
// MarkDeprecated hides the flag from the help. We don't want that: // MarkDeprecated hides the flag from the help. We don't want that:
// fs.MarkDeprecated("port", "see --secure-port instead.") // fs.MarkDeprecated("port", "see --secure-port instead.")
} }
......
...@@ -152,12 +152,10 @@ func newDefaultComponentConfig() (*kubeschedulerconfig.KubeSchedulerConfiguratio ...@@ -152,12 +152,10 @@ func newDefaultComponentConfig() (*kubeschedulerconfig.KubeSchedulerConfiguratio
func (o *Options) Flags() (nfs cliflag.NamedFlagSets) { func (o *Options) Flags() (nfs cliflag.NamedFlagSets) {
fs := nfs.FlagSet("misc") fs := nfs.FlagSet("misc")
fs.StringVar(&o.ConfigFile, "config", o.ConfigFile, `The path to the configuration file. The following flags can overwrite fields in this file: fs.StringVar(&o.ConfigFile, "config", o.ConfigFile, `The path to the configuration file. The following flags can overwrite fields in this file:
--address --algorithm-provider
--port
--use-legacy-policy-config
--policy-configmap
--policy-config-file --policy-config-file
--algorithm-provider`) --policy-configmap
--policy-configmap-namespace`)
fs.StringVar(&o.WriteConfigTo, "write-config-to", o.WriteConfigTo, "If set, write the configuration values to this file and exit.") fs.StringVar(&o.WriteConfigTo, "write-config-to", o.WriteConfigTo, "If set, write the configuration values to this file and exit.")
fs.StringVar(&o.Master, "master", o.Master, "The address of the Kubernetes API server (overrides any value in kubeconfig)") fs.StringVar(&o.Master, "master", o.Master, "The address of the Kubernetes API server (overrides any value in kubeconfig)")
......
...@@ -471,12 +471,12 @@ func (dc *DisruptionController) getPdbForPod(pod *v1.Pod) *policy.PodDisruptionB ...@@ -471,12 +471,12 @@ func (dc *DisruptionController) getPdbForPod(pod *v1.Pod) *policy.PodDisruptionB
// IMPORTANT NOTE : the returned pods should NOT be modified. // IMPORTANT NOTE : the returned pods should NOT be modified.
func (dc *DisruptionController) getPodsForPdb(pdb *policy.PodDisruptionBudget) ([]*v1.Pod, error) { func (dc *DisruptionController) getPodsForPdb(pdb *policy.PodDisruptionBudget) ([]*v1.Pod, error) {
sel, err := metav1.LabelSelectorAsSelector(pdb.Spec.Selector) sel, err := metav1.LabelSelectorAsSelector(pdb.Spec.Selector)
if sel.Empty() {
return []*v1.Pod{}, nil
}
if err != nil { if err != nil {
return []*v1.Pod{}, err return []*v1.Pod{}, err
} }
if sel.Empty() {
return []*v1.Pod{}, nil
}
pods, err := dc.podLister.Pods(pdb.Namespace).List(sel) pods, err := dc.podLister.Pods(pdb.Namespace).List(sel)
if err != nil { if err != nil {
return []*v1.Pod{}, err return []*v1.Pod{}, err
......
...@@ -385,7 +385,11 @@ func (adc *attachDetachController) populateActualStateOfWorld() error { ...@@ -385,7 +385,11 @@ func (adc *attachDetachController) populateActualStateOfWorld() error {
adc.addNodeToDswp(node, types.NodeName(node.Name)) adc.addNodeToDswp(node, types.NodeName(node.Name))
} }
} }
return nil err = adc.processVolumeAttachments()
if err != nil {
klog.Errorf("Failed to process volume attachments: %v", err)
}
return err
} }
func (adc *attachDetachController) getNodeVolumeDevicePath( func (adc *attachDetachController) getNodeVolumeDevicePath(
...@@ -455,7 +459,12 @@ func (adc *attachDetachController) populateDesiredStateOfWorld() error { ...@@ -455,7 +459,12 @@ func (adc *attachDetachController) populateDesiredStateOfWorld() error {
err) err)
continue continue
} }
if adc.actualStateOfWorld.IsVolumeAttachedToNode(volumeName, nodeName) { attachState := adc.actualStateOfWorld.GetAttachState(volumeName, nodeName)
if attachState == cache.AttachStateAttached {
klog.V(10).Infof("Volume %q is attached to node %q. Marking as attached in ActualStateOfWorld",
volumeName,
nodeName,
)
devicePath, err := adc.getNodeVolumeDevicePath(volumeName, nodeName) devicePath, err := adc.getNodeVolumeDevicePath(volumeName, nodeName)
if err != nil { if err != nil {
klog.Errorf("Failed to find device path: %v", err) klog.Errorf("Failed to find device path: %v", err)
...@@ -673,6 +682,67 @@ func (adc *attachDetachController) processVolumesInUse( ...@@ -673,6 +682,67 @@ func (adc *attachDetachController) processVolumesInUse(
} }
} }
// Process Volume-Attachment objects.
// Should be called only after populating attached volumes in the ASW.
// For each VA object, this function checks if its present in the ASW.
// If not, adds the volume to ASW as an "uncertain" attachment.
// In the reconciler, the logic checks if the volume is present in the DSW;
// if yes, the reconciler will attempt attach on the volume;
// if not (could be a dangling attachment), the reconciler will detach this volume.
func (adc *attachDetachController) processVolumeAttachments() error {
vas, err := adc.volumeAttachmentLister.List(labels.Everything())
if err != nil {
klog.Errorf("failed to list VolumeAttachment objects: %v", err)
return err
}
for _, va := range vas {
nodeName := types.NodeName(va.Spec.NodeName)
pvName := va.Spec.Source.PersistentVolumeName
if pvName == nil {
// Currently VA objects are created for CSI volumes only. nil pvName is unexpected, generate a warning
klog.Warningf("Skipping the va as its pvName is nil, va.Name: %q, nodeName: %q",
va.Name, nodeName)
continue
}
pv, err := adc.pvLister.Get(*pvName)
if err != nil {
klog.Errorf("Unable to lookup pv object for: %q, err: %v", *pvName, err)
continue
}
volumeSpec := volume.NewSpecFromPersistentVolume(pv, false)
plugin, err := adc.volumePluginMgr.FindAttachablePluginBySpec(volumeSpec)
if err != nil || plugin == nil {
// Currently VA objects are created for CSI volumes only. nil plugin is unexpected, generate a warning
klog.Warningf(
"Skipping processing the volume %q on nodeName: %q, no attacher interface found. err=%v",
*pvName,
nodeName,
err)
continue
}
volumeName, err := volumeutil.GetUniqueVolumeNameFromSpec(plugin, volumeSpec)
if err != nil {
klog.Errorf(
"Failed to find unique name for volume:%q, va.Name:%q, nodeName:%q: %v",
*pvName,
va.Name,
nodeName,
err)
continue
}
attachState := adc.actualStateOfWorld.GetAttachState(volumeName, nodeName)
if attachState == cache.AttachStateDetached {
klog.V(1).Infof("Marking volume attachment as uncertain as volume:%q (%q) is not attached (%v)",
volumeName, nodeName, attachState)
err = adc.actualStateOfWorld.MarkVolumeAsUncertain(volumeName, volumeSpec, nodeName)
if err != nil {
klog.Errorf("MarkVolumeAsUncertain fail to add the volume %q (%q) to ASW. err: %s", volumeName, nodeName, err)
}
}
}
return nil
}
var _ volume.VolumeHost = &attachDetachController{} var _ volume.VolumeHost = &attachDetachController{}
var _ volume.AttachDetachVolumeHost = &attachDetachController{} var _ volume.AttachDetachVolumeHost = &attachDetachController{}
......
...@@ -96,10 +96,13 @@ type ActualStateOfWorld interface { ...@@ -96,10 +96,13 @@ type ActualStateOfWorld interface {
// nodes, the volume is also deleted. // nodes, the volume is also deleted.
DeleteVolumeNode(volumeName v1.UniqueVolumeName, nodeName types.NodeName) DeleteVolumeNode(volumeName v1.UniqueVolumeName, nodeName types.NodeName)
// IsVolumeAttachedToNode returns true if the specified volume/node combo exists // GetAttachState returns the attach state for the given volume-node
// in the underlying store indicating the specified volume is attached to // combination.
// the specified node. // Returns AttachStateAttached if the specified volume/node combo exists in
IsVolumeAttachedToNode(volumeName v1.UniqueVolumeName, nodeName types.NodeName) bool // the underlying store indicating the specified volume is attached to the
// specified node, AttachStateDetached if the combo does not exist, or
// AttachStateUncertain if the attached state is marked as uncertain.
GetAttachState(volumeName v1.UniqueVolumeName, nodeName types.NodeName) AttachState
// GetAttachedVolumes generates and returns a list of volumes/node pairs // GetAttachedVolumes generates and returns a list of volumes/node pairs
// reflecting which volumes might attached to which nodes based on the // reflecting which volumes might attached to which nodes based on the
...@@ -153,6 +156,31 @@ type AttachedVolume struct { ...@@ -153,6 +156,31 @@ type AttachedVolume struct {
DetachRequestedTime time.Time DetachRequestedTime time.Time
} }
// AttachState represents the attach state of a volume to a node known to the
// Actual State of World.
// This type is used as external representation of attach state (specifically
// as the return type of GetAttachState only); the state is represented
// differently in the internal cache implementation.
type AttachState int
const (
// AttachStateAttached represents the state in which the volume is attached to
// the node.
AttachStateAttached AttachState = iota
// AttachStateUncertain represents the state in which the Actual State of World
// does not know whether the volume is attached to the node.
AttachStateUncertain
// AttachStateDetached represents the state in which the volume is not
// attached to the node.
AttachStateDetached
)
func (s AttachState) String() string {
return []string{"Attached", "Uncertain", "Detached"}[s]
}
// NewActualStateOfWorld returns a new instance of ActualStateOfWorld. // NewActualStateOfWorld returns a new instance of ActualStateOfWorld.
func NewActualStateOfWorld(volumePluginMgr *volume.VolumePluginMgr) ActualStateOfWorld { func NewActualStateOfWorld(volumePluginMgr *volume.VolumePluginMgr) ActualStateOfWorld {
return &actualStateOfWorld{ return &actualStateOfWorld{
...@@ -530,19 +558,22 @@ func (asw *actualStateOfWorld) DeleteVolumeNode( ...@@ -530,19 +558,22 @@ func (asw *actualStateOfWorld) DeleteVolumeNode(
asw.removeVolumeFromReportAsAttached(volumeName, nodeName) asw.removeVolumeFromReportAsAttached(volumeName, nodeName)
} }
func (asw *actualStateOfWorld) IsVolumeAttachedToNode( func (asw *actualStateOfWorld) GetAttachState(
volumeName v1.UniqueVolumeName, nodeName types.NodeName) bool { volumeName v1.UniqueVolumeName, nodeName types.NodeName) AttachState {
asw.RLock() asw.RLock()
defer asw.RUnlock() defer asw.RUnlock()
volumeObj, volumeExists := asw.attachedVolumes[volumeName] volumeObj, volumeExists := asw.attachedVolumes[volumeName]
if volumeExists { if volumeExists {
if node, nodeExists := volumeObj.nodesAttachedTo[nodeName]; nodeExists { if node, nodeExists := volumeObj.nodesAttachedTo[nodeName]; nodeExists {
return node.attachedConfirmed if node.attachedConfirmed {
return AttachStateAttached
}
return AttachStateUncertain
} }
} }
return false return AttachStateDetached
} }
func (asw *actualStateOfWorld) GetAttachedVolumes() []AttachedVolume { func (asw *actualStateOfWorld) GetAttachedVolumes() []AttachedVolume {
......
...@@ -142,6 +142,7 @@ func (rc *reconciler) reconcile() { ...@@ -142,6 +142,7 @@ func (rc *reconciler) reconcile() {
for _, attachedVolume := range rc.actualStateOfWorld.GetAttachedVolumes() { for _, attachedVolume := range rc.actualStateOfWorld.GetAttachedVolumes() {
if !rc.desiredStateOfWorld.VolumeExists( if !rc.desiredStateOfWorld.VolumeExists(
attachedVolume.VolumeName, attachedVolume.NodeName) { attachedVolume.VolumeName, attachedVolume.NodeName) {
// Check whether there already exist an operation pending, and don't even // Check whether there already exist an operation pending, and don't even
// try to start an operation if there is already one running. // try to start an operation if there is already one running.
// This check must be done before we do any other checks, as otherwise the other checks // This check must be done before we do any other checks, as otherwise the other checks
...@@ -161,6 +162,21 @@ func (rc *reconciler) reconcile() { ...@@ -161,6 +162,21 @@ func (rc *reconciler) reconcile() {
} }
} }
// Because the detach operation updates the ActualStateOfWorld before
// marking itself complete, it's possible for the volume to be removed
// from the ActualStateOfWorld between the GetAttachedVolumes() check
// and the IsOperationPending() check above.
// Check the ActualStateOfWorld again to avoid issuing an unnecessary
// detach.
// See https://github.com/kubernetes/kubernetes/issues/93902
attachState := rc.actualStateOfWorld.GetAttachState(attachedVolume.VolumeName, attachedVolume.NodeName)
if attachState == cache.AttachStateDetached {
if klog.V(5).Enabled() {
klog.Infof(attachedVolume.GenerateMsgDetailed("Volume detached--skipping", ""))
}
continue
}
// Set the detach request time // Set the detach request time
elapsedTime, err := rc.actualStateOfWorld.SetDetachRequestTime(attachedVolume.VolumeName, attachedVolume.NodeName) elapsedTime, err := rc.actualStateOfWorld.SetDetachRequestTime(attachedVolume.VolumeName, attachedVolume.NodeName)
if err != nil { if err != nil {
...@@ -226,17 +242,7 @@ func (rc *reconciler) reconcile() { ...@@ -226,17 +242,7 @@ func (rc *reconciler) reconcile() {
func (rc *reconciler) attachDesiredVolumes() { func (rc *reconciler) attachDesiredVolumes() {
// Ensure volumes that should be attached are attached. // Ensure volumes that should be attached are attached.
for _, volumeToAttach := range rc.desiredStateOfWorld.GetVolumesToAttach() { for _, volumeToAttach := range rc.desiredStateOfWorld.GetVolumesToAttach() {
if rc.actualStateOfWorld.IsVolumeAttachedToNode(volumeToAttach.VolumeName, volumeToAttach.NodeName) {
// Volume/Node exists, touch it to reset detachRequestedTime
if klog.V(5).Enabled() {
klog.Infof(volumeToAttach.GenerateMsgDetailed("Volume attached--touching", ""))
}
rc.actualStateOfWorld.ResetDetachRequestTime(volumeToAttach.VolumeName, volumeToAttach.NodeName)
continue
}
if util.IsMultiAttachAllowed(volumeToAttach.VolumeSpec) { if util.IsMultiAttachAllowed(volumeToAttach.VolumeSpec) {
// Don't even try to start an operation if there is already one running for the given volume and node. // Don't even try to start an operation if there is already one running for the given volume and node.
if rc.attacherDetacher.IsOperationPending(volumeToAttach.VolumeName, "" /* podName */, volumeToAttach.NodeName) { if rc.attacherDetacher.IsOperationPending(volumeToAttach.VolumeName, "" /* podName */, volumeToAttach.NodeName) {
if klog.V(10).Enabled() { if klog.V(10).Enabled() {
...@@ -244,9 +250,7 @@ func (rc *reconciler) attachDesiredVolumes() { ...@@ -244,9 +250,7 @@ func (rc *reconciler) attachDesiredVolumes() {
} }
continue continue
} }
} else { } else {
// Don't even try to start an operation if there is already one running for the given volume // Don't even try to start an operation if there is already one running for the given volume
if rc.attacherDetacher.IsOperationPending(volumeToAttach.VolumeName, "" /* podName */, "" /* nodeName */) { if rc.attacherDetacher.IsOperationPending(volumeToAttach.VolumeName, "" /* podName */, "" /* nodeName */) {
if klog.V(10).Enabled() { if klog.V(10).Enabled() {
...@@ -254,7 +258,24 @@ func (rc *reconciler) attachDesiredVolumes() { ...@@ -254,7 +258,24 @@ func (rc *reconciler) attachDesiredVolumes() {
} }
continue continue
} }
}
// Because the attach operation updates the ActualStateOfWorld before
// marking itself complete, IsOperationPending() must be checked before
// GetAttachState() to guarantee the ActualStateOfWorld is
// up-to-date when it's read.
// See https://github.com/kubernetes/kubernetes/issues/93902
attachState := rc.actualStateOfWorld.GetAttachState(volumeToAttach.VolumeName, volumeToAttach.NodeName)
if attachState == cache.AttachStateAttached {
// Volume/Node exists, touch it to reset detachRequestedTime
if klog.V(5).Enabled() {
klog.Infof(volumeToAttach.GenerateMsgDetailed("Volume attached--touching", ""))
}
rc.actualStateOfWorld.ResetDetachRequestTime(volumeToAttach.VolumeName, volumeToAttach.NodeName)
continue
}
if !util.IsMultiAttachAllowed(volumeToAttach.VolumeSpec) {
nodes := rc.actualStateOfWorld.GetNodesForAttachedVolume(volumeToAttach.VolumeName) nodes := rc.actualStateOfWorld.GetNodesForAttachedVolume(volumeToAttach.VolumeName)
if len(nodes) > 0 { if len(nodes) > 0 {
if !volumeToAttach.MultiAttachErrorReported { if !volumeToAttach.MultiAttachErrorReported {
...@@ -263,7 +284,6 @@ func (rc *reconciler) attachDesiredVolumes() { ...@@ -263,7 +284,6 @@ func (rc *reconciler) attachDesiredVolumes() {
} }
continue continue
} }
} }
// Volume/Node doesn't exist, spawn a goroutine to attach it // Volume/Node doesn't exist, spawn a goroutine to attach it
......
...@@ -167,6 +167,8 @@ func NewManager(numaNodeInfo cputopology.NUMANodeInfo, topologyPolicyName string ...@@ -167,6 +167,8 @@ func NewManager(numaNodeInfo cputopology.NUMANodeInfo, topologyPolicyName string
} }
func (m *manager) GetAffinity(podUID string, containerName string) TopologyHint { func (m *manager) GetAffinity(podUID string, containerName string) TopologyHint {
m.mutex.Lock()
defer m.mutex.Unlock()
return m.podTopologyHints[podUID][containerName] return m.podTopologyHints[podUID][containerName]
} }
...@@ -256,10 +258,12 @@ func (m *manager) Admit(attrs *lifecycle.PodAdmitAttributes) lifecycle.PodAdmitR ...@@ -256,10 +258,12 @@ func (m *manager) Admit(attrs *lifecycle.PodAdmitAttributes) lifecycle.PodAdmitR
} }
klog.Infof("[topologymanager] Topology Affinity for (pod: %v container: %v): %v", pod.UID, container.Name, result) klog.Infof("[topologymanager] Topology Affinity for (pod: %v container: %v): %v", pod.UID, container.Name, result)
m.mutex.Lock()
if m.podTopologyHints[string(pod.UID)] == nil { if m.podTopologyHints[string(pod.UID)] == nil {
m.podTopologyHints[string(pod.UID)] = make(map[string]TopologyHint) m.podTopologyHints[string(pod.UID)] = make(map[string]TopologyHint)
} }
m.podTopologyHints[string(pod.UID)][container.Name] = result m.podTopologyHints[string(pod.UID)][container.Name] = result
m.mutex.Unlock()
err := m.allocateAlignedResources(pod, &container) err := m.allocateAlignedResources(pod, &container)
if err != nil { if err != nil {
......
...@@ -24,6 +24,7 @@ import ( ...@@ -24,6 +24,7 @@ import (
"net/http" "net/http"
"path" "path"
"path/filepath" "path/filepath"
"runtime"
"sync" "sync"
"time" "time"
...@@ -255,24 +256,28 @@ func NewDockerService(config *ClientConfig, podSandboxImage string, streamingCon ...@@ -255,24 +256,28 @@ func NewDockerService(config *ClientConfig, podSandboxImage string, streamingCon
ds.network = network.NewPluginManager(plug) ds.network = network.NewPluginManager(plug)
klog.Infof("Docker cri networking managed by %v", plug.Name()) klog.Infof("Docker cri networking managed by %v", plug.Name())
// NOTE: cgroup driver is only detectable in docker 1.11+ // skipping cgroup driver checks for Windows
cgroupDriver := defaultCgroupDriver if runtime.GOOS == "linux" {
dockerInfo, err := ds.client.Info() // NOTE: cgroup driver is only detectable in docker 1.11+
klog.Infof("Docker Info: %+v", dockerInfo) cgroupDriver := defaultCgroupDriver
if err != nil { dockerInfo, err := ds.client.Info()
klog.Errorf("Failed to execute Info() call to the Docker client: %v", err) klog.Infof("Docker Info: %+v", dockerInfo)
klog.Warningf("Falling back to use the default driver: %q", cgroupDriver) if err != nil {
} else if len(dockerInfo.CgroupDriver) == 0 { klog.Errorf("Failed to execute Info() call to the Docker client: %v", err)
klog.Warningf("No cgroup driver is set in Docker") klog.Warningf("Falling back to use the default driver: %q", cgroupDriver)
klog.Warningf("Falling back to use the default driver: %q", cgroupDriver) } else if len(dockerInfo.CgroupDriver) == 0 {
} else { klog.Warningf("No cgroup driver is set in Docker")
cgroupDriver = dockerInfo.CgroupDriver klog.Warningf("Falling back to use the default driver: %q", cgroupDriver)
} } else {
if len(kubeCgroupDriver) != 0 && kubeCgroupDriver != cgroupDriver { cgroupDriver = dockerInfo.CgroupDriver
return nil, fmt.Errorf("misconfiguration: kubelet cgroup driver: %q is different from docker cgroup driver: %q", kubeCgroupDriver, cgroupDriver) }
if len(kubeCgroupDriver) != 0 && kubeCgroupDriver != cgroupDriver {
return nil, fmt.Errorf("misconfiguration: kubelet cgroup driver: %q is different from docker cgroup driver: %q", kubeCgroupDriver, cgroupDriver)
}
klog.Infof("Setting cgroupDriver to %s", cgroupDriver)
ds.cgroupDriver = cgroupDriver
} }
klog.Infof("Setting cgroupDriver to %s", cgroupDriver)
ds.cgroupDriver = cgroupDriver
ds.versionCache = cache.NewObjectCache( ds.versionCache = cache.NewObjectCache(
func() (interface{}, error) { func() (interface{}, error) {
return ds.getDockerVersion() return ds.getDockerVersion()
......
...@@ -125,6 +125,9 @@ const ( ...@@ -125,6 +125,9 @@ const (
// Max amount of time to wait for the container runtime to come up. // Max amount of time to wait for the container runtime to come up.
maxWaitForContainerRuntime = 30 * time.Second maxWaitForContainerRuntime = 30 * time.Second
// Max amount of time to wait for node list/watch to initially sync
maxWaitForAPIServerSync = 10 * time.Second
// nodeStatusUpdateRetry specifies how many times kubelet retries when posting node status failed. // nodeStatusUpdateRetry specifies how many times kubelet retries when posting node status failed.
nodeStatusUpdateRetry = 5 nodeStatusUpdateRetry = 5
...@@ -431,14 +434,30 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration, ...@@ -431,14 +434,30 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
serviceHasSynced = func() bool { return true } serviceHasSynced = func() bool { return true }
} }
nodeIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{}) var nodeHasSynced cache.InformerSynced
var nodeLister corelisters.NodeLister
if kubeDeps.KubeClient != nil { if kubeDeps.KubeClient != nil {
fieldSelector := fields.Set{api.ObjectNameField: string(nodeName)}.AsSelector() kubeInformers := informers.NewSharedInformerFactoryWithOptions(kubeDeps.KubeClient, 0, informers.WithTweakListOptions(func(options *metav1.ListOptions) {
nodeLW := cache.NewListWatchFromClient(kubeDeps.KubeClient.CoreV1().RESTClient(), "nodes", metav1.NamespaceAll, fieldSelector) options.FieldSelector = fields.Set{api.ObjectNameField: string(nodeName)}.String()
r := cache.NewReflector(nodeLW, &v1.Node{}, nodeIndexer, 0) }))
go r.Run(wait.NeverStop) nodeLister = kubeInformers.Core().V1().Nodes().Lister()
nodeHasSynced = func() bool {
if kubeInformers.Core().V1().Nodes().Informer().HasSynced() {
return true
}
klog.Infof("kubelet nodes not sync")
return false
}
kubeInformers.Start(wait.NeverStop)
klog.Info("Kubelet client is not nil")
} else {
// we dont have a client to sync!
nodeIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
nodeLister = corelisters.NewNodeLister(nodeIndexer)
nodeHasSynced = func() bool { return true }
klog.Info("Kubelet client is nil")
} }
nodeLister := corelisters.NewNodeLister(nodeIndexer)
// TODO: get the real node object of ourself, // TODO: get the real node object of ourself,
// and use the real node name and UID. // and use the real node name and UID.
...@@ -489,6 +508,7 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration, ...@@ -489,6 +508,7 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
serviceLister: serviceLister, serviceLister: serviceLister,
serviceHasSynced: serviceHasSynced, serviceHasSynced: serviceHasSynced,
nodeLister: nodeLister, nodeLister: nodeLister,
nodeHasSynced: nodeHasSynced,
masterServiceNamespace: masterServiceNamespace, masterServiceNamespace: masterServiceNamespace,
streamingConnectionIdleTimeout: kubeCfg.StreamingConnectionIdleTimeout.Duration, streamingConnectionIdleTimeout: kubeCfg.StreamingConnectionIdleTimeout.Duration,
recorder: kubeDeps.Recorder, recorder: kubeDeps.Recorder,
...@@ -876,7 +896,9 @@ type Kubelet struct { ...@@ -876,7 +896,9 @@ type Kubelet struct {
serviceHasSynced cache.InformerSynced serviceHasSynced cache.InformerSynced
// nodeLister knows how to list nodes // nodeLister knows how to list nodes
nodeLister corelisters.NodeLister nodeLister corelisters.NodeLister
// nodeHasSynced indicates whether nodes have been sync'd at least once.
// Check this before trusting a response from the node lister.
nodeHasSynced cache.InformerSynced
// a list of node labels to register // a list of node labels to register
nodeLabels map[string]string nodeLabels map[string]string
...@@ -1420,6 +1442,13 @@ func (kl *Kubelet) syncPod(o syncPodOptions) error { ...@@ -1420,6 +1442,13 @@ func (kl *Kubelet) syncPod(o syncPodOptions) error {
return nil return nil
} }
// If the pod is a static pod and its mirror pod is still gracefully terminating,
// we do not want to start the new static pod until the old static pod is gracefully terminated.
podFullName := kubecontainer.GetPodFullName(pod)
if kl.podKiller.IsMirrorPodPendingTerminationByPodName(podFullName) {
return fmt.Errorf("pod %q is pending termination", podFullName)
}
// Latency measurements for the main workflow are relative to the // Latency measurements for the main workflow are relative to the
// first time the pod was seen by the API server. // first time the pod was seen by the API server.
var firstSeenTime time.Time var firstSeenTime time.Time
...@@ -1553,7 +1582,6 @@ func (kl *Kubelet) syncPod(o syncPodOptions) error { ...@@ -1553,7 +1582,6 @@ func (kl *Kubelet) syncPod(o syncPodOptions) error {
// Create Mirror Pod for Static Pod if it doesn't already exist // Create Mirror Pod for Static Pod if it doesn't already exist
if kubetypes.IsStaticPod(pod) { if kubetypes.IsStaticPod(pod) {
podFullName := kubecontainer.GetPodFullName(pod)
deleted := false deleted := false
if mirrorPod != nil { if mirrorPod != nil {
if mirrorPod.DeletionTimestamp != nil || !kl.podManager.IsMirrorPodOf(mirrorPod, pod) { if mirrorPod.DeletionTimestamp != nil || !kl.podManager.IsMirrorPodOf(mirrorPod, pod) {
...@@ -1684,6 +1712,9 @@ func (kl *Kubelet) deletePod(pod *v1.Pod) error { ...@@ -1684,6 +1712,9 @@ func (kl *Kubelet) deletePod(pod *v1.Pod) error {
} }
podPair := kubecontainer.PodPair{APIPod: pod, RunningPod: &runningPod} podPair := kubecontainer.PodPair{APIPod: pod, RunningPod: &runningPod}
if _, ok := kl.podManager.GetMirrorPodByPod(pod); ok {
kl.podKiller.MarkMirrorPodPendingTermination(pod)
}
kl.podKiller.KillPod(&podPair) kl.podKiller.KillPod(&podPair)
// TODO: delete the mirror pod here? // TODO: delete the mirror pod here?
...@@ -2025,9 +2056,6 @@ func (kl *Kubelet) HandlePodRemoves(pods []*v1.Pod) { ...@@ -2025,9 +2056,6 @@ func (kl *Kubelet) HandlePodRemoves(pods []*v1.Pod) {
kl.handleMirrorPod(pod, start) kl.handleMirrorPod(pod, start)
continue continue
} }
if _, ok := kl.podManager.GetMirrorPodByPod(pod); ok {
kl.podKiller.MarkMirrorPodPendingTermination(pod)
}
// Deletion is allowed to fail because the periodic cleanup routine // Deletion is allowed to fail because the periodic cleanup routine
// will trigger deletion again. // will trigger deletion again.
if err := kl.deletePod(pod); err != nil { if err := kl.deletePod(pod); err != nil {
......
...@@ -22,6 +22,7 @@ import ( ...@@ -22,6 +22,7 @@ import (
"io/ioutil" "io/ioutil"
"net" "net"
"path/filepath" "path/filepath"
"time"
cadvisorapiv1 "github.com/google/cadvisor/info/v1" cadvisorapiv1 "github.com/google/cadvisor/info/v1"
cadvisorv2 "github.com/google/cadvisor/info/v2" cadvisorv2 "github.com/google/cadvisor/info/v2"
...@@ -32,6 +33,7 @@ import ( ...@@ -32,6 +33,7 @@ import (
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/kubernetes/pkg/kubelet/cm" "k8s.io/kubernetes/pkg/kubelet/cm"
"k8s.io/kubernetes/pkg/kubelet/config" "k8s.io/kubernetes/pkg/kubelet/config"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
...@@ -235,6 +237,15 @@ func (kl *Kubelet) GetNode() (*v1.Node, error) { ...@@ -235,6 +237,15 @@ func (kl *Kubelet) GetNode() (*v1.Node, error) {
if kl.kubeClient == nil { if kl.kubeClient == nil {
return kl.initialNode(context.TODO()) return kl.initialNode(context.TODO())
} }
// if we have a valid kube client, we wait for initial lister to sync
if !kl.nodeHasSynced() {
err := wait.PollImmediate(time.Second, maxWaitForAPIServerSync, func() (bool, error) {
return kl.nodeHasSynced(), nil
})
if err != nil {
return nil, fmt.Errorf("nodes have not yet been read at least once, cannot construct node object")
}
}
return kl.nodeLister.Get(string(kl.nodeName)) return kl.nodeLister.Get(string(kl.nodeName))
} }
...@@ -245,7 +256,7 @@ func (kl *Kubelet) GetNode() (*v1.Node, error) { ...@@ -245,7 +256,7 @@ func (kl *Kubelet) GetNode() (*v1.Node, error) {
// zero capacity, and the default labels. // zero capacity, and the default labels.
func (kl *Kubelet) getNodeAnyWay() (*v1.Node, error) { func (kl *Kubelet) getNodeAnyWay() (*v1.Node, error) {
if kl.kubeClient != nil { if kl.kubeClient != nil {
if n, err := kl.nodeLister.Get(string(kl.nodeName)); err == nil { if n, err := kl.GetNode(); err == nil {
return n, nil return n, nil
} }
} }
......
...@@ -319,19 +319,21 @@ func (ep *endpointsInfo) Cleanup() { ...@@ -319,19 +319,21 @@ func (ep *endpointsInfo) Cleanup() {
Log(ep, "Endpoint Cleanup", 3) Log(ep, "Endpoint Cleanup", 3)
if ep.refCount != nil { if ep.refCount != nil {
*ep.refCount-- *ep.refCount--
}
// Remove the remote hns endpoint, if no service is referring it // Remove the remote hns endpoint, if no service is referring it
// Never delete a Local Endpoint. Local Endpoints are already created by other entities. // Never delete a Local Endpoint. Local Endpoints are already created by other entities.
// Remove only remote endpoints created by this service // Remove only remote endpoints created by this service
if (ep.refCount == nil || *ep.refCount <= 0) && !ep.GetIsLocal() { if *ep.refCount <= 0 && !ep.GetIsLocal() {
klog.V(4).Infof("Removing endpoints for %v, since no one is referencing it", ep) klog.V(4).Infof("Removing endpoints for %v, since no one is referencing it", ep)
err := ep.hns.deleteEndpoint(ep.hnsID) err := ep.hns.deleteEndpoint(ep.hnsID)
if err == nil { if err == nil {
ep.hnsID = "" ep.hnsID = ""
} else { } else {
klog.Errorf("Endpoint deletion failed for %v: %v", ep.IP(), err) klog.Errorf("Endpoint deletion failed for %v: %v", ep.IP(), err)
}
} }
ep.refCount = nil
} }
} }
......
...@@ -11,6 +11,7 @@ go_library( ...@@ -11,6 +11,7 @@ go_library(
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = [ deps = [
"//pkg/api/v1/pod:go_default_library", "//pkg/api/v1/pod:go_default_library",
"//pkg/apis/core/validation:go_default_library",
"//pkg/features:go_default_library", "//pkg/features:go_default_library",
"//pkg/scheduler/algorithmprovider:go_default_library", "//pkg/scheduler/algorithmprovider:go_default_library",
"//pkg/scheduler/apis/config:go_default_library", "//pkg/scheduler/apis/config:go_default_library",
......
...@@ -35,6 +35,7 @@ import ( ...@@ -35,6 +35,7 @@ import (
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
"k8s.io/klog/v2" "k8s.io/klog/v2"
podutil "k8s.io/kubernetes/pkg/api/v1/pod" podutil "k8s.io/kubernetes/pkg/api/v1/pod"
"k8s.io/kubernetes/pkg/apis/core/validation"
schedulerapi "k8s.io/kubernetes/pkg/scheduler/apis/config" schedulerapi "k8s.io/kubernetes/pkg/scheduler/apis/config"
"k8s.io/kubernetes/pkg/scheduler/apis/config/scheme" "k8s.io/kubernetes/pkg/scheduler/apis/config/scheme"
"k8s.io/kubernetes/pkg/scheduler/core" "k8s.io/kubernetes/pkg/scheduler/core"
...@@ -332,7 +333,8 @@ func (sched *Scheduler) recordSchedulingFailure(prof *profile.Profile, podInfo * ...@@ -332,7 +333,8 @@ func (sched *Scheduler) recordSchedulingFailure(prof *profile.Profile, podInfo *
} }
pod := podInfo.Pod pod := podInfo.Pod
prof.Recorder.Eventf(pod, nil, v1.EventTypeWarning, "FailedScheduling", "Scheduling", err.Error()) msg := truncateMessage(err.Error())
prof.Recorder.Eventf(pod, nil, v1.EventTypeWarning, "FailedScheduling", "Scheduling", msg)
if err := updatePod(sched.client, pod, &v1.PodCondition{ if err := updatePod(sched.client, pod, &v1.PodCondition{
Type: v1.PodScheduled, Type: v1.PodScheduled,
Status: v1.ConditionFalse, Status: v1.ConditionFalse,
...@@ -343,6 +345,16 @@ func (sched *Scheduler) recordSchedulingFailure(prof *profile.Profile, podInfo * ...@@ -343,6 +345,16 @@ func (sched *Scheduler) recordSchedulingFailure(prof *profile.Profile, podInfo *
} }
} }
// truncateMessage truncates a message if it hits the NoteLengthLimit.
func truncateMessage(message string) string {
max := validation.NoteLengthLimit
if len(message) <= max {
return message
}
suffix := " ..."
return message[:max-len(suffix)] + suffix
}
func updatePod(client clientset.Interface, pod *v1.Pod, condition *v1.PodCondition, nominatedNode string) error { func updatePod(client clientset.Interface, pod *v1.Pod, condition *v1.PodCondition, nominatedNode string) error {
klog.V(3).Infof("Updating pod condition for %s/%s to (%s==%s, Reason=%s)", pod.Namespace, pod.Name, condition.Type, condition.Status, condition.Reason) klog.V(3).Infof("Updating pod condition for %s/%s to (%s==%s, Reason=%s)", pod.Namespace, pod.Name, condition.Type, condition.Status, condition.Reason)
podCopy := pod.DeepCopy() podCopy := pod.DeepCopy()
......
...@@ -28,6 +28,7 @@ go_library( ...@@ -28,6 +28,7 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library",
"//staging/src/k8s.io/client-go/informers:go_default_library", "//staging/src/k8s.io/client-go/informers:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes:go_default_library", "//staging/src/k8s.io/client-go/kubernetes:go_default_library",
......
...@@ -22,6 +22,7 @@ import ( ...@@ -22,6 +22,7 @@ import (
"strings" "strings"
"sync" "sync"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/utils/exec" "k8s.io/utils/exec"
"k8s.io/utils/mount" "k8s.io/utils/mount"
...@@ -454,11 +455,12 @@ type VolumeHost interface { ...@@ -454,11 +455,12 @@ type VolumeHost interface {
// VolumePluginMgr tracks registered plugins. // VolumePluginMgr tracks registered plugins.
type VolumePluginMgr struct { type VolumePluginMgr struct {
mutex sync.Mutex mutex sync.Mutex
plugins map[string]VolumePlugin plugins map[string]VolumePlugin
prober DynamicPluginProber prober DynamicPluginProber
probedPlugins map[string]VolumePlugin probedPlugins map[string]VolumePlugin
Host VolumeHost loggedDeprecationWarnings sets.String
Host VolumeHost
} }
// Spec is an internal representation of a volume. All API volume types translate to Spec. // Spec is an internal representation of a volume. All API volume types translate to Spec.
...@@ -589,6 +591,7 @@ func (pm *VolumePluginMgr) InitPlugins(plugins []VolumePlugin, prober DynamicPlu ...@@ -589,6 +591,7 @@ func (pm *VolumePluginMgr) InitPlugins(plugins []VolumePlugin, prober DynamicPlu
defer pm.mutex.Unlock() defer pm.mutex.Unlock()
pm.Host = host pm.Host = host
pm.loggedDeprecationWarnings = sets.NewString()
if prober == nil { if prober == nil {
// Use a dummy prober to prevent nil deference. // Use a dummy prober to prevent nil deference.
...@@ -685,9 +688,7 @@ func (pm *VolumePluginMgr) FindPluginBySpec(spec *Spec) (VolumePlugin, error) { ...@@ -685,9 +688,7 @@ func (pm *VolumePluginMgr) FindPluginBySpec(spec *Spec) (VolumePlugin, error) {
} }
// Issue warning if the matched provider is deprecated // Issue warning if the matched provider is deprecated
if detail, ok := deprecatedVolumeProviders[matches[0].GetPluginName()]; ok { pm.logDeprecation(matches[0].GetPluginName())
klog.Warningf("WARNING: %s built-in volume provider is now deprecated. %s", matches[0].GetPluginName(), detail)
}
return matches[0], nil return matches[0], nil
} }
...@@ -720,12 +721,20 @@ func (pm *VolumePluginMgr) FindPluginByName(name string) (VolumePlugin, error) { ...@@ -720,12 +721,20 @@ func (pm *VolumePluginMgr) FindPluginByName(name string) (VolumePlugin, error) {
} }
// Issue warning if the matched provider is deprecated // Issue warning if the matched provider is deprecated
if detail, ok := deprecatedVolumeProviders[matches[0].GetPluginName()]; ok { pm.logDeprecation(matches[0].GetPluginName())
klog.Warningf("WARNING: %s built-in volume provider is now deprecated. %s", matches[0].GetPluginName(), detail)
}
return matches[0], nil return matches[0], nil
} }
// logDeprecation logs warning when a deprecated plugin is used.
func (pm *VolumePluginMgr) logDeprecation(plugin string) {
if detail, ok := deprecatedVolumeProviders[plugin]; ok && !pm.loggedDeprecationWarnings.Has(plugin) {
klog.Warningf("WARNING: %s built-in volume provider is now deprecated. %s", plugin, detail)
// Make sure the message is logged only once. It has Warning severity
// and we don't want to spam the log too much.
pm.loggedDeprecationWarnings.Insert(plugin)
}
}
// Check if probedPlugin cache update is required. // Check if probedPlugin cache update is required.
// If it is, initialize all probed plugins and replace the cache with them. // If it is, initialize all probed plugins and replace the cache with them.
func (pm *VolumePluginMgr) refreshProbedPlugins() { func (pm *VolumePluginMgr) refreshProbedPlugins() {
......
...@@ -181,11 +181,40 @@ func (az *Cloud) CreateOrUpdateSecurityGroup(service *v1.Service, sg network.Sec ...@@ -181,11 +181,40 @@ func (az *Cloud) CreateOrUpdateSecurityGroup(service *v1.Service, sg network.Sec
return rerr.Error() return rerr.Error()
} }
func cleanupSubnetInFrontendIPConfigurations(lb *network.LoadBalancer) network.LoadBalancer {
if lb.LoadBalancerPropertiesFormat == nil || lb.FrontendIPConfigurations == nil {
return *lb
}
frontendIPConfigurations := *lb.FrontendIPConfigurations
for i := range frontendIPConfigurations {
config := frontendIPConfigurations[i]
if config.FrontendIPConfigurationPropertiesFormat != nil &&
config.Subnet != nil &&
config.Subnet.ID != nil {
subnet := network.Subnet{
ID: config.Subnet.ID,
}
if config.Subnet.Name != nil {
subnet.Name = config.FrontendIPConfigurationPropertiesFormat.Subnet.Name
}
config.FrontendIPConfigurationPropertiesFormat.Subnet = &subnet
frontendIPConfigurations[i] = config
continue
}
}
lb.FrontendIPConfigurations = &frontendIPConfigurations
return *lb
}
// CreateOrUpdateLB invokes az.LoadBalancerClient.CreateOrUpdate with exponential backoff retry // CreateOrUpdateLB invokes az.LoadBalancerClient.CreateOrUpdate with exponential backoff retry
func (az *Cloud) CreateOrUpdateLB(service *v1.Service, lb network.LoadBalancer) error { func (az *Cloud) CreateOrUpdateLB(service *v1.Service, lb network.LoadBalancer) error {
ctx, cancel := getContextWithCancel() ctx, cancel := getContextWithCancel()
defer cancel() defer cancel()
lb = cleanupSubnetInFrontendIPConfigurations(&lb)
rgName := az.getLoadBalancerResourceGroup() rgName := az.getLoadBalancerResourceGroup()
rerr := az.LoadBalancerClient.CreateOrUpdate(ctx, rgName, to.String(lb.Name), lb, to.String(lb.Etag)) rerr := az.LoadBalancerClient.CreateOrUpdate(ctx, rgName, to.String(lb.Name), lb, to.String(lb.Etag))
klog.V(10).Infof("LoadBalancerClient.CreateOrUpdate(%s): end", *lb.Name) klog.V(10).Infof("LoadBalancerClient.CreateOrUpdate(%s): end", *lb.Name)
......
...@@ -1411,10 +1411,15 @@ func (ss *scaleSet) ensureBackendPoolDeletedFromVMSS(service *v1.Service, backen ...@@ -1411,10 +1411,15 @@ func (ss *scaleSet) ensureBackendPoolDeletedFromVMSS(service *v1.Service, backen
vmssNamesMap[vmSetName] = true vmssNamesMap[vmSetName] = true
} }
vmssUpdaters := make([]func() error, 0, len(vmssNamesMap))
errors := make([]error, 0, len(vmssNamesMap))
for vmssName := range vmssNamesMap { for vmssName := range vmssNamesMap {
vmssName := vmssName
vmss, err := ss.getVMSS(vmssName, azcache.CacheReadTypeDefault) vmss, err := ss.getVMSS(vmssName, azcache.CacheReadTypeDefault)
if err != nil { if err != nil {
return err klog.Errorf("ensureBackendPoolDeletedFromVMSS: failed to get VMSS %s: %v", vmssName, err)
errors = append(errors, err)
continue
} }
// When vmss is being deleted, CreateOrUpdate API would report "the vmss is being deleted" error. // When vmss is being deleted, CreateOrUpdate API would report "the vmss is being deleted" error.
...@@ -1430,11 +1435,15 @@ func (ss *scaleSet) ensureBackendPoolDeletedFromVMSS(service *v1.Service, backen ...@@ -1430,11 +1435,15 @@ func (ss *scaleSet) ensureBackendPoolDeletedFromVMSS(service *v1.Service, backen
vmssNIC := *vmss.VirtualMachineProfile.NetworkProfile.NetworkInterfaceConfigurations vmssNIC := *vmss.VirtualMachineProfile.NetworkProfile.NetworkInterfaceConfigurations
primaryNIC, err := ss.getPrimaryNetworkInterfaceConfigurationForScaleSet(vmssNIC, vmssName) primaryNIC, err := ss.getPrimaryNetworkInterfaceConfigurationForScaleSet(vmssNIC, vmssName)
if err != nil { if err != nil {
return err klog.Errorf("ensureBackendPoolDeletedFromVMSS: failed to get the primary network interface config of the VMSS %s: %v", vmssName, err)
errors = append(errors, err)
continue
} }
primaryIPConfig, err := getPrimaryIPConfigFromVMSSNetworkConfig(primaryNIC) primaryIPConfig, err := getPrimaryIPConfigFromVMSSNetworkConfig(primaryNIC)
if err != nil { if err != nil {
return err klog.Errorf("ensureBackendPoolDeletedFromVMSS: failed to the primary IP config from the VMSS %s's network config : %v", vmssName, err)
errors = append(errors, err)
continue
} }
loadBalancerBackendAddressPools := []compute.SubResource{} loadBalancerBackendAddressPools := []compute.SubResource{}
if primaryIPConfig.LoadBalancerBackendAddressPools != nil { if primaryIPConfig.LoadBalancerBackendAddressPools != nil {
...@@ -1455,26 +1464,39 @@ func (ss *scaleSet) ensureBackendPoolDeletedFromVMSS(service *v1.Service, backen ...@@ -1455,26 +1464,39 @@ func (ss *scaleSet) ensureBackendPoolDeletedFromVMSS(service *v1.Service, backen
continue continue
} }
// Compose a new vmss with added backendPoolID. vmssUpdaters = append(vmssUpdaters, func() error {
primaryIPConfig.LoadBalancerBackendAddressPools = &newBackendPools // Compose a new vmss with added backendPoolID.
newVMSS := compute.VirtualMachineScaleSet{ primaryIPConfig.LoadBalancerBackendAddressPools = &newBackendPools
Sku: vmss.Sku, newVMSS := compute.VirtualMachineScaleSet{
Location: vmss.Location, Sku: vmss.Sku,
VirtualMachineScaleSetProperties: &compute.VirtualMachineScaleSetProperties{ Location: vmss.Location,
VirtualMachineProfile: &compute.VirtualMachineScaleSetVMProfile{ VirtualMachineScaleSetProperties: &compute.VirtualMachineScaleSetProperties{
NetworkProfile: &compute.VirtualMachineScaleSetNetworkProfile{ VirtualMachineProfile: &compute.VirtualMachineScaleSetVMProfile{
NetworkInterfaceConfigurations: &vmssNIC, NetworkProfile: &compute.VirtualMachineScaleSetNetworkProfile{
NetworkInterfaceConfigurations: &vmssNIC,
},
}, },
}, },
}, }
}
klog.V(2).Infof("ensureBackendPoolDeletedFromVMSS begins to update vmss(%s) with backendPoolID %s", vmssName, backendPoolID) klog.V(2).Infof("ensureBackendPoolDeletedFromVMSS begins to update vmss(%s) with backendPoolID %s", vmssName, backendPoolID)
rerr := ss.CreateOrUpdateVMSS(ss.ResourceGroup, vmssName, newVMSS) rerr := ss.CreateOrUpdateVMSS(ss.ResourceGroup, vmssName, newVMSS)
if rerr != nil { if rerr != nil {
klog.Errorf("ensureBackendPoolDeletedFromVMSS CreateOrUpdateVMSS(%s) with new backendPoolID %s, err: %v", vmssName, backendPoolID, err) klog.Errorf("ensureBackendPoolDeletedFromVMSS CreateOrUpdateVMSS(%s) with new backendPoolID %s, err: %v", vmssName, backendPoolID, rerr)
return rerr.Error() return rerr.Error()
} }
return nil
})
}
errs := utilerrors.AggregateGoroutines(vmssUpdaters...)
if errs != nil {
return utilerrors.Flatten(errs)
}
// Fail if there are other errors.
if len(errors) > 0 {
return utilerrors.Flatten(utilerrors.NewAggregate(errors))
} }
return nil return nil
......
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