Unverified Commit 65110a4e authored by Chris Kim's avatar Chris Kim Committed by GitHub

[master] Bump Kubernetes to v1.22.4-k3s1 (#4536)

* Update to v1.22.4 Signed-off-by: 's avatarChris Kim <oats87g@gmail.com>
parent 03485632
...@@ -38,37 +38,37 @@ replace ( ...@@ -38,37 +38,37 @@ 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.38.0 google.golang.org/grpc => google.golang.org/grpc v1.38.0
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.22.3-k3s1 k8s.io/api => github.com/k3s-io/kubernetes/staging/src/k8s.io/api v1.22.4-k3s1
k8s.io/apiextensions-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiextensions-apiserver v1.22.3-k3s1 k8s.io/apiextensions-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiextensions-apiserver v1.22.4-k3s1
k8s.io/apimachinery => github.com/k3s-io/kubernetes/staging/src/k8s.io/apimachinery v1.22.3-k3s1 k8s.io/apimachinery => github.com/k3s-io/kubernetes/staging/src/k8s.io/apimachinery v1.22.4-k3s1
k8s.io/apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiserver v1.22.3-k3s1 k8s.io/apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiserver v1.22.4-k3s1
k8s.io/cli-runtime => github.com/k3s-io/kubernetes/staging/src/k8s.io/cli-runtime v1.22.3-k3s1 k8s.io/cli-runtime => github.com/k3s-io/kubernetes/staging/src/k8s.io/cli-runtime v1.22.4-k3s1
k8s.io/client-go => github.com/k3s-io/kubernetes/staging/src/k8s.io/client-go v1.22.3-k3s1 k8s.io/client-go => github.com/k3s-io/kubernetes/staging/src/k8s.io/client-go v1.22.4-k3s1
k8s.io/cloud-provider => github.com/k3s-io/kubernetes/staging/src/k8s.io/cloud-provider v1.22.3-k3s1 k8s.io/cloud-provider => github.com/k3s-io/kubernetes/staging/src/k8s.io/cloud-provider v1.22.4-k3s1
k8s.io/cluster-bootstrap => github.com/k3s-io/kubernetes/staging/src/k8s.io/cluster-bootstrap v1.22.3-k3s1 k8s.io/cluster-bootstrap => github.com/k3s-io/kubernetes/staging/src/k8s.io/cluster-bootstrap v1.22.4-k3s1
k8s.io/code-generator => github.com/k3s-io/kubernetes/staging/src/k8s.io/code-generator v1.22.3-k3s1 k8s.io/code-generator => github.com/k3s-io/kubernetes/staging/src/k8s.io/code-generator v1.22.4-k3s1
k8s.io/component-base => github.com/k3s-io/kubernetes/staging/src/k8s.io/component-base v1.22.3-k3s1 k8s.io/component-base => github.com/k3s-io/kubernetes/staging/src/k8s.io/component-base v1.22.4-k3s1
k8s.io/component-helpers => github.com/k3s-io/kubernetes/staging/src/k8s.io/component-helpers v1.22.3-k3s1 k8s.io/component-helpers => github.com/k3s-io/kubernetes/staging/src/k8s.io/component-helpers v1.22.4-k3s1
k8s.io/controller-manager => github.com/k3s-io/kubernetes/staging/src/k8s.io/controller-manager v1.22.3-k3s1 k8s.io/controller-manager => github.com/k3s-io/kubernetes/staging/src/k8s.io/controller-manager v1.22.4-k3s1
k8s.io/cri-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/cri-api v1.22.3-k3s1 k8s.io/cri-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/cri-api v1.22.4-k3s1
k8s.io/csi-translation-lib => github.com/k3s-io/kubernetes/staging/src/k8s.io/csi-translation-lib v1.22.3-k3s1 k8s.io/csi-translation-lib => github.com/k3s-io/kubernetes/staging/src/k8s.io/csi-translation-lib v1.22.4-k3s1
k8s.io/klog => github.com/k3s-io/klog v1.0.0-k3s2 // k3s-release-1.x k8s.io/klog => github.com/k3s-io/klog v1.0.0-k3s2 // k3s-release-1.x
k8s.io/klog/v2 => github.com/k3s-io/klog/v2 v2.9.0-k3s2 // k3s-main k8s.io/klog/v2 => github.com/k3s-io/klog/v2 v2.9.0-k3s2 // k3s-main
k8s.io/kube-aggregator => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-aggregator v1.22.3-k3s1 k8s.io/kube-aggregator => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-aggregator v1.22.4-k3s1
k8s.io/kube-controller-manager => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-controller-manager v1.22.3-k3s1 k8s.io/kube-controller-manager => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-controller-manager v1.22.4-k3s1
k8s.io/kube-proxy => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-proxy v1.22.3-k3s1 k8s.io/kube-proxy => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-proxy v1.22.4-k3s1
k8s.io/kube-scheduler => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-scheduler v1.22.3-k3s1 k8s.io/kube-scheduler => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-scheduler v1.22.4-k3s1
k8s.io/kubectl => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubectl v1.22.3-k3s1 k8s.io/kubectl => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubectl v1.22.4-k3s1
k8s.io/kubelet => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubelet v1.22.3-k3s1 k8s.io/kubelet => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubelet v1.22.4-k3s1
k8s.io/kubernetes => github.com/k3s-io/kubernetes v1.22.3-k3s1 k8s.io/kubernetes => github.com/k3s-io/kubernetes v1.22.4-k3s1
k8s.io/legacy-cloud-providers => github.com/k3s-io/kubernetes/staging/src/k8s.io/legacy-cloud-providers v1.22.3-k3s1 k8s.io/legacy-cloud-providers => github.com/k3s-io/kubernetes/staging/src/k8s.io/legacy-cloud-providers v1.22.4-k3s1
k8s.io/metrics => github.com/k3s-io/kubernetes/staging/src/k8s.io/metrics v1.22.3-k3s1 k8s.io/metrics => github.com/k3s-io/kubernetes/staging/src/k8s.io/metrics v1.22.4-k3s1
k8s.io/mount-utils => github.com/k3s-io/kubernetes/staging/src/k8s.io/mount-utils v1.22.3-k3s1 k8s.io/mount-utils => github.com/k3s-io/kubernetes/staging/src/k8s.io/mount-utils v1.22.4-k3s1
k8s.io/node-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/node-api v1.22.3-k3s1 k8s.io/node-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/node-api v1.22.4-k3s1
k8s.io/pod-security-admission => github.com/k3s-io/kubernetes/staging/src/k8s.io/pod-security-admission v1.22.3-k3s1 k8s.io/pod-security-admission => github.com/k3s-io/kubernetes/staging/src/k8s.io/pod-security-admission v1.22.4-k3s1
k8s.io/sample-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-apiserver v1.22.3-k3s1 k8s.io/sample-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-apiserver v1.22.4-k3s1
k8s.io/sample-cli-plugin => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-cli-plugin v1.22.3-k3s1 k8s.io/sample-cli-plugin => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-cli-plugin v1.22.4-k3s1
k8s.io/sample-controller => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-controller v1.22.3-k3s1 k8s.io/sample-controller => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-controller v1.22.4-k3s1
mvdan.cc/unparam => mvdan.cc/unparam v0.0.0-20210104141923-aac4ce9116a7 mvdan.cc/unparam => mvdan.cc/unparam v0.0.0-20210104141923-aac4ce9116a7
) )
...@@ -127,18 +127,18 @@ require ( ...@@ -127,18 +127,18 @@ require (
google.golang.org/grpc v1.40.0 google.golang.org/grpc v1.40.0
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
inet.af/tcpproxy v0.0.0-20200125044825-b6bb9b5b8252 inet.af/tcpproxy v0.0.0-20200125044825-b6bb9b5b8252
k8s.io/api v0.22.3 k8s.io/api v0.22.4
k8s.io/apimachinery v0.22.3 k8s.io/apimachinery v0.22.4
k8s.io/apiserver v0.22.3 k8s.io/apiserver v0.22.4
k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible
k8s.io/cloud-provider v0.22.3 k8s.io/cloud-provider v0.22.4
k8s.io/component-base v0.22.3 k8s.io/component-base v0.22.4
k8s.io/controller-manager v0.22.3 // indirect k8s.io/controller-manager v0.22.4 // indirect
k8s.io/cri-api v0.22.3 k8s.io/cri-api v0.22.4
k8s.io/klog v1.0.0 k8s.io/klog v1.0.0
k8s.io/klog/v2 v2.9.0 k8s.io/klog/v2 v2.9.0
k8s.io/kubectl v0.22.3 k8s.io/kubectl v0.22.4
k8s.io/kubernetes v1.22.3 k8s.io/kubernetes v1.22.4
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a
sigs.k8s.io/yaml v1.2.0 sigs.k8s.io/yaml v1.2.0
) )
...@@ -24,6 +24,7 @@ import ( ...@@ -24,6 +24,7 @@ import (
"net/http" "net/http"
"runtime" "runtime"
"strings" "strings"
"sync"
"time" "time"
"k8s.io/apiserver/pkg/endpoints/metrics" "k8s.io/apiserver/pkg/endpoints/metrics"
...@@ -54,12 +55,16 @@ type respLogger struct { ...@@ -54,12 +55,16 @@ type respLogger struct {
statusRecorded bool statusRecorded bool
status int status int
statusStack string statusStack string
// mutex is used when accessing addedInfo
// It can be modified by other goroutine when logging happens (in case of request timeout)
mutex sync.Mutex
addedInfo strings.Builder addedInfo strings.Builder
startTime time.Time startTime time.Time
captureErrorOutput bool captureErrorOutput bool
req *http.Request req *http.Request
userAgent string
w http.ResponseWriter w http.ResponseWriter
logStacktracePred StacktracePred logStacktracePred StacktracePred
...@@ -121,6 +126,7 @@ func newLoggedWithStartTime(req *http.Request, w http.ResponseWriter, startTime ...@@ -121,6 +126,7 @@ func newLoggedWithStartTime(req *http.Request, w http.ResponseWriter, startTime
return &respLogger{ return &respLogger{
startTime: startTime, startTime: startTime,
req: req, req: req,
userAgent: req.UserAgent(),
w: w, w: w,
logStacktracePred: DefaultStacktracePred, logStacktracePred: DefaultStacktracePred,
} }
...@@ -171,6 +177,8 @@ func StatusIsNot(statuses ...int) StacktracePred { ...@@ -171,6 +177,8 @@ func StatusIsNot(statuses ...int) StacktracePred {
// Addf adds additional data to be logged with this request. // Addf adds additional data to be logged with this request.
func (rl *respLogger) Addf(format string, data ...interface{}) { func (rl *respLogger) Addf(format string, data ...interface{}) {
rl.mutex.Lock()
defer rl.mutex.Unlock()
rl.addedInfo.WriteString("\n") rl.addedInfo.WriteString("\n")
rl.addedInfo.WriteString(fmt.Sprintf(format, data...)) rl.addedInfo.WriteString(fmt.Sprintf(format, data...))
} }
...@@ -200,10 +208,18 @@ func (rl *respLogger) Log() { ...@@ -200,10 +208,18 @@ func (rl *respLogger) Log() {
"verb", verb, "verb", verb,
"URI", rl.req.RequestURI, "URI", rl.req.RequestURI,
"latency", latency, "latency", latency,
"userAgent", rl.req.UserAgent(), // We can't get UserAgent from rl.req.UserAgent() here as it accesses headers map,
// which can be modified in another goroutine when apiserver request times out.
// For example authentication filter modifies request's headers,
// This can cause apiserver to crash with unrecoverable fatal error.
// More info about concurrent read and write for maps: https://golang.org/doc/go1.6#runtime
"userAgent", rl.userAgent,
"audit-ID", auditID, "audit-ID", auditID,
"srcIP", rl.req.RemoteAddr, "srcIP", rl.req.RemoteAddr,
} }
// Lock for accessing addedInfo
rl.mutex.Lock()
defer rl.mutex.Unlock()
if rl.hijacked { if rl.hijacked {
keysAndValues = append(keysAndValues, "hijacked", true) keysAndValues = append(keysAndValues, "hijacked", true)
......
...@@ -52,6 +52,17 @@ func WatchInitialized(ctx context.Context) { ...@@ -52,6 +52,17 @@ func WatchInitialized(ctx context.Context) {
} }
} }
// RequestDelegated informs the priority and fairness dispatcher that
// a given request has been delegated to an aggregated API
// server. No-op when priority and fairness is disabled.
func RequestDelegated(ctx context.Context) {
// The watch initialization signal doesn't traverse request
// boundaries, so we generously fire it as soon as we know
// that the request won't be serviced locally. Safe to call
// for non-watch requests.
WatchInitialized(ctx)
}
// InitializationSignal is an interface that allows sending and handling // InitializationSignal is an interface that allows sending and handling
// initialization signals. // initialization signals.
type InitializationSignal interface { type InitializationSignal interface {
......
...@@ -3,8 +3,8 @@ package version ...@@ -3,8 +3,8 @@ package version
var ( var (
gitMajor = "1" gitMajor = "1"
gitMinor = "22" gitMinor = "22"
gitVersion = "v1.22.3-k3s1" gitVersion = "v1.22.4-k3s1"
gitCommit = "5d8c744cb54fc54d261b2f14e8464049aad7e32c" gitCommit = "00226be68bd6b70f8fca252a1027fc0a35d070b8"
gitTreeState = "clean" gitTreeState = "clean"
buildDate = "2021-10-28T15:23:07Z" buildDate = "2021-11-17T22:30:06Z"
) )
...@@ -55,6 +55,9 @@ const ( ...@@ -55,6 +55,9 @@ const (
// should be changed appropriately. // should be changed appropriately.
minRetryDelay = 5 * time.Second minRetryDelay = 5 * time.Second
maxRetryDelay = 300 * time.Second maxRetryDelay = 300 * time.Second
// ToBeDeletedTaint is a taint used by the CLuster Autoscaler before marking a node for deletion. Defined in
// https://github.com/kubernetes/autoscaler/blob/e80ab518340f88f364fe3ef063f8303755125971/cluster-autoscaler/utils/deletetaint/delete.go#L36
ToBeDeletedTaint = "ToBeDeletedByClusterAutoscaler"
) )
type cachedService struct { type cachedService struct {
...@@ -671,6 +674,14 @@ func (s *Controller) getNodeConditionPredicate() NodeConditionPredicate { ...@@ -671,6 +674,14 @@ func (s *Controller) getNodeConditionPredicate() NodeConditionPredicate {
return false return false
} }
// Remove nodes that are about to be deleted by the cluster autoscaler.
for _, taint := range node.Spec.Taints {
if taint.Key == ToBeDeletedTaint {
klog.V(4).Infof("Ignoring node %v with autoscaler taint %+v", node.Name, taint)
return false
}
}
// If we have no info, don't accept // If we have no info, don't accept
if len(node.Status.Conditions) == 0 { if len(node.Status.Conditions) == 0 {
return false return false
......
...@@ -780,8 +780,8 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8 ...@@ -780,8 +780,8 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM= k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM=
k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM= k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c h1:jvamsI1tn9V0S8jicyX82qaFC0H/NKxv2e5mbqsgR80=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a h1:8dYfu/Fc9Gz2rNJKB9IQRGgQOh2clmRzNIPPY1xLY5g= k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a h1:8dYfu/Fc9Gz2rNJKB9IQRGgQOh2clmRzNIPPY1xLY5g=
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
......
...@@ -58,8 +58,8 @@ func (o *Options) Validate() []error { ...@@ -58,8 +58,8 @@ func (o *Options) Validate() []error {
// AddFlags adds flags for exposing component metrics. // AddFlags adds flags for exposing component metrics.
func (o *Options) AddFlags(fs *pflag.FlagSet) { func (o *Options) AddFlags(fs *pflag.FlagSet) {
if o != nil { if o == nil {
o = NewOptions() return
} }
fs.StringVar(&o.ShowHiddenMetricsForVersion, "show-hidden-metrics-for-version", o.ShowHiddenMetricsForVersion, fs.StringVar(&o.ShowHiddenMetricsForVersion, "show-hidden-metrics-for-version", o.ShowHiddenMetricsForVersion,
"The previous version for which you want to show hidden metrics. "+ "The previous version for which you want to show hidden metrics. "+
......
...@@ -3,8 +3,8 @@ package version ...@@ -3,8 +3,8 @@ package version
var ( var (
gitMajor = "1" gitMajor = "1"
gitMinor = "22" gitMinor = "22"
gitVersion = "v1.22.3-k3s1" gitVersion = "v1.22.4-k3s1"
gitCommit = "5d8c744cb54fc54d261b2f14e8464049aad7e32c" gitCommit = "00226be68bd6b70f8fca252a1027fc0a35d070b8"
gitTreeState = "clean" gitTreeState = "clean"
buildDate = "2021-10-28T15:23:07Z" buildDate = "2021-11-17T22:30:06Z"
) )
...@@ -210,7 +210,7 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8 ...@@ -210,7 +210,7 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM= k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM=
k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno= sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno=
sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
......
...@@ -35,6 +35,7 @@ import ( ...@@ -35,6 +35,7 @@ import (
genericfeatures "k8s.io/apiserver/pkg/features" genericfeatures "k8s.io/apiserver/pkg/features"
"k8s.io/apiserver/pkg/server/egressselector" "k8s.io/apiserver/pkg/server/egressselector"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
utilflowcontrol "k8s.io/apiserver/pkg/util/flowcontrol"
"k8s.io/apiserver/pkg/util/x509metrics" "k8s.io/apiserver/pkg/util/x509metrics"
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
"k8s.io/client-go/transport" "k8s.io/client-go/transport"
...@@ -175,6 +176,7 @@ func (r *proxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { ...@@ -175,6 +176,7 @@ func (r *proxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
handler := proxy.NewUpgradeAwareHandler(location, proxyRoundTripper, true, upgrade, &responder{w: w}) handler := proxy.NewUpgradeAwareHandler(location, proxyRoundTripper, true, upgrade, &responder{w: w})
handler.InterceptRedirects = utilfeature.DefaultFeatureGate.Enabled(genericfeatures.StreamingProxyRedirects) handler.InterceptRedirects = utilfeature.DefaultFeatureGate.Enabled(genericfeatures.StreamingProxyRedirects)
handler.RequireSameHostRedirects = utilfeature.DefaultFeatureGate.Enabled(genericfeatures.ValidateProxyRedirects) handler.RequireSameHostRedirects = utilfeature.DefaultFeatureGate.Enabled(genericfeatures.ValidateProxyRedirects)
utilflowcontrol.RequestDelegated(req.Context())
handler.ServeHTTP(w, newReq) handler.ServeHTTP(w, newReq)
} }
......
...@@ -20,6 +20,7 @@ import ( ...@@ -20,6 +20,7 @@ import (
"bytes" "bytes"
"compress/gzip" "compress/gzip"
"crypto/sha512" "crypto/sha512"
"encoding/json"
"fmt" "fmt"
"mime" "mime"
"net/http" "net/http"
...@@ -30,9 +31,9 @@ import ( ...@@ -30,9 +31,9 @@ import (
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
openapi_v2 "github.com/googleapis/gnostic/openapiv2" openapi_v2 "github.com/googleapis/gnostic/openapiv2"
jsoniter "github.com/json-iterator/go"
"github.com/munnerz/goautoneg" "github.com/munnerz/goautoneg"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
klog "k8s.io/klog/v2"
"k8s.io/kube-openapi/pkg/builder" "k8s.io/kube-openapi/pkg/builder"
"k8s.io/kube-openapi/pkg/common" "k8s.io/kube-openapi/pkg/common"
"k8s.io/kube-openapi/pkg/validation/spec" "k8s.io/kube-openapi/pkg/validation/spec"
...@@ -55,13 +56,40 @@ type OpenAPIService struct { ...@@ -55,13 +56,40 @@ type OpenAPIService struct {
lastModified time.Time lastModified time.Time
specBytes []byte jsonCache cache
specPb []byte protoCache cache
specPbGz []byte }
type cache struct {
BuildCache func() ([]byte, error)
once sync.Once
bytes []byte
etag string
err error
}
func (c *cache) Get() ([]byte, string, error) {
c.once.Do(func() {
bytes, err := c.BuildCache()
// if there is an error updating the cache, there can be situations where
// c.bytes contains a valid value (carried over from the previous update)
// but c.err is also not nil; the cache user is expected to check for this
c.err = err
if c.err == nil {
// don't override previous spec if we had an error
c.bytes = bytes
c.etag = computeETag(c.bytes)
}
})
return c.bytes, c.etag, c.err
}
specBytesETag string func (c *cache) New(cacheBuilder func() ([]byte, error)) cache {
specPbETag string return cache{
specPbGzETag string bytes: c.bytes,
etag: c.etag,
BuildCache: cacheBuilder,
}
} }
func init() { func init() {
...@@ -71,6 +99,9 @@ func init() { ...@@ -71,6 +99,9 @@ func init() {
} }
func computeETag(data []byte) string { func computeETag(data []byte) string {
if data == nil {
return ""
}
return fmt.Sprintf("\"%X\"", sha512.Sum512(data)) return fmt.Sprintf("\"%X\"", sha512.Sum512(data))
} }
...@@ -83,51 +114,40 @@ func NewOpenAPIService(spec *spec.Swagger) (*OpenAPIService, error) { ...@@ -83,51 +114,40 @@ func NewOpenAPIService(spec *spec.Swagger) (*OpenAPIService, error) {
return o, nil return o, nil
} }
func (o *OpenAPIService) getSwaggerBytes() ([]byte, string, time.Time) { func (o *OpenAPIService) getSwaggerBytes() ([]byte, string, time.Time, error) {
o.rwMutex.RLock() o.rwMutex.RLock()
defer o.rwMutex.RUnlock() defer o.rwMutex.RUnlock()
return o.specBytes, o.specBytesETag, o.lastModified specBytes, etag, err := o.jsonCache.Get()
} if err != nil {
return nil, "", time.Time{}, err
func (o *OpenAPIService) getSwaggerPbBytes() ([]byte, string, time.Time) { }
o.rwMutex.RLock() return specBytes, etag, o.lastModified, nil
defer o.rwMutex.RUnlock()
return o.specPb, o.specPbETag, o.lastModified
} }
func (o *OpenAPIService) getSwaggerPbGzBytes() ([]byte, string, time.Time) { func (o *OpenAPIService) getSwaggerPbBytes() ([]byte, string, time.Time, error) {
o.rwMutex.RLock() o.rwMutex.RLock()
defer o.rwMutex.RUnlock() defer o.rwMutex.RUnlock()
return o.specPbGz, o.specPbGzETag, o.lastModified specPb, etag, err := o.protoCache.Get()
}
func (o *OpenAPIService) UpdateSpec(openapiSpec *spec.Swagger) (err error) {
specBytes, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(openapiSpec)
if err != nil {
return err
}
specPb, err := ToProtoBinary(specBytes)
if err != nil { if err != nil {
return err return nil, "", time.Time{}, err
} }
specPbGz := toGzip(specPb) return specPb, etag, o.lastModified, nil
}
specBytesETag := computeETag(specBytes)
specPbETag := computeETag(specPb)
specPbGzETag := computeETag(specPbGz)
lastModified := time.Now()
func (o *OpenAPIService) UpdateSpec(openapiSpec *spec.Swagger) (err error) {
o.rwMutex.Lock() o.rwMutex.Lock()
defer o.rwMutex.Unlock() defer o.rwMutex.Unlock()
o.jsonCache = o.jsonCache.New(func() ([]byte, error) {
o.specBytes = specBytes return json.Marshal(openapiSpec)
o.specPb = specPb })
o.specPbGz = specPbGz o.protoCache = o.protoCache.New(func() ([]byte, error) {
o.specBytesETag = specBytesETag json, _, err := o.jsonCache.Get()
o.specPbETag = specPbETag if err != nil {
o.specPbGzETag = specPbGzETag return nil, err
o.lastModified = lastModified }
return ToProtoBinary(json)
})
o.lastModified = time.Now()
return nil return nil
} }
...@@ -206,7 +226,7 @@ func (o *OpenAPIService) RegisterOpenAPIVersionedService(servePath string, handl ...@@ -206,7 +226,7 @@ func (o *OpenAPIService) RegisterOpenAPIVersionedService(servePath string, handl
accepted := []struct { accepted := []struct {
Type string Type string
SubType string SubType string
GetDataAndETag func() ([]byte, string, time.Time) GetDataAndETag func() ([]byte, string, time.Time, error)
}{ }{
{"application", "json", o.getSwaggerBytes}, {"application", "json", o.getSwaggerBytes},
{"application", "com.github.proto-openapi.spec.v2@v1.0+protobuf", o.getSwaggerPbBytes}, {"application", "com.github.proto-openapi.spec.v2@v1.0+protobuf", o.getSwaggerPbBytes},
...@@ -230,7 +250,15 @@ func (o *OpenAPIService) RegisterOpenAPIVersionedService(servePath string, handl ...@@ -230,7 +250,15 @@ func (o *OpenAPIService) RegisterOpenAPIVersionedService(servePath string, handl
} }
// serve the first matching media type in the sorted clause list // serve the first matching media type in the sorted clause list
data, etag, lastModified := accepts.GetDataAndETag() data, etag, lastModified, err := accepts.GetDataAndETag()
if err != nil {
klog.Errorf("Error in OpenAPI handler: %s", err)
// only return a 503 if we have no older cache data to serve
if data == nil {
w.WriteHeader(http.StatusServiceUnavailable)
return
}
}
w.Header().Set("Etag", etag) w.Header().Set("Etag", etag)
// ServeContent will take care of caching using eTag. // ServeContent will take care of caching using eTag.
http.ServeContent(w, r, servePath, lastModified, bytes.NewReader(data)) http.ServeContent(w, r, servePath, lastModified, bytes.NewReader(data))
......
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cp
import (
"path"
"path/filepath"
"strings"
)
type fileSpec struct {
PodName string
PodNamespace string
File pathSpec
}
type pathSpec interface {
String() string
}
// localPath represents a client-native path, which will differ based
// on the client OS, its methods will use path/filepath package which
// is OS dependant
type localPath struct {
file string
}
func newLocalPath(fileName string) localPath {
file := stripTrailingSlash(fileName)
return localPath{file: file}
}
func (p localPath) String() string {
return p.file
}
func (p localPath) Dir() localPath {
return newLocalPath(filepath.Dir(p.file))
}
func (p localPath) Base() localPath {
return newLocalPath(filepath.Base(p.file))
}
func (p localPath) Clean() localPath {
return newLocalPath(filepath.Clean(p.file))
}
func (p localPath) Join(elem pathSpec) localPath {
return newLocalPath(filepath.Join(p.file, elem.String()))
}
func (p localPath) Glob() (matches []string, err error) {
return filepath.Glob(p.file)
}
func (p localPath) StripSlashes() localPath {
return newLocalPath(stripLeadingSlash(p.file))
}
func isRelative(base, target localPath) bool {
relative, err := filepath.Rel(base.String(), target.String())
if err != nil {
return false
}
return relative == "." || relative == stripPathShortcuts(relative)
}
// remotePath represents always UNIX path, its methods will use path
// package which is always using `/`
type remotePath struct {
file string
}
func newRemotePath(fileName string) remotePath {
// we assume remote file is a linux container but we need to convert
// windows path separators to unix style for consistent processing
file := strings.ReplaceAll(stripTrailingSlash(fileName), `\`, "/")
return remotePath{file: file}
}
func (p remotePath) String() string {
return p.file
}
func (p remotePath) Dir() remotePath {
return newRemotePath(path.Dir(p.file))
}
func (p remotePath) Base() remotePath {
return newRemotePath(path.Base(p.file))
}
func (p remotePath) Clean() remotePath {
return newRemotePath(path.Clean(p.file))
}
func (p remotePath) Join(elem pathSpec) remotePath {
return newRemotePath(path.Join(p.file, elem.String()))
}
func (p remotePath) StripShortcuts() remotePath {
p = p.Clean()
return newRemotePath(stripPathShortcuts(p.file))
}
func (p remotePath) StripSlashes() remotePath {
return newRemotePath(stripLeadingSlash(p.file))
}
// strips trailing slash (if any) both unix and windows style
func stripTrailingSlash(file string) string {
if len(file) == 0 {
return file
}
if file != "/" && strings.HasSuffix(string(file[len(file)-1]), "/") {
return file[:len(file)-1]
}
return file
}
func stripLeadingSlash(file string) string {
// tar strips the leading '/' and '\' if it's there, so we will too
return strings.TrimLeft(file, `/\`)
}
// stripPathShortcuts removes any leading or trailing "../" from a given path
func stripPathShortcuts(p string) string {
newPath := p
trimmed := strings.TrimPrefix(newPath, "../")
for trimmed != newPath {
newPath = trimmed
trimmed = strings.TrimPrefix(newPath, "../")
}
// trim leftover {".", ".."}
if newPath == "." || newPath == ".." {
newPath = ""
}
if len(newPath) > 0 && string(newPath[0]) == "/" {
return newPath[1:]
}
return newPath
}
...@@ -198,13 +198,13 @@ func (o *CreateSecretDockerRegistryOptions) Complete(f cmdutil.Factory, cmd *cob ...@@ -198,13 +198,13 @@ func (o *CreateSecretDockerRegistryOptions) Complete(f cmdutil.Factory, cmd *cob
o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil { if err != nil {
return nil return err
} }
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy) cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
printer, err := o.PrintFlags.ToPrinter() printer, err := o.PrintFlags.ToPrinter()
if err != nil { if err != nil {
return nil return err
} }
o.PrintObj = func(obj runtime.Object) error { o.PrintObj = func(obj runtime.Object) error {
......
...@@ -21,6 +21,7 @@ import ( ...@@ -21,6 +21,7 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
componentbaseconfig "k8s.io/component-base/config"
schedulerappconfig "k8s.io/kubernetes/cmd/kube-scheduler/app/config" schedulerappconfig "k8s.io/kubernetes/cmd/kube-scheduler/app/config"
kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config" kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config"
) )
...@@ -28,6 +29,10 @@ import ( ...@@ -28,6 +29,10 @@ import (
// DeprecatedOptions contains deprecated options and their flags. // DeprecatedOptions contains deprecated options and their flags.
// TODO remove these fields once the deprecated flags are removed. // TODO remove these fields once the deprecated flags are removed.
type DeprecatedOptions struct { type DeprecatedOptions struct {
componentbaseconfig.DebuggingConfiguration
componentbaseconfig.ClientConnectionConfiguration
// Note that only the deprecated options (lock-object-name and lock-object-namespace) are populated here.
componentbaseconfig.LeaderElectionConfiguration
// The fields below here are placeholders for flags that can't be directly // The fields below here are placeholders for flags that can't be directly
// mapped into componentconfig.KubeSchedulerConfiguration. // mapped into componentconfig.KubeSchedulerConfiguration.
PolicyConfigFile string PolicyConfigFile string
...@@ -37,7 +42,7 @@ type DeprecatedOptions struct { ...@@ -37,7 +42,7 @@ type DeprecatedOptions struct {
} }
// AddFlags adds flags for the deprecated options. // AddFlags adds flags for the deprecated options.
func (o *DeprecatedOptions) AddFlags(fs *pflag.FlagSet, cfg *kubeschedulerconfig.KubeSchedulerConfiguration) { func (o *DeprecatedOptions) AddFlags(fs *pflag.FlagSet) {
if o == nil { if o == nil {
return return
} }
...@@ -48,14 +53,14 @@ func (o *DeprecatedOptions) AddFlags(fs *pflag.FlagSet, cfg *kubeschedulerconfig ...@@ -48,14 +53,14 @@ 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 predicates/priorities defined in this file will take precedence over any profiles define in ComponentConfig.") 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 predicates/priorities defined in this file will take precedence over any profiles define in ComponentConfig.")
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/. This parameter is ignored if a config file is specified in --config.") fs.BoolVar(&o.EnableProfiling, "profiling", true, "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. This parameter is ignored if a config file is specified in --config.") fs.BoolVar(&o.EnableContentionProfiling, "contention-profiling", true, "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. This parameter is ignored if a config file is specified in --config.") fs.StringVar(&o.Kubeconfig, "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. This parameter is ignored if a config file is specified in --config.") fs.StringVar(&o.ContentType, "kube-api-content-type", "application/vnd.kubernetes.protobuf", "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. This parameter is ignored if a config file is specified in --config.") fs.Float32Var(&o.QPS, "kube-api-qps", 50.0, "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. This parameter is ignored if a config file is specified in --config.") fs.Int32Var(&o.Burst, "kube-api-burst", 100, "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. This parameter is ignored if a config file is specified in --config.") fs.StringVar(&o.ResourceNamespace, "lock-object-namespace", "kube-system", "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. This parameter is ignored if a config file is specified in --config.") fs.StringVar(&o.ResourceName, "lock-object-name", "kube-scheduler", "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.")
} }
// Validate validates the deprecated scheduler options. // Validate validates the deprecated scheduler options.
......
...@@ -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 or :: for listening in all interfaces and IP families). See --bind-address instead. This parameter is ignored if a config file is specified in --config.") fs.StringVar(&o.BindAddress, "address", "0.0.0.0", "DEPRECATED: the IP address on which to listen for the --port port (set to 0.0.0.0 or :: for listening in all interfaces and IP families). 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. This parameter is ignored if a config file is specified in --config.") fs.IntVar(&o.BindPort, "port", kubeschedulerconfig.DefaultInsecureSchedulerPort, "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.")
} }
......
...@@ -43,14 +43,13 @@ import ( ...@@ -43,14 +43,13 @@ import (
schedulerappconfig "k8s.io/kubernetes/cmd/kube-scheduler/app/config" schedulerappconfig "k8s.io/kubernetes/cmd/kube-scheduler/app/config"
"k8s.io/kubernetes/pkg/scheduler" "k8s.io/kubernetes/pkg/scheduler"
kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config" kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config"
"k8s.io/kubernetes/pkg/scheduler/apis/config/latest"
"k8s.io/kubernetes/pkg/scheduler/apis/config/validation" "k8s.io/kubernetes/pkg/scheduler/apis/config/validation"
) )
// Options has all the params needed to run a Scheduler // Options has all the params needed to run a Scheduler
type Options struct { type Options struct {
// The default values. These are overridden if ConfigFile is set or by values in InsecureServing. // The default values. These are overridden if ConfigFile is set or by values in InsecureServing.
ComponentConfig kubeschedulerconfig.KubeSchedulerConfiguration ComponentConfig *kubeschedulerconfig.KubeSchedulerConfiguration
SecureServing *apiserveroptions.SecureServingOptionsWithLoopback SecureServing *apiserveroptions.SecureServingOptionsWithLoopback
CombinedInsecureServing *CombinedInsecureServingOptions CombinedInsecureServing *CombinedInsecureServingOptions
...@@ -59,6 +58,7 @@ type Options struct { ...@@ -59,6 +58,7 @@ type Options struct {
Metrics *metrics.Options Metrics *metrics.Options
Logs *logs.Options Logs *logs.Options
Deprecated *DeprecatedOptions Deprecated *DeprecatedOptions
LeaderElection *componentbaseconfig.LeaderElectionConfiguration
// ConfigFile is the location of the scheduler server's configuration file. // ConfigFile is the location of the scheduler server's configuration file.
ConfigFile string ConfigFile string
...@@ -67,10 +67,13 @@ type Options struct { ...@@ -67,10 +67,13 @@ type Options struct {
WriteConfigTo string WriteConfigTo string
Master string Master string
// Flags hold the parsed CLI flags.
Flags *cliflag.NamedFlagSets
} }
// NewOptions returns default scheduler app options. // NewOptions returns default scheduler app options.
func NewOptions() (*Options, error) { func NewOptions() *Options {
o := &Options{ o := &Options{
SecureServing: apiserveroptions.NewSecureServingOptions().WithLoopback(), SecureServing: apiserveroptions.NewSecureServingOptions().WithLoopback(),
CombinedInsecureServing: &CombinedInsecureServingOptions{ CombinedInsecureServing: &CombinedInsecureServingOptions{
...@@ -86,6 +89,15 @@ func NewOptions() (*Options, error) { ...@@ -86,6 +89,15 @@ func NewOptions() (*Options, error) {
UseLegacyPolicyConfig: false, UseLegacyPolicyConfig: false,
PolicyConfigMapNamespace: metav1.NamespaceSystem, PolicyConfigMapNamespace: metav1.NamespaceSystem,
}, },
LeaderElection: &componentbaseconfig.LeaderElectionConfiguration{
LeaderElect: true,
LeaseDuration: metav1.Duration{Duration: 15 * time.Second},
RenewDeadline: metav1.Duration{Duration: 10 * time.Second},
RetryPeriod: metav1.Duration{Duration: 2 * time.Second},
ResourceLock: "leases",
ResourceName: "kube-scheduler",
ResourceNamespace: "kube-system",
},
Metrics: metrics.NewOptions(), Metrics: metrics.NewOptions(),
Logs: logs.NewOptions(), Logs: logs.NewOptions(),
} }
...@@ -99,82 +111,76 @@ func NewOptions() (*Options, error) { ...@@ -99,82 +111,76 @@ func NewOptions() (*Options, error) {
o.SecureServing.ServerCert.PairName = "kube-scheduler" o.SecureServing.ServerCert.PairName = "kube-scheduler"
o.SecureServing.BindPort = kubeschedulerconfig.DefaultKubeSchedulerPort o.SecureServing.BindPort = kubeschedulerconfig.DefaultKubeSchedulerPort
return o, nil o.initFlags()
return o
} }
// Complete completes the remaining instantiation of the options obj. // ApplyDeprecated obtains the deprecated CLI args and set them to `o.ComponentConfig` if specified.
// In particular, it injects the latest internal versioned ComponentConfig. func (o *Options) ApplyDeprecated() {
func (o *Options) Complete(nfs *cliflag.NamedFlagSets) error { if o.Flags == nil {
cfg, err := latest.Default() return
if err != nil {
return err
} }
hhost, hport, err := splitHostIntPort(cfg.HealthzBindAddress)
if err != nil {
return err
}
// Obtain CLI args related with insecure serving.
// If not specified in command line, derive the default settings from cfg.
insecureServing := nfs.FlagSet("insecure serving")
if !insecureServing.Changed("address") {
o.CombinedInsecureServing.BindAddress = hhost
}
if !insecureServing.Changed("port") {
o.CombinedInsecureServing.BindPort = hport
}
// Obtain deprecated CLI args. Set them to cfg if specified in command line. // Obtain deprecated CLI args. Set them to cfg if specified in command line.
deprecated := nfs.FlagSet("deprecated") deprecated := o.Flags.FlagSet("deprecated")
if deprecated.Changed("profiling") { if deprecated.Changed("profiling") {
cfg.EnableProfiling = o.ComponentConfig.EnableProfiling o.ComponentConfig.EnableProfiling = o.Deprecated.EnableProfiling
} }
if deprecated.Changed("contention-profiling") { if deprecated.Changed("contention-profiling") {
cfg.EnableContentionProfiling = o.ComponentConfig.EnableContentionProfiling o.ComponentConfig.EnableContentionProfiling = o.Deprecated.EnableContentionProfiling
} }
if deprecated.Changed("kubeconfig") { if deprecated.Changed("kubeconfig") {
cfg.ClientConnection.Kubeconfig = o.ComponentConfig.ClientConnection.Kubeconfig o.ComponentConfig.ClientConnection.Kubeconfig = o.Deprecated.Kubeconfig
} }
if deprecated.Changed("kube-api-content-type") { if deprecated.Changed("kube-api-content-type") {
cfg.ClientConnection.ContentType = o.ComponentConfig.ClientConnection.ContentType o.ComponentConfig.ClientConnection.ContentType = o.Deprecated.ContentType
} }
if deprecated.Changed("kube-api-qps") { if deprecated.Changed("kube-api-qps") {
cfg.ClientConnection.QPS = o.ComponentConfig.ClientConnection.QPS o.ComponentConfig.ClientConnection.QPS = o.Deprecated.QPS
} }
if deprecated.Changed("kube-api-burst") { if deprecated.Changed("kube-api-burst") {
cfg.ClientConnection.Burst = o.ComponentConfig.ClientConnection.Burst o.ComponentConfig.ClientConnection.Burst = o.Deprecated.Burst
} }
if deprecated.Changed("lock-object-namespace") { if deprecated.Changed("lock-object-namespace") {
cfg.LeaderElection.ResourceNamespace = o.ComponentConfig.LeaderElection.ResourceNamespace o.ComponentConfig.LeaderElection.ResourceNamespace = o.Deprecated.ResourceNamespace
} }
if deprecated.Changed("lock-object-name") { if deprecated.Changed("lock-object-name") {
cfg.LeaderElection.ResourceName = o.ComponentConfig.LeaderElection.ResourceName o.ComponentConfig.LeaderElection.ResourceName = o.Deprecated.ResourceName
} }
// Obtain CLI args related with leaderelection. Set them to cfg if specified in command line. }
leaderelection := nfs.FlagSet("leader election")
// ApplyLeaderElectionTo obtains the CLI args related with leaderelection, and override the values in `cfg`.
// Then the `cfg` object is injected into the `options` object.
func (o *Options) ApplyLeaderElectionTo(cfg *kubeschedulerconfig.KubeSchedulerConfiguration) {
if o.Flags == nil {
return
}
// Obtain CLI args related with leaderelection. Set them to `cfg` if specified in command line.
leaderelection := o.Flags.FlagSet("leader election")
if leaderelection.Changed("leader-elect") { if leaderelection.Changed("leader-elect") {
cfg.LeaderElection.LeaderElect = o.ComponentConfig.LeaderElection.LeaderElect cfg.LeaderElection.LeaderElect = o.LeaderElection.LeaderElect
} }
if leaderelection.Changed("leader-elect-lease-duration") { if leaderelection.Changed("leader-elect-lease-duration") {
cfg.LeaderElection.LeaseDuration = o.ComponentConfig.LeaderElection.LeaseDuration cfg.LeaderElection.LeaseDuration = o.LeaderElection.LeaseDuration
} }
if leaderelection.Changed("leader-elect-renew-deadline") { if leaderelection.Changed("leader-elect-renew-deadline") {
cfg.LeaderElection.RenewDeadline = o.ComponentConfig.LeaderElection.RenewDeadline cfg.LeaderElection.RenewDeadline = o.LeaderElection.RenewDeadline
} }
if leaderelection.Changed("leader-elect-retry-period") { if leaderelection.Changed("leader-elect-retry-period") {
cfg.LeaderElection.RetryPeriod = o.ComponentConfig.LeaderElection.RetryPeriod cfg.LeaderElection.RetryPeriod = o.LeaderElection.RetryPeriod
} }
if leaderelection.Changed("leader-elect-resource-lock") { if leaderelection.Changed("leader-elect-resource-lock") {
cfg.LeaderElection.ResourceLock = o.ComponentConfig.LeaderElection.ResourceLock cfg.LeaderElection.ResourceLock = o.LeaderElection.ResourceLock
} }
if leaderelection.Changed("leader-elect-resource-name") { if leaderelection.Changed("leader-elect-resource-name") {
cfg.LeaderElection.ResourceName = o.ComponentConfig.LeaderElection.ResourceName cfg.LeaderElection.ResourceName = o.LeaderElection.ResourceName
} }
if leaderelection.Changed("leader-elect-resource-namespace") { if leaderelection.Changed("leader-elect-resource-namespace") {
cfg.LeaderElection.ResourceNamespace = o.ComponentConfig.LeaderElection.ResourceNamespace cfg.LeaderElection.ResourceNamespace = o.LeaderElection.ResourceNamespace
} }
o.ComponentConfig = *cfg o.ComponentConfig = cfg
return nil
} }
func splitHostIntPort(s string) (string, int, error) { func splitHostIntPort(s string) (string, int, error) {
...@@ -189,8 +195,13 @@ func splitHostIntPort(s string) (string, int, error) { ...@@ -189,8 +195,13 @@ func splitHostIntPort(s string) (string, int, error) {
return host, portInt, err return host, portInt, err
} }
// Flags returns flags for a specific scheduler by section name // initFlags initializes flags by section name.
func (o *Options) Flags() (nfs cliflag.NamedFlagSets) { func (o *Options) initFlags() {
if o.Flags != nil {
return
}
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:
--policy-config-file --policy-config-file
...@@ -203,20 +214,22 @@ func (o *Options) Flags() (nfs cliflag.NamedFlagSets) { ...@@ -203,20 +214,22 @@ func (o *Options) Flags() (nfs cliflag.NamedFlagSets) {
o.CombinedInsecureServing.AddFlags(nfs.FlagSet("insecure serving")) o.CombinedInsecureServing.AddFlags(nfs.FlagSet("insecure serving"))
o.Authentication.AddFlags(nfs.FlagSet("authentication")) o.Authentication.AddFlags(nfs.FlagSet("authentication"))
o.Authorization.AddFlags(nfs.FlagSet("authorization")) o.Authorization.AddFlags(nfs.FlagSet("authorization"))
o.Deprecated.AddFlags(nfs.FlagSet("deprecated"), &o.ComponentConfig) o.Deprecated.AddFlags(nfs.FlagSet("deprecated"))
options.BindLeaderElectionFlags(o.LeaderElection, nfs.FlagSet("leader election"))
options.BindLeaderElectionFlags(&o.ComponentConfig.LeaderElection, nfs.FlagSet("leader election"))
utilfeature.DefaultMutableFeatureGate.AddFlag(nfs.FlagSet("feature gate")) utilfeature.DefaultMutableFeatureGate.AddFlag(nfs.FlagSet("feature gate"))
o.Metrics.AddFlags(nfs.FlagSet("metrics")) o.Metrics.AddFlags(nfs.FlagSet("metrics"))
o.Logs.AddFlags(nfs.FlagSet("logs")) o.Logs.AddFlags(nfs.FlagSet("logs"))
return nfs o.Flags = &nfs
} }
// ApplyTo applies the scheduler options to the given scheduler app configuration. // ApplyTo applies the scheduler options to the given scheduler app configuration.
func (o *Options) ApplyTo(c *schedulerappconfig.Config) error { func (o *Options) ApplyTo(c *schedulerappconfig.Config) error {
if len(o.ConfigFile) == 0 { if len(o.ConfigFile) == 0 {
c.ComponentConfig = o.ComponentConfig // If the --config arg is not specified, honor the deprecated as well as leader election CLI args.
o.ApplyDeprecated()
o.ApplyLeaderElectionTo(o.ComponentConfig)
c.ComponentConfig = *o.ComponentConfig
o.Deprecated.ApplyTo(c) o.Deprecated.ApplyTo(c)
...@@ -228,6 +241,9 @@ func (o *Options) ApplyTo(c *schedulerappconfig.Config) error { ...@@ -228,6 +241,9 @@ func (o *Options) ApplyTo(c *schedulerappconfig.Config) error {
if err != nil { if err != nil {
return err return err
} }
// If the --config arg is specified, honor the leader election CLI args only.
o.ApplyLeaderElectionTo(cfg)
if err := validation.ValidateKubeSchedulerConfiguration(cfg); err != nil { if err := validation.ValidateKubeSchedulerConfiguration(cfg); err != nil {
return err return err
} }
...@@ -269,7 +285,7 @@ func (o *Options) ApplyTo(c *schedulerappconfig.Config) error { ...@@ -269,7 +285,7 @@ func (o *Options) ApplyTo(c *schedulerappconfig.Config) error {
func (o *Options) Validate() []error { func (o *Options) Validate() []error {
var errs []error var errs []error
if err := validation.ValidateKubeSchedulerConfiguration(&o.ComponentConfig); err != nil { if err := validation.ValidateKubeSchedulerConfiguration(o.ComponentConfig); err != nil {
errs = append(errs, err.Errors()...) errs = append(errs, err.Errors()...)
} }
errs = append(errs, o.SecureServing.Validate()...) errs = append(errs, o.SecureServing.Validate()...)
......
...@@ -53,6 +53,7 @@ import ( ...@@ -53,6 +53,7 @@ import (
"k8s.io/kubernetes/cmd/kube-scheduler/app/options" "k8s.io/kubernetes/cmd/kube-scheduler/app/options"
"k8s.io/kubernetes/pkg/scheduler" "k8s.io/kubernetes/pkg/scheduler"
kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config" kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config"
"k8s.io/kubernetes/pkg/scheduler/apis/config/latest"
"k8s.io/kubernetes/pkg/scheduler/framework/runtime" "k8s.io/kubernetes/pkg/scheduler/framework/runtime"
"k8s.io/kubernetes/pkg/scheduler/metrics/resources" "k8s.io/kubernetes/pkg/scheduler/metrics/resources"
"k8s.io/kubernetes/pkg/scheduler/profile" "k8s.io/kubernetes/pkg/scheduler/profile"
...@@ -63,12 +64,8 @@ type Option func(runtime.Registry) error ...@@ -63,12 +64,8 @@ type Option func(runtime.Registry) error
// NewSchedulerCommand creates a *cobra.Command object with default parameters and registryOptions // NewSchedulerCommand creates a *cobra.Command object with default parameters and registryOptions
func NewSchedulerCommand(registryOptions ...Option) *cobra.Command { func NewSchedulerCommand(registryOptions ...Option) *cobra.Command {
opts, err := options.NewOptions() opts := options.NewOptions()
if err != nil {
klog.Fatalf("unable to initialize command options: %v", err)
}
namedFlagSets := opts.Flags()
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "kube-scheduler", Use: "kube-scheduler",
Long: `The Kubernetes scheduler is a control plane process which assigns Long: `The Kubernetes scheduler is a control plane process which assigns
...@@ -80,10 +77,6 @@ kube-scheduler is the reference implementation. ...@@ -80,10 +77,6 @@ kube-scheduler is the reference implementation.
See [scheduling](https://kubernetes.io/docs/concepts/scheduling-eviction/) See [scheduling](https://kubernetes.io/docs/concepts/scheduling-eviction/)
for more information about scheduling and the kube-scheduler component.`, for more information about scheduling and the kube-scheduler component.`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := opts.Complete(&namedFlagSets); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
if err := runCommand(cmd, opts, registryOptions...); err != nil { if err := runCommand(cmd, opts, registryOptions...); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err) fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1) os.Exit(1)
...@@ -98,15 +91,17 @@ for more information about scheduling and the kube-scheduler component.`, ...@@ -98,15 +91,17 @@ for more information about scheduling and the kube-scheduler component.`,
return nil return nil
}, },
} }
nfs := opts.Flags
verflag.AddFlags(nfs.FlagSet("global"))
globalflag.AddGlobalFlags(nfs.FlagSet("global"), cmd.Name())
fs := cmd.Flags() fs := cmd.Flags()
verflag.AddFlags(namedFlagSets.FlagSet("global")) for _, f := range nfs.FlagSets {
globalflag.AddGlobalFlags(namedFlagSets.FlagSet("global"), cmd.Name())
for _, f := range namedFlagSets.FlagSets {
fs.AddFlagSet(f) fs.AddFlagSet(f)
} }
cols, _, _ := term.TerminalSize(cmd.OutOrStdout()) cols, _, _ := term.TerminalSize(cmd.OutOrStdout())
cliflag.SetUsageAndHelpFunc(cmd, namedFlagSets, cols) cliflag.SetUsageAndHelpFunc(cmd, *nfs, cols)
cmd.MarkFlagFilename("config", "yaml", "yml", "json") cmd.MarkFlagFilename("config", "yaml", "yml", "json")
...@@ -308,6 +303,12 @@ func WithPlugin(name string, factory runtime.PluginFactory) Option { ...@@ -308,6 +303,12 @@ func WithPlugin(name string, factory runtime.PluginFactory) Option {
// Setup creates a completed config and a scheduler based on the command args and options // Setup creates a completed config and a scheduler based on the command args and options
func Setup(ctx context.Context, opts *options.Options, outOfTreeRegistryOptions ...Option) (*schedulerserverconfig.CompletedConfig, *scheduler.Scheduler, error) { func Setup(ctx context.Context, opts *options.Options, outOfTreeRegistryOptions ...Option) (*schedulerserverconfig.CompletedConfig, *scheduler.Scheduler, error) {
if cfg, err := latest.Default(); err != nil {
return nil, nil, err
} else {
opts.ComponentConfig = cfg
}
if errs := opts.Validate(); len(errs) > 0 { if errs := opts.Validate(); len(errs) > 0 {
return nil, nil, utilerrors.NewAggregate(errs) return nil, nil, utilerrors.NewAggregate(errs)
} }
......
...@@ -304,12 +304,11 @@ func (c *Controller) syncEndpoints(key string) error { ...@@ -304,12 +304,11 @@ func (c *Controller) syncEndpoints(key string) error {
return err return err
} }
// This means that if a Service transitions away from a nil selector, any // If a selector is specified, clean up any mirrored slices.
// mirrored EndpointSlices will not be cleaned up. #91072 tracks this issue
// for this controller along with the Endpoints and EndpointSlice
// controllers.
if svc.Spec.Selector != nil { if svc.Spec.Selector != nil {
return nil klog.V(4).Infof("%s/%s Service now has selector, cleaning up any mirrored EndpointSlices", namespace, name)
c.endpointSliceTracker.DeleteService(namespace, name)
return c.deleteMirroredSlices(namespace, name)
} }
endpointSlices, err := endpointSlicesMirroredForService(c.endpointSliceLister, namespace, name) endpointSlices, err := endpointSlicesMirroredForService(c.endpointSliceLister, namespace, name)
...@@ -372,7 +371,7 @@ func (c *Controller) onServiceUpdate(prevObj, obj interface{}) { ...@@ -372,7 +371,7 @@ func (c *Controller) onServiceUpdate(prevObj, obj interface{}) {
service := obj.(*v1.Service) service := obj.(*v1.Service)
prevService := prevObj.(*v1.Service) prevService := prevObj.(*v1.Service)
if service == nil || prevService == nil { if service == nil || prevService == nil {
utilruntime.HandleError(fmt.Errorf("onServiceUpdate() expected type v1.Endpoints, got %T, %T", prevObj, obj)) utilruntime.HandleError(fmt.Errorf("onServiceUpdate() expected type v1.Service, got %T, %T", prevObj, obj))
return return
} }
if (service.Spec.Selector == nil) != (prevService.Spec.Selector == nil) { if (service.Spec.Selector == nil) != (prevService.Spec.Selector == nil) {
......
...@@ -28,7 +28,7 @@ import ( ...@@ -28,7 +28,7 @@ import (
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/util" "k8s.io/kubernetes/pkg/volume/util"
......
...@@ -151,13 +151,13 @@ func (rc *reconciler) reconcile() { ...@@ -151,13 +151,13 @@ func (rc *reconciler) reconcile() {
// The operation key format is different depending on whether the volume // The operation key format is different depending on whether the volume
// allows multi attach across different nodes. // allows multi attach across different nodes.
if util.IsMultiAttachAllowed(attachedVolume.VolumeSpec) { if util.IsMultiAttachAllowed(attachedVolume.VolumeSpec) {
if rc.attacherDetacher.IsOperationPending(attachedVolume.VolumeName, "" /* podName */, attachedVolume.NodeName) { if !rc.attacherDetacher.IsOperationSafeToRetry(attachedVolume.VolumeName, "" /* podName */, attachedVolume.NodeName, operationexecutor.DetachOperationName) {
klog.V(10).Infof("Operation for volume %q is already running for node %q. Can't start detach", attachedVolume.VolumeName, attachedVolume.NodeName) klog.V(10).Infof("Operation for volume %q is already running or still in exponential backoff for node %q. Can't start detach", attachedVolume.VolumeName, attachedVolume.NodeName)
continue continue
} }
} else { } else {
if rc.attacherDetacher.IsOperationPending(attachedVolume.VolumeName, "" /* podName */, "" /* nodeName */) { if !rc.attacherDetacher.IsOperationSafeToRetry(attachedVolume.VolumeName, "" /* podName */, "" /* nodeName */, operationexecutor.DetachOperationName) {
klog.V(10).Infof("Operation for volume %q is already running in the cluster. Can't start detach for %q", attachedVolume.VolumeName, attachedVolume.NodeName) klog.V(10).Infof("Operation for volume %q is already running or still in exponential backoff in the cluster. Can't start detach for %q", attachedVolume.VolumeName, attachedVolume.NodeName)
continue continue
} }
} }
...@@ -193,6 +193,8 @@ func (rc *reconciler) reconcile() { ...@@ -193,6 +193,8 @@ func (rc *reconciler) reconcile() {
// Before triggering volume detach, mark volume as detached and update the node status // Before triggering volume detach, mark volume as detached and update the node status
// If it fails to update node status, skip detach volume // If it fails to update node status, skip detach volume
// If volume detach operation fails, the volume needs to be added back to report as attached so that node status
// has the correct volume attachment information.
err = rc.actualStateOfWorld.RemoveVolumeFromReportAsAttached(attachedVolume.VolumeName, attachedVolume.NodeName) err = rc.actualStateOfWorld.RemoveVolumeFromReportAsAttached(attachedVolume.VolumeName, attachedVolume.NodeName)
if err != nil { if err != nil {
klog.V(5).Infof("RemoveVolumeFromReportAsAttached failed while removing volume %q from node %q with: %v", klog.V(5).Infof("RemoveVolumeFromReportAsAttached failed while removing volume %q from node %q with: %v",
...@@ -222,13 +224,20 @@ func (rc *reconciler) reconcile() { ...@@ -222,13 +224,20 @@ func (rc *reconciler) reconcile() {
klog.Warningf(attachedVolume.GenerateMsgDetailed("attacherDetacher.DetachVolume started", fmt.Sprintf("This volume is not safe to detach, but maxWaitForUnmountDuration %v expired, force detaching", rc.maxWaitForUnmountDuration))) klog.Warningf(attachedVolume.GenerateMsgDetailed("attacherDetacher.DetachVolume started", fmt.Sprintf("This volume is not safe to detach, but maxWaitForUnmountDuration %v expired, force detaching", rc.maxWaitForUnmountDuration)))
} }
} }
if err != nil && !exponentialbackoff.IsExponentialBackoff(err) { if err != nil {
// Add volume back to ReportAsAttached if DetachVolume call failed so that node status updater will add it back to VolumeAttached list.
// This function is also called during executing the volume detach operation in operation_generoator.
// It is needed here too because DetachVolume call might fail before executing the actual operation in operation_executor (e.g., cannot find volume plugin etc.)
rc.actualStateOfWorld.AddVolumeToReportAsAttached(attachedVolume.VolumeName, attachedVolume.NodeName)
if !exponentialbackoff.IsExponentialBackoff(err) {
// Ignore exponentialbackoff.IsExponentialBackoff errors, they are expected. // Ignore exponentialbackoff.IsExponentialBackoff errors, they are expected.
// Log all other errors. // Log all other errors.
klog.Errorf(attachedVolume.GenerateErrorDetailed("attacherDetacher.DetachVolume failed to start", err).Error()) klog.Errorf(attachedVolume.GenerateErrorDetailed("attacherDetacher.DetachVolume failed to start", err).Error())
} }
} }
} }
}
rc.attachDesiredVolumes() rc.attachDesiredVolumes()
......
...@@ -350,7 +350,7 @@ func (c *Controller) pvcAddedUpdated(obj interface{}) { ...@@ -350,7 +350,7 @@ func (c *Controller) pvcAddedUpdated(obj interface{}) {
utilruntime.HandleError(fmt.Errorf("couldn't get key for Persistent Volume Claim %#v: %v", pvc, err)) utilruntime.HandleError(fmt.Errorf("couldn't get key for Persistent Volume Claim %#v: %v", pvc, err))
return return
} }
klog.V(4).InfoS("Got event on PVC", key) klog.V(4).InfoS("Got event on PVC", "pvc", klog.KObj(pvc))
if protectionutil.NeedToAddFinalizer(pvc, volumeutil.PVCProtectionFinalizer) || protectionutil.IsDeletionCandidate(pvc, volumeutil.PVCProtectionFinalizer) { if protectionutil.NeedToAddFinalizer(pvc, volumeutil.PVCProtectionFinalizer) || protectionutil.IsDeletionCandidate(pvc, volumeutil.PVCProtectionFinalizer) {
c.queue.Add(key) c.queue.Add(key)
......
...@@ -361,6 +361,7 @@ func ReadLogs(ctx context.Context, path, containerID string, opts *LogOptions, r ...@@ -361,6 +361,7 @@ func ReadLogs(ctx context.Context, path, containerID string, opts *LogOptions, r
} }
return fmt.Errorf("failed to open log file %q: %v", path, err) return fmt.Errorf("failed to open log file %q: %v", path, err)
} }
defer newF.Close()
f.Close() f.Close()
if err := watcher.Remove(f.Name()); err != nil && !os.IsNotExist(err) { if err := watcher.Remove(f.Name()); err != nil && !os.IsNotExist(err) {
klog.ErrorS(err, "Failed to remove file watch", "path", f.Name()) klog.ErrorS(err, "Failed to remove file watch", "path", f.Name())
......
...@@ -121,7 +121,9 @@ func (info *BaseEndpointInfo) Port() (int, error) { ...@@ -121,7 +121,9 @@ func (info *BaseEndpointInfo) Port() (int, error) {
// Equal is part of proxy.Endpoint interface. // Equal is part of proxy.Endpoint interface.
func (info *BaseEndpointInfo) Equal(other Endpoint) bool { func (info *BaseEndpointInfo) Equal(other Endpoint) bool {
return info.String() == other.String() && info.GetIsLocal() == other.GetIsLocal() return info.String() == other.String() &&
info.GetIsLocal() == other.GetIsLocal() &&
info.IsReady() == other.IsReady()
} }
// GetNodeName returns the NodeName for this endpoint. // GetNodeName returns the NodeName for this endpoint.
...@@ -536,13 +538,22 @@ func (em EndpointsMap) getLocalReadyEndpointIPs() map[types.NamespacedName]sets. ...@@ -536,13 +538,22 @@ func (em EndpointsMap) getLocalReadyEndpointIPs() map[types.NamespacedName]sets.
// detectStaleConnections modifies <staleEndpoints> and <staleServices> with detected stale connections. <staleServiceNames> // detectStaleConnections modifies <staleEndpoints> and <staleServices> with detected stale connections. <staleServiceNames>
// is used to store stale udp service in order to clear udp conntrack later. // is used to store stale udp service in order to clear udp conntrack later.
func detectStaleConnections(oldEndpointsMap, newEndpointsMap EndpointsMap, staleEndpoints *[]ServiceEndpoint, staleServiceNames *[]ServicePortName) { func detectStaleConnections(oldEndpointsMap, newEndpointsMap EndpointsMap, staleEndpoints *[]ServiceEndpoint, staleServiceNames *[]ServicePortName) {
// Detect stale endpoints: an endpoint can have stale conntrack entries if it was receiving traffic
// and then goes unready or changes its IP address.
for svcPortName, epList := range oldEndpointsMap { for svcPortName, epList := range oldEndpointsMap {
if svcPortName.Protocol != v1.ProtocolUDP { if svcPortName.Protocol != v1.ProtocolUDP {
continue continue
} }
for _, ep := range epList { for _, ep := range epList {
// if the old endpoint wasn't ready is not possible to have stale entries
// since there was no traffic sent to it.
if !ep.IsReady() {
continue
}
stale := true stale := true
// Check if the endpoint has changed, including if it went from ready to not ready.
// If it did change stale entries for the old endpoint has to be cleared.
for i := range newEndpointsMap[svcPortName] { for i := range newEndpointsMap[svcPortName] {
if newEndpointsMap[svcPortName][i].Equal(ep) { if newEndpointsMap[svcPortName][i].Equal(ep) {
stale = false stale = false
...@@ -556,13 +567,29 @@ func detectStaleConnections(oldEndpointsMap, newEndpointsMap EndpointsMap, stale ...@@ -556,13 +567,29 @@ func detectStaleConnections(oldEndpointsMap, newEndpointsMap EndpointsMap, stale
} }
} }
// Detect stale services
// For udp service, if its backend changes from 0 to non-0 ready endpoints.
// There may exist a conntrack entry that could blackhole traffic to the service.
for svcPortName, epList := range newEndpointsMap { for svcPortName, epList := range newEndpointsMap {
if svcPortName.Protocol != v1.ProtocolUDP { if svcPortName.Protocol != v1.ProtocolUDP {
continue continue
} }
// For udp service, if its backend changes from 0 to non-0. There may exist a conntrack entry that could blackhole traffic to the service. epReady := 0
if len(epList) > 0 && len(oldEndpointsMap[svcPortName]) == 0 { for _, ep := range epList {
if ep.IsReady() {
epReady++
}
}
oldEpReady := 0
for _, ep := range oldEndpointsMap[svcPortName] {
if ep.IsReady() {
oldEpReady++
}
}
if epReady > 0 && oldEpReady == 0 {
*staleServiceNames = append(*staleServiceNames, svcPortName) *staleServiceNames = append(*staleServiceNames, svcPortName)
} }
} }
......
...@@ -89,6 +89,7 @@ func NewBalancedAllocation(baArgs runtime.Object, h framework.Handle, fts featur ...@@ -89,6 +89,7 @@ func NewBalancedAllocation(baArgs runtime.Object, h framework.Handle, fts featur
resourceAllocationScorer: resourceAllocationScorer{ resourceAllocationScorer: resourceAllocationScorer{
Name: BalancedAllocationName, Name: BalancedAllocationName,
scorer: balancedResourceScorer, scorer: balancedResourceScorer,
useRequested: true,
resourceToWeightMap: resToWeightMap, resourceToWeightMap: resToWeightMap,
enablePodOverhead: fts.EnablePodOverhead, enablePodOverhead: fts.EnablePodOverhead,
}, },
......
...@@ -33,6 +33,9 @@ type scorer func(args *config.NodeResourcesFitArgs) *resourceAllocationScorer ...@@ -33,6 +33,9 @@ type scorer func(args *config.NodeResourcesFitArgs) *resourceAllocationScorer
// resourceAllocationScorer contains information to calculate resource allocation score. // resourceAllocationScorer contains information to calculate resource allocation score.
type resourceAllocationScorer struct { type resourceAllocationScorer struct {
Name string Name string
// used to decide whether to use Requested or NonZeroRequested for
// cpu and memory.
useRequested bool
scorer func(requested, allocable resourceToValueMap) int64 scorer func(requested, allocable resourceToValueMap) int64
resourceToWeightMap resourceToWeightMap resourceToWeightMap resourceToWeightMap
...@@ -53,10 +56,11 @@ func (r *resourceAllocationScorer) score( ...@@ -53,10 +56,11 @@ func (r *resourceAllocationScorer) score(
if r.resourceToWeightMap == nil { if r.resourceToWeightMap == nil {
return 0, framework.NewStatus(framework.Error, "resources not found") return 0, framework.NewStatus(framework.Error, "resources not found")
} }
requested := make(resourceToValueMap) requested := make(resourceToValueMap)
allocatable := make(resourceToValueMap) allocatable := make(resourceToValueMap)
for resource := range r.resourceToWeightMap { for resource := range r.resourceToWeightMap {
alloc, req := calculateResourceAllocatableRequest(nodeInfo, pod, resource, r.enablePodOverhead) alloc, req := r.calculateResourceAllocatableRequest(nodeInfo, pod, resource)
if alloc != 0 { if alloc != 0 {
// Only fill the extended resource entry when it's non-zero. // Only fill the extended resource entry when it's non-zero.
allocatable[resource], requested[resource] = alloc, req allocatable[resource], requested[resource] = alloc, req
...@@ -80,8 +84,13 @@ func (r *resourceAllocationScorer) score( ...@@ -80,8 +84,13 @@ func (r *resourceAllocationScorer) score(
// - 1st param: quantity of allocatable resource on the node. // - 1st param: quantity of allocatable resource on the node.
// - 2nd param: aggregated quantity of requested resource on the node. // - 2nd param: aggregated quantity of requested resource on the node.
// Note: if it's an extended resource, and the pod doesn't request it, (0, 0) is returned. // Note: if it's an extended resource, and the pod doesn't request it, (0, 0) is returned.
func calculateResourceAllocatableRequest(nodeInfo *framework.NodeInfo, pod *v1.Pod, resource v1.ResourceName, enablePodOverhead bool) (int64, int64) { func (r *resourceAllocationScorer) calculateResourceAllocatableRequest(nodeInfo *framework.NodeInfo, pod *v1.Pod, resource v1.ResourceName) (int64, int64) {
podRequest := calculatePodResourceRequest(pod, resource, enablePodOverhead) requested := nodeInfo.NonZeroRequested
if r.useRequested {
requested = nodeInfo.Requested
}
podRequest := r.calculatePodResourceRequest(pod, resource)
// If it's an extended resource, and the pod doesn't request it. We return (0, 0) // If it's an extended resource, and the pod doesn't request it. We return (0, 0)
// as an implication to bypass scoring on this resource. // as an implication to bypass scoring on this resource.
if podRequest == 0 && schedutil.IsScalarResourceName(resource) { if podRequest == 0 && schedutil.IsScalarResourceName(resource) {
...@@ -89,9 +98,9 @@ func calculateResourceAllocatableRequest(nodeInfo *framework.NodeInfo, pod *v1.P ...@@ -89,9 +98,9 @@ func calculateResourceAllocatableRequest(nodeInfo *framework.NodeInfo, pod *v1.P
} }
switch resource { switch resource {
case v1.ResourceCPU: case v1.ResourceCPU:
return nodeInfo.Allocatable.MilliCPU, (nodeInfo.NonZeroRequested.MilliCPU + podRequest) return nodeInfo.Allocatable.MilliCPU, (requested.MilliCPU + podRequest)
case v1.ResourceMemory: case v1.ResourceMemory:
return nodeInfo.Allocatable.Memory, (nodeInfo.NonZeroRequested.Memory + podRequest) return nodeInfo.Allocatable.Memory, (requested.Memory + podRequest)
case v1.ResourceEphemeralStorage: case v1.ResourceEphemeralStorage:
return nodeInfo.Allocatable.EphemeralStorage, (nodeInfo.Requested.EphemeralStorage + podRequest) return nodeInfo.Allocatable.EphemeralStorage, (nodeInfo.Requested.EphemeralStorage + podRequest)
default: default:
...@@ -108,24 +117,24 @@ func calculateResourceAllocatableRequest(nodeInfo *framework.NodeInfo, pod *v1.P ...@@ -108,24 +117,24 @@ func calculateResourceAllocatableRequest(nodeInfo *framework.NodeInfo, pod *v1.P
// calculatePodResourceRequest returns the total non-zero requests. If Overhead is defined for the pod and the // calculatePodResourceRequest returns the total non-zero requests. If Overhead is defined for the pod and the
// PodOverhead feature is enabled, the Overhead is added to the result. // PodOverhead feature is enabled, the Overhead is added to the result.
// podResourceRequest = max(sum(podSpec.Containers), podSpec.InitContainers) + overHead // podResourceRequest = max(sum(podSpec.Containers), podSpec.InitContainers) + overHead
func calculatePodResourceRequest(pod *v1.Pod, resource v1.ResourceName, enablePodOverhead bool) int64 { func (r *resourceAllocationScorer) calculatePodResourceRequest(pod *v1.Pod, resource v1.ResourceName) int64 {
var podRequest int64 var podRequest int64
for i := range pod.Spec.Containers { for i := range pod.Spec.Containers {
container := &pod.Spec.Containers[i] container := &pod.Spec.Containers[i]
value := schedutil.GetNonzeroRequestForResource(resource, &container.Resources.Requests) value := schedutil.GetRequestForResource(resource, &container.Resources.Requests, !r.useRequested)
podRequest += value podRequest += value
} }
for i := range pod.Spec.InitContainers { for i := range pod.Spec.InitContainers {
initContainer := &pod.Spec.InitContainers[i] initContainer := &pod.Spec.InitContainers[i]
value := schedutil.GetNonzeroRequestForResource(resource, &initContainer.Resources.Requests) value := schedutil.GetRequestForResource(resource, &initContainer.Resources.Requests, !r.useRequested)
if podRequest < value { if podRequest < value {
podRequest = value podRequest = value
} }
} }
// If Overhead is being utilized, add to the total requests for the pod // If Overhead is being utilized, add to the total requests for the pod
if pod.Spec.Overhead != nil && enablePodOverhead { if pod.Spec.Overhead != nil && r.enablePodOverhead {
if quantity, found := pod.Spec.Overhead[resource]; found { if quantity, found := pod.Spec.Overhead[resource]; found {
podRequest += quantity.Value() podRequest += quantity.Value()
} }
......
...@@ -40,26 +40,27 @@ const ( ...@@ -40,26 +40,27 @@ const (
// GetNonzeroRequests returns the default cpu and memory resource request if none is found or // GetNonzeroRequests returns the default cpu and memory resource request if none is found or
// what is provided on the request. // what is provided on the request.
func GetNonzeroRequests(requests *v1.ResourceList) (int64, int64) { func GetNonzeroRequests(requests *v1.ResourceList) (int64, int64) {
return GetNonzeroRequestForResource(v1.ResourceCPU, requests), return GetRequestForResource(v1.ResourceCPU, requests, true),
GetNonzeroRequestForResource(v1.ResourceMemory, requests) GetRequestForResource(v1.ResourceMemory, requests, true)
} }
// GetNonzeroRequestForResource returns the default resource request if none is found or // GetRequestForResource returns the requested values unless nonZero is true and there is no defined request
// what is provided on the request. // for CPU and memory.
func GetNonzeroRequestForResource(resource v1.ResourceName, requests *v1.ResourceList) int64 { // If nonZero is true and the resource has no defined request for CPU or memory, it returns a default value.
func GetRequestForResource(resource v1.ResourceName, requests *v1.ResourceList, nonZero bool) int64 {
if requests == nil { if requests == nil {
return 0 return 0
} }
switch resource { switch resource {
case v1.ResourceCPU: case v1.ResourceCPU:
// Override if un-set, but not if explicitly set to zero // Override if un-set, but not if explicitly set to zero
if _, found := (*requests)[v1.ResourceCPU]; !found { if _, found := (*requests)[v1.ResourceCPU]; !found && nonZero {
return DefaultMilliCPURequest return DefaultMilliCPURequest
} }
return requests.Cpu().MilliValue() return requests.Cpu().MilliValue()
case v1.ResourceMemory: case v1.ResourceMemory:
// Override if un-set, but not if explicitly set to zero // Override if un-set, but not if explicitly set to zero
if _, found := (*requests)[v1.ResourceMemory]; !found { if _, found := (*requests)[v1.ResourceMemory]; !found && nonZero {
return DefaultMemoryRequest return DefaultMemoryRequest
} }
return requests.Memory().Value() return requests.Memory().Value()
......
...@@ -276,7 +276,8 @@ func (c *csiMountMgr) SetUpAt(dir string, mounterArgs volume.MounterArgs) error ...@@ -276,7 +276,8 @@ func (c *csiMountMgr) SetUpAt(dir string, mounterArgs volume.MounterArgs) error
c.supportsSELinux, err = c.kubeVolHost.GetHostUtil().GetSELinuxSupport(dir) c.supportsSELinux, err = c.kubeVolHost.GetHostUtil().GetSELinuxSupport(dir)
if err != nil { if err != nil {
klog.V(2).Info(log("error checking for SELinux support: %s", err)) // The volume is mounted. Return UncertainProgressError, so kubelet will unmount it when user deletes the pod.
return volumetypes.NewUncertainProgressError(fmt.Sprintf("error checking for SELinux support: %s", err))
} }
if !driverSupportsCSIVolumeMountGroup && c.supportsFSGroup(fsType, mounterArgs.FsGroup, c.fsGroupPolicy) { if !driverSupportsCSIVolumeMountGroup && c.supportsFSGroup(fsType, mounterArgs.FsGroup, c.fsGroupPolicy) {
......
...@@ -108,7 +108,7 @@ func (hu *FakeHostUtil) GetOwner(pathname string) (int64, int64, error) { ...@@ -108,7 +108,7 @@ func (hu *FakeHostUtil) GetOwner(pathname string) (int64, int64, error) {
// GetSELinuxSupport tests if pathname is on a mount that supports SELinux. // GetSELinuxSupport tests if pathname is on a mount that supports SELinux.
// Not implemented for testing // Not implemented for testing
func (hu *FakeHostUtil) GetSELinuxSupport(pathname string) (bool, error) { func (hu *FakeHostUtil) GetSELinuxSupport(pathname string) (bool, error) {
return false, errors.New("GetSELinuxSupport not implemented") return false, nil
} }
// GetMode returns permissions of pathname. // GetMode returns permissions of pathname.
......
...@@ -28,6 +28,7 @@ import ( ...@@ -28,6 +28,7 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/kubernetes/pkg/util/selinux"
"k8s.io/mount-utils" "k8s.io/mount-utils"
utilpath "k8s.io/utils/path" utilpath "k8s.io/utils/path"
) )
...@@ -229,8 +230,16 @@ func DoMakeRShared(path string, mountInfoFilename string) error { ...@@ -229,8 +230,16 @@ func DoMakeRShared(path string, mountInfoFilename string) error {
return nil return nil
} }
// selinux.SELinuxEnabled implementation for unit tests
type seLinuxEnabledFunc func() bool
// GetSELinux is common implementation of GetSELinuxSupport on Linux. // GetSELinux is common implementation of GetSELinuxSupport on Linux.
func GetSELinux(path string, mountInfoFilename string) (bool, error) { func GetSELinux(path string, mountInfoFilename string, selinuxEnabled seLinuxEnabledFunc) (bool, error) {
// Skip /proc/mounts parsing if SELinux is disabled.
if !selinuxEnabled() {
return false, nil
}
info, err := findMountInfo(path, mountInfoFilename) info, err := findMountInfo(path, mountInfoFilename)
if err != nil { if err != nil {
return false, err return false, err
...@@ -253,7 +262,7 @@ func GetSELinux(path string, mountInfoFilename string) (bool, error) { ...@@ -253,7 +262,7 @@ func GetSELinux(path string, mountInfoFilename string) (bool, error) {
// GetSELinuxSupport returns true if given path is on a mount that supports // GetSELinuxSupport returns true if given path is on a mount that supports
// SELinux. // SELinux.
func (hu *HostUtil) GetSELinuxSupport(pathname string) (bool, error) { func (hu *HostUtil) GetSELinuxSupport(pathname string) (bool, error) {
return GetSELinux(pathname, procMountInfoPath) return GetSELinux(pathname, procMountInfoPath, selinux.SELinuxEnabled)
} }
// GetOwner returns the integer ID for the user and group of the given path // GetOwner returns the integer ID for the user and group of the given path
......
...@@ -28,7 +28,7 @@ import ( ...@@ -28,7 +28,7 @@ import (
"fmt" "fmt"
"sync" "sync"
"k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
k8sRuntime "k8s.io/apimachinery/pkg/util/runtime" k8sRuntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/klog/v2" "k8s.io/klog/v2"
...@@ -106,6 +106,13 @@ type NestedPendingOperations interface { ...@@ -106,6 +106,13 @@ type NestedPendingOperations interface {
volumeName v1.UniqueVolumeName, volumeName v1.UniqueVolumeName,
podName volumetypes.UniquePodName, podName volumetypes.UniquePodName,
nodeName types.NodeName) bool nodeName types.NodeName) bool
// IsOperationSafeToRetry returns false if an operation for the given volumeName
// and one of podName or nodeName is pending or in exponential backoff, otherwise it returns true
IsOperationSafeToRetry(
volumeName v1.UniqueVolumeName,
podName volumetypes.UniquePodName,
nodeName types.NodeName, operationName string) bool
} }
// NewNestedPendingOperations returns a new instance of NestedPendingOperations. // NewNestedPendingOperations returns a new instance of NestedPendingOperations.
...@@ -185,6 +192,33 @@ func (grm *nestedPendingOperations) Run( ...@@ -185,6 +192,33 @@ func (grm *nestedPendingOperations) Run(
return nil return nil
} }
func (grm *nestedPendingOperations) IsOperationSafeToRetry(
volumeName v1.UniqueVolumeName,
podName volumetypes.UniquePodName,
nodeName types.NodeName,
operationName string) bool {
grm.lock.RLock()
defer grm.lock.RUnlock()
opKey := operationKey{volumeName, podName, nodeName}
exist, previousOpIndex := grm.isOperationExists(opKey)
if !exist {
return true
}
previousOp := grm.operations[previousOpIndex]
if previousOp.operationPending {
return false
}
backOffErr := previousOp.expBackoff.SafeToRetry(fmt.Sprintf("%+v", opKey))
if backOffErr != nil {
if previousOp.operationName == operationName {
return false
}
}
return true
}
func (grm *nestedPendingOperations) IsOperationPending( func (grm *nestedPendingOperations) IsOperationPending(
volumeName v1.UniqueVolumeName, volumeName v1.UniqueVolumeName,
......
...@@ -141,6 +141,9 @@ type OperationExecutor interface { ...@@ -141,6 +141,9 @@ type OperationExecutor interface {
// IsOperationPending returns true if an operation for the given volumeName // IsOperationPending returns true if an operation for the given volumeName
// and one of podName or nodeName is pending, otherwise it returns false // and one of podName or nodeName is pending, otherwise it returns false
IsOperationPending(volumeName v1.UniqueVolumeName, podName volumetypes.UniquePodName, nodeName types.NodeName) bool IsOperationPending(volumeName v1.UniqueVolumeName, podName volumetypes.UniquePodName, nodeName types.NodeName) bool
// IsOperationSafeToRetry returns false if an operation for the given volumeName
// and one of podName or nodeName is pending or in exponential backoff, otherwise it returns true
IsOperationSafeToRetry(volumeName v1.UniqueVolumeName, podName volumetypes.UniquePodName, nodeName types.NodeName, operationName string) bool
// ExpandInUseVolume will resize volume's file system to expected size without unmounting the volume. // ExpandInUseVolume will resize volume's file system to expected size without unmounting the volume.
ExpandInUseVolume(volumeToMount VolumeToMount, actualStateOfWorld ActualStateOfWorldMounterUpdater) error ExpandInUseVolume(volumeToMount VolumeToMount, actualStateOfWorld ActualStateOfWorldMounterUpdater) error
// ReconstructVolumeOperation construct a new volumeSpec and returns it created by plugin // ReconstructVolumeOperation construct a new volumeSpec and returns it created by plugin
...@@ -664,6 +667,14 @@ func (oe *operationExecutor) IsOperationPending( ...@@ -664,6 +667,14 @@ func (oe *operationExecutor) IsOperationPending(
return oe.pendingOperations.IsOperationPending(volumeName, podName, nodeName) return oe.pendingOperations.IsOperationPending(volumeName, podName, nodeName)
} }
func (oe *operationExecutor) IsOperationSafeToRetry(
volumeName v1.UniqueVolumeName,
podName volumetypes.UniquePodName,
nodeName types.NodeName,
operationName string) bool {
return oe.pendingOperations.IsOperationSafeToRetry(volumeName, podName, nodeName, operationName)
}
func (oe *operationExecutor) AttachVolume( func (oe *operationExecutor) AttachVolume(
volumeToAttach VolumeToAttach, volumeToAttach VolumeToAttach,
actualStateOfWorld ActualStateOfWorldAttacherUpdater) error { actualStateOfWorld ActualStateOfWorldAttacherUpdater) error {
......
...@@ -48,6 +48,7 @@ import ( ...@@ -48,6 +48,7 @@ import (
const ( const (
unknownVolumePlugin string = "UnknownVolumePlugin" unknownVolumePlugin string = "UnknownVolumePlugin"
unknownAttachableVolumePlugin string = "UnknownAttachableVolumePlugin" unknownAttachableVolumePlugin string = "UnknownAttachableVolumePlugin"
DetachOperationName string = "volume_detach"
) )
// InTreeToCSITranslator contains methods required to check migratable status // InTreeToCSITranslator contains methods required to check migratable status
...@@ -491,9 +492,9 @@ func (og *operationGenerator) GenerateDetachVolumeFunc( ...@@ -491,9 +492,9 @@ func (og *operationGenerator) GenerateDetachVolumeFunc(
} }
return volumetypes.GeneratedOperations{ return volumetypes.GeneratedOperations{
OperationName: "volume_detach", OperationName: DetachOperationName,
OperationFunc: detachVolumeFunc, OperationFunc: detachVolumeFunc,
CompleteFunc: util.OperationCompleteHook(util.GetFullQualifiedPluginNameForVolume(pluginName, volumeToDetach.VolumeSpec), "volume_detach"), CompleteFunc: util.OperationCompleteHook(util.GetFullQualifiedPluginNameForVolume(pluginName, volumeToDetach.VolumeSpec), DetachOperationName),
EventRecorderFunc: nil, // nil because we do not want to generate event on error EventRecorderFunc: nil, // nil because we do not want to generate event on error
}, nil }, nil
} }
......
...@@ -29,7 +29,6 @@ import ( ...@@ -29,7 +29,6 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/kubernetes/pkg/volume/util/hostutil"
"k8s.io/mount-utils" "k8s.io/mount-utils"
) )
...@@ -109,12 +108,12 @@ func prepareSubpathTarget(mounter mount.Interface, subpath Subpath) (bool, strin ...@@ -109,12 +108,12 @@ func prepareSubpathTarget(mounter mount.Interface, subpath Subpath) (bool, strin
notMount = true notMount = true
} }
if !notMount { if !notMount {
linuxHostUtil := hostutil.NewHostUtil() // It's already mounted, so check if it's bind-mounted to the same path
mntInfo, err := linuxHostUtil.FindMountInfo(bindPathTarget) samePath, err := checkSubPathFileEqual(subpath, bindPathTarget)
if err != nil { if err != nil {
return false, "", fmt.Errorf("error calling findMountInfo for %s: %s", bindPathTarget, err) return false, "", fmt.Errorf("error checking subpath mount info for %s: %s", bindPathTarget, err)
} }
if mntInfo.Root != subpath.Path { if !samePath {
// It's already mounted but not what we want, unmount it // It's already mounted but not what we want, unmount it
if err = mounter.Unmount(bindPathTarget); err != nil { if err = mounter.Unmount(bindPathTarget); err != nil {
return false, "", fmt.Errorf("error ummounting %s: %s", bindPathTarget, err) return false, "", fmt.Errorf("error ummounting %s: %s", bindPathTarget, err)
...@@ -155,6 +154,23 @@ func prepareSubpathTarget(mounter mount.Interface, subpath Subpath) (bool, strin ...@@ -155,6 +154,23 @@ func prepareSubpathTarget(mounter mount.Interface, subpath Subpath) (bool, strin
return false, bindPathTarget, nil return false, bindPathTarget, nil
} }
func checkSubPathFileEqual(subpath Subpath, bindMountTarget string) (bool, error) {
s, err := os.Lstat(subpath.Path)
if err != nil {
return false, fmt.Errorf("stat %s failed: %s", subpath.Path, err)
}
t, err := os.Lstat(bindMountTarget)
if err != nil {
return false, fmt.Errorf("lstat %s failed: %s", bindMountTarget, err)
}
if !os.SameFile(s, t) {
return false, nil
}
return true, nil
}
func getSubpathBindTarget(subpath Subpath) string { func getSubpathBindTarget(subpath Subpath) string {
// containerName is DNS label, i.e. safe as a directory name. // containerName is DNS label, i.e. safe as a directory name.
return filepath.Join(subpath.PodDir, containerSubPathDirectoryName, subpath.VolumeName, subpath.ContainerName, strconv.Itoa(subpath.VolumeMountIndex)) return filepath.Join(subpath.PodDir, containerSubPathDirectoryName, subpath.VolumeName, subpath.ContainerName, strconv.Itoa(subpath.VolumeMountIndex))
......
...@@ -84,15 +84,9 @@ func NormalizeWindowsPath(path string) string { ...@@ -84,15 +84,9 @@ func NormalizeWindowsPath(path string) string {
// ValidateDiskNumber : disk number should be a number in [0, 99] // ValidateDiskNumber : disk number should be a number in [0, 99]
func ValidateDiskNumber(disk string) error { func ValidateDiskNumber(disk string) error {
diskNum, err := strconv.Atoi(disk) if _, err := strconv.Atoi(disk); err != nil {
if err != nil { return fmt.Errorf("wrong disk number format: %q, err: %v", disk, err)
return fmt.Errorf("wrong disk number format: %q, err:%v", disk, err)
} }
if diskNum < 0 || diskNum > 99 {
return fmt.Errorf("disk number out of range: %q", disk)
}
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