Unverified Commit 91cf835e authored by Hussein Galal's avatar Hussein Galal Committed by GitHub

Update to v1.22.3 (#4354)

parent 7c3f21e5
...@@ -39,37 +39,37 @@ replace ( ...@@ -39,37 +39,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.2-k3s1 k8s.io/api => github.com/k3s-io/kubernetes/staging/src/k8s.io/api v1.22.3-k3s1
k8s.io/apiextensions-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiextensions-apiserver v1.22.2-k3s1 k8s.io/apiextensions-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiextensions-apiserver v1.22.3-k3s1
k8s.io/apimachinery => github.com/k3s-io/kubernetes/staging/src/k8s.io/apimachinery v1.22.2-k3s1 k8s.io/apimachinery => github.com/k3s-io/kubernetes/staging/src/k8s.io/apimachinery v1.22.3-k3s1
k8s.io/apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiserver v1.22.2-k3s1 k8s.io/apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiserver v1.22.3-k3s1
k8s.io/cli-runtime => github.com/k3s-io/kubernetes/staging/src/k8s.io/cli-runtime v1.22.2-k3s1 k8s.io/cli-runtime => github.com/k3s-io/kubernetes/staging/src/k8s.io/cli-runtime v1.22.3-k3s1
k8s.io/client-go => github.com/k3s-io/kubernetes/staging/src/k8s.io/client-go v1.22.2-k3s1 k8s.io/client-go => github.com/k3s-io/kubernetes/staging/src/k8s.io/client-go v1.22.3-k3s1
k8s.io/cloud-provider => github.com/k3s-io/kubernetes/staging/src/k8s.io/cloud-provider v1.22.2-k3s1 k8s.io/cloud-provider => github.com/k3s-io/kubernetes/staging/src/k8s.io/cloud-provider v1.22.3-k3s1
k8s.io/cluster-bootstrap => github.com/k3s-io/kubernetes/staging/src/k8s.io/cluster-bootstrap v1.22.2-k3s1 k8s.io/cluster-bootstrap => github.com/k3s-io/kubernetes/staging/src/k8s.io/cluster-bootstrap v1.22.3-k3s1
k8s.io/code-generator => github.com/k3s-io/kubernetes/staging/src/k8s.io/code-generator v1.22.2-k3s1 k8s.io/code-generator => github.com/k3s-io/kubernetes/staging/src/k8s.io/code-generator v1.22.3-k3s1
k8s.io/component-base => github.com/k3s-io/kubernetes/staging/src/k8s.io/component-base v1.22.2-k3s1 k8s.io/component-base => github.com/k3s-io/kubernetes/staging/src/k8s.io/component-base v1.22.3-k3s1
k8s.io/component-helpers => github.com/k3s-io/kubernetes/staging/src/k8s.io/component-helpers v1.22.2-k3s1 k8s.io/component-helpers => github.com/k3s-io/kubernetes/staging/src/k8s.io/component-helpers v1.22.3-k3s1
k8s.io/controller-manager => github.com/k3s-io/kubernetes/staging/src/k8s.io/controller-manager v1.22.2-k3s1 k8s.io/controller-manager => github.com/k3s-io/kubernetes/staging/src/k8s.io/controller-manager v1.22.3-k3s1
k8s.io/cri-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/cri-api v1.22.2-k3s1 k8s.io/cri-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/cri-api v1.22.3-k3s1
k8s.io/csi-translation-lib => github.com/k3s-io/kubernetes/staging/src/k8s.io/csi-translation-lib v1.22.2-k3s1 k8s.io/csi-translation-lib => github.com/k3s-io/kubernetes/staging/src/k8s.io/csi-translation-lib v1.22.3-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.2-k3s1 k8s.io/kube-aggregator => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-aggregator v1.22.3-k3s1
k8s.io/kube-controller-manager => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-controller-manager v1.22.2-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-proxy => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-proxy v1.22.2-k3s1 k8s.io/kube-proxy => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-proxy v1.22.3-k3s1
k8s.io/kube-scheduler => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-scheduler v1.22.2-k3s1 k8s.io/kube-scheduler => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-scheduler v1.22.3-k3s1
k8s.io/kubectl => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubectl v1.22.2-k3s1 k8s.io/kubectl => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubectl v1.22.3-k3s1
k8s.io/kubelet => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubelet v1.22.2-k3s1 k8s.io/kubelet => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubelet v1.22.3-k3s1
k8s.io/kubernetes => github.com/k3s-io/kubernetes v1.22.2-k3s1 k8s.io/kubernetes => github.com/k3s-io/kubernetes v1.22.3-k3s1
k8s.io/legacy-cloud-providers => github.com/k3s-io/kubernetes/staging/src/k8s.io/legacy-cloud-providers v1.22.2-k3s1 k8s.io/legacy-cloud-providers => github.com/k3s-io/kubernetes/staging/src/k8s.io/legacy-cloud-providers v1.22.3-k3s1
k8s.io/metrics => github.com/k3s-io/kubernetes/staging/src/k8s.io/metrics v1.22.2-k3s1 k8s.io/metrics => github.com/k3s-io/kubernetes/staging/src/k8s.io/metrics v1.22.3-k3s1
k8s.io/mount-utils => github.com/k3s-io/kubernetes/staging/src/k8s.io/mount-utils v1.22.2-k3s1 k8s.io/mount-utils => github.com/k3s-io/kubernetes/staging/src/k8s.io/mount-utils v1.22.3-k3s1
k8s.io/node-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/node-api v1.22.2-k3s1 k8s.io/node-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/node-api v1.22.3-k3s1
k8s.io/pod-security-admission => github.com/k3s-io/kubernetes/staging/src/k8s.io/pod-security-admission v1.22.2-k3s1 k8s.io/pod-security-admission => github.com/k3s-io/kubernetes/staging/src/k8s.io/pod-security-admission v1.22.3-k3s1
k8s.io/sample-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-apiserver v1.22.2-k3s1 k8s.io/sample-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-apiserver v1.22.3-k3s1
k8s.io/sample-cli-plugin => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-cli-plugin v1.22.2-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-controller => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-controller v1.22.2-k3s1 k8s.io/sample-controller => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-controller v1.22.3-k3s1
mvdan.cc/unparam => mvdan.cc/unparam v0.0.0-20210104141923-aac4ce9116a7 mvdan.cc/unparam => mvdan.cc/unparam v0.0.0-20210104141923-aac4ce9116a7
) )
...@@ -128,18 +128,18 @@ require ( ...@@ -128,18 +128,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.2 k8s.io/api v0.22.3
k8s.io/apimachinery v0.22.2 k8s.io/apimachinery v0.22.3
k8s.io/apiserver v0.22.2 k8s.io/apiserver v0.22.3
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.2 k8s.io/cloud-provider v0.22.3
k8s.io/component-base v0.22.2 k8s.io/component-base v0.22.3
k8s.io/controller-manager v0.22.2 // indirect k8s.io/controller-manager v0.22.3 // indirect
k8s.io/cri-api v0.22.2 k8s.io/cri-api v0.22.3
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.2 k8s.io/kubectl v0.22.3
k8s.io/kubernetes v1.22.2 k8s.io/kubernetes v1.22.3
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
) )
...@@ -17,6 +17,8 @@ limitations under the License. ...@@ -17,6 +17,8 @@ limitations under the License.
package v1 package v1
import ( import (
"bytes"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
apiequality "k8s.io/apimachinery/pkg/api/equality" apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/conversion" "k8s.io/apimachinery/pkg/conversion"
...@@ -36,21 +38,30 @@ func Convert_apiextensions_JSONSchemaProps_To_v1_JSONSchemaProps(in *apiextensio ...@@ -36,21 +38,30 @@ func Convert_apiextensions_JSONSchemaProps_To_v1_JSONSchemaProps(in *apiextensio
return nil return nil
} }
var nullLiteral = []byte(`null`)
func Convert_apiextensions_JSON_To_v1_JSON(in *apiextensions.JSON, out *JSON, s conversion.Scope) error { func Convert_apiextensions_JSON_To_v1_JSON(in *apiextensions.JSON, out *JSON, s conversion.Scope) error {
raw, err := json.Marshal(*in) raw, err := json.Marshal(*in)
if err != nil { if err != nil {
return err return err
} }
if len(raw) == 0 || bytes.Equal(raw, nullLiteral) {
// match JSON#UnmarshalJSON treatment of literal nulls
out.Raw = nil
} else {
out.Raw = raw out.Raw = raw
}
return nil return nil
} }
func Convert_v1_JSON_To_apiextensions_JSON(in *JSON, out *apiextensions.JSON, s conversion.Scope) error { func Convert_v1_JSON_To_apiextensions_JSON(in *JSON, out *apiextensions.JSON, s conversion.Scope) error {
if in != nil { if in != nil {
var i interface{} var i interface{}
if len(in.Raw) > 0 && !bytes.Equal(in.Raw, nullLiteral) {
if err := json.Unmarshal(in.Raw, &i); err != nil { if err := json.Unmarshal(in.Raw, &i); err != nil {
return err return err
} }
}
*out = i *out = i
} else { } else {
out = nil out = nil
...@@ -103,7 +114,7 @@ func Convert_apiextensions_CustomResourceDefinitionSpec_To_v1_CustomResourceDefi ...@@ -103,7 +114,7 @@ func Convert_apiextensions_CustomResourceDefinitionSpec_To_v1_CustomResourceDefi
func Convert_v1_CustomResourceDefinitionSpec_To_apiextensions_CustomResourceDefinitionSpec(in *CustomResourceDefinitionSpec, out *apiextensions.CustomResourceDefinitionSpec, s conversion.Scope) error { func Convert_v1_CustomResourceDefinitionSpec_To_apiextensions_CustomResourceDefinitionSpec(in *CustomResourceDefinitionSpec, out *apiextensions.CustomResourceDefinitionSpec, s conversion.Scope) error {
if err := autoConvert_v1_CustomResourceDefinitionSpec_To_apiextensions_CustomResourceDefinitionSpec(in, out, s); err != nil { if err := autoConvert_v1_CustomResourceDefinitionSpec_To_apiextensions_CustomResourceDefinitionSpec(in, out, s); err != nil {
return nil return err
} }
if len(out.Versions) == 0 { if len(out.Versions) == 0 {
......
...@@ -17,6 +17,7 @@ limitations under the License. ...@@ -17,6 +17,7 @@ limitations under the License.
package v1 package v1
import ( import (
"bytes"
"errors" "errors"
"k8s.io/apimachinery/pkg/util/json" "k8s.io/apimachinery/pkg/util/json"
...@@ -128,7 +129,7 @@ func (s JSON) MarshalJSON() ([]byte, error) { ...@@ -128,7 +129,7 @@ func (s JSON) MarshalJSON() ([]byte, error) {
} }
func (s *JSON) UnmarshalJSON(data []byte) error { func (s *JSON) UnmarshalJSON(data []byte) error {
if len(data) > 0 && string(data) != "null" { if len(data) > 0 && !bytes.Equal(data, nullLiteral) {
s.Raw = data s.Raw = data
} }
return nil return nil
......
...@@ -17,6 +17,8 @@ limitations under the License. ...@@ -17,6 +17,8 @@ limitations under the License.
package v1beta1 package v1beta1
import ( import (
"bytes"
"k8s.io/apimachinery/pkg/conversion" "k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/util/json" "k8s.io/apimachinery/pkg/util/json"
...@@ -36,21 +38,30 @@ func Convert_apiextensions_JSONSchemaProps_To_v1beta1_JSONSchemaProps(in *apiext ...@@ -36,21 +38,30 @@ func Convert_apiextensions_JSONSchemaProps_To_v1beta1_JSONSchemaProps(in *apiext
return nil return nil
} }
var nullLiteral = []byte(`null`)
func Convert_apiextensions_JSON_To_v1beta1_JSON(in *apiextensions.JSON, out *JSON, s conversion.Scope) error { func Convert_apiextensions_JSON_To_v1beta1_JSON(in *apiextensions.JSON, out *JSON, s conversion.Scope) error {
raw, err := json.Marshal(*in) raw, err := json.Marshal(*in)
if err != nil { if err != nil {
return err return err
} }
if len(raw) == 0 || bytes.Equal(raw, nullLiteral) {
// match JSON#UnmarshalJSON treatment of literal nulls
out.Raw = nil
} else {
out.Raw = raw out.Raw = raw
}
return nil return nil
} }
func Convert_v1beta1_JSON_To_apiextensions_JSON(in *JSON, out *apiextensions.JSON, s conversion.Scope) error { func Convert_v1beta1_JSON_To_apiextensions_JSON(in *JSON, out *apiextensions.JSON, s conversion.Scope) error {
if in != nil { if in != nil {
var i interface{} var i interface{}
if len(in.Raw) > 0 && !bytes.Equal(in.Raw, nullLiteral) {
if err := json.Unmarshal(in.Raw, &i); err != nil { if err := json.Unmarshal(in.Raw, &i); err != nil {
return err return err
} }
}
*out = i *out = i
} else { } else {
out = nil out = nil
......
...@@ -17,6 +17,7 @@ limitations under the License. ...@@ -17,6 +17,7 @@ limitations under the License.
package v1beta1 package v1beta1
import ( import (
"bytes"
"errors" "errors"
"k8s.io/apimachinery/pkg/util/json" "k8s.io/apimachinery/pkg/util/json"
...@@ -128,7 +129,7 @@ func (s JSON) MarshalJSON() ([]byte, error) { ...@@ -128,7 +129,7 @@ func (s JSON) MarshalJSON() ([]byte, error) {
} }
func (s *JSON) UnmarshalJSON(data []byte) error { func (s *JSON) UnmarshalJSON(data []byte) error {
if len(data) > 0 && string(data) != "null" { if len(data) > 0 && !bytes.Equal(data, nullLiteral) {
s.Raw = data s.Raw = data
} }
return nil return nil
......
...@@ -362,6 +362,16 @@ func NewEgressSelector(config *apiserver.EgressSelectorConfiguration) (*EgressSe ...@@ -362,6 +362,16 @@ func NewEgressSelector(config *apiserver.EgressSelectorConfiguration) (*EgressSe
return cs, nil return cs, nil
} }
// NewEgressSelectorWithMap returns a EgressSelector with the supplied EgressType to DialFunc map.
func NewEgressSelectorWithMap(m map[EgressType]utilnet.DialFunc) *EgressSelector {
if m == nil {
m = make(map[EgressType]utilnet.DialFunc)
}
return &EgressSelector{
egressToDialer: m,
}
}
// Lookup gets the dialer function for the network context. // Lookup gets the dialer function for the network context.
// This is configured for the Kubernetes API Server at startup. // This is configured for the Kubernetes API Server at startup.
func (cs *EgressSelector) Lookup(networkContext NetworkContext) (utilnet.DialFunc, error) { func (cs *EgressSelector) Lookup(networkContext NetworkContext) (utilnet.DialFunc, error) {
......
...@@ -67,6 +67,14 @@ const timeFmt = "2006-01-02T15:04:05.999" ...@@ -67,6 +67,14 @@ const timeFmt = "2006-01-02T15:04:05.999"
// undesired becomes completely unused, all the config objects are // undesired becomes completely unused, all the config objects are
// read and processed as a whole. // read and processed as a whole.
// The funcs in this package follow the naming convention that the suffix
// "Locked" means the relevant mutex must be locked at the start of each
// call and will be locked upon return. For a configController, the
// suffix "ReadLocked" stipulates a read lock while just "Locked"
// stipulates a full lock. Absence of either suffix means that either
// (a) the lock must NOT be held at call time and will not be held
// upon return or (b) locking is irrelevant.
// StartFunction begins the process of handling a request. If the // StartFunction begins the process of handling a request. If the
// request gets queued then this function uses the given hashValue as // request gets queued then this function uses the given hashValue as
// the source of entropy as it shuffle-shards the request into a // the source of entropy as it shuffle-shards the request into a
...@@ -124,10 +132,25 @@ type configController struct { ...@@ -124,10 +132,25 @@ type configController struct {
// requestWaitLimit comes from server configuration. // requestWaitLimit comes from server configuration.
requestWaitLimit time.Duration requestWaitLimit time.Duration
// watchTracker implements the necessary WatchTracker interface.
WatchTracker
// the most recent update attempts, ordered by increasing age.
// Consumer trims to keep only the last minute's worth of entries.
// The controller uses this to limit itself to at most six updates
// to a given FlowSchema in any minute.
// This may only be accessed from the one and only worker goroutine.
mostRecentUpdates []updateAttempt
// This must be locked while accessing flowSchemas or // This must be locked while accessing flowSchemas or
// priorityLevelStates. It is the lock involved in // priorityLevelStates. A lock for writing is needed
// LockingWriteMultiple. // for writing to any of the following:
lock sync.Mutex // - the flowSchemas field
// - the slice held in the flowSchemas field
// - the priorityLevelStates field
// - the map held in the priorityLevelStates field
// - any field of a priorityLevelState held in that map
lock sync.RWMutex
// flowSchemas holds the flow schema objects, sorted by increasing // flowSchemas holds the flow schema objects, sorted by increasing
// numerical (decreasing logical) matching precedence. Every // numerical (decreasing logical) matching precedence. Every
...@@ -138,16 +161,6 @@ type configController struct { ...@@ -138,16 +161,6 @@ type configController struct {
// name to the state for that level. Every name referenced from a // name to the state for that level. Every name referenced from a
// member of `flowSchemas` has an entry here. // member of `flowSchemas` has an entry here.
priorityLevelStates map[string]*priorityLevelState priorityLevelStates map[string]*priorityLevelState
// the most recent update attempts, ordered by increasing age.
// Consumer trims to keep only the last minute's worth of entries.
// The controller uses this to limit itself to at most six updates
// to a given FlowSchema in any minute.
// This may only be accessed from the one and only worker goroutine.
mostRecentUpdates []updateAttempt
// watchTracker implements the necessary WatchTracker interface.
WatchTracker
} }
type updateAttempt struct { type updateAttempt struct {
...@@ -281,8 +294,8 @@ func (cfgCtlr *configController) MaintainObservations(stopCh <-chan struct{}) { ...@@ -281,8 +294,8 @@ func (cfgCtlr *configController) MaintainObservations(stopCh <-chan struct{}) {
} }
func (cfgCtlr *configController) updateObservations() { func (cfgCtlr *configController) updateObservations() {
cfgCtlr.lock.Lock() cfgCtlr.lock.RLock()
defer cfgCtlr.lock.Unlock() defer cfgCtlr.lock.RUnlock()
for _, plc := range cfgCtlr.priorityLevelStates { for _, plc := range cfgCtlr.priorityLevelStates {
if plc.queues != nil { if plc.queues != nil {
plc.queues.UpdateObservations() plc.queues.UpdateObservations()
...@@ -765,8 +778,8 @@ func (immediateRequest) Finish(execute func()) bool { ...@@ -765,8 +778,8 @@ func (immediateRequest) Finish(execute func()) bool {
// waiting in its queue, or `Time{}` if this did not happen. // waiting in its queue, or `Time{}` if this did not happen.
func (cfgCtlr *configController) startRequest(ctx context.Context, rd RequestDigest, queueNoteFn fq.QueueNoteFn) (fs *flowcontrol.FlowSchema, pl *flowcontrol.PriorityLevelConfiguration, isExempt bool, req fq.Request, startWaitingTime time.Time) { func (cfgCtlr *configController) startRequest(ctx context.Context, rd RequestDigest, queueNoteFn fq.QueueNoteFn) (fs *flowcontrol.FlowSchema, pl *flowcontrol.PriorityLevelConfiguration, isExempt bool, req fq.Request, startWaitingTime time.Time) {
klog.V(7).Infof("startRequest(%#+v)", rd) klog.V(7).Infof("startRequest(%#+v)", rd)
cfgCtlr.lock.Lock() cfgCtlr.lock.RLock()
defer cfgCtlr.lock.Unlock() defer cfgCtlr.lock.RUnlock()
var selectedFlowSchema, catchAllFlowSchema *flowcontrol.FlowSchema var selectedFlowSchema, catchAllFlowSchema *flowcontrol.FlowSchema
for _, fs := range cfgCtlr.flowSchemas { for _, fs := range cfgCtlr.flowSchemas {
if matchesFlowSchema(rd, fs) { if matchesFlowSchema(rd, fs) {
...@@ -811,7 +824,7 @@ func (cfgCtlr *configController) startRequest(ctx context.Context, rd RequestDig ...@@ -811,7 +824,7 @@ func (cfgCtlr *configController) startRequest(ctx context.Context, rd RequestDig
klog.V(7).Infof("startRequest(%#+v) => fsName=%q, distMethod=%#+v, plName=%q, numQueues=%d", rd, selectedFlowSchema.Name, selectedFlowSchema.Spec.DistinguisherMethod, plName, numQueues) klog.V(7).Infof("startRequest(%#+v) => fsName=%q, distMethod=%#+v, plName=%q, numQueues=%d", rd, selectedFlowSchema.Name, selectedFlowSchema.Spec.DistinguisherMethod, plName, numQueues)
req, idle := plState.queues.StartRequest(ctx, &rd.Width, hashValue, flowDistinguisher, selectedFlowSchema.Name, rd.RequestInfo, rd.User, queueNoteFn) req, idle := plState.queues.StartRequest(ctx, &rd.Width, hashValue, flowDistinguisher, selectedFlowSchema.Name, rd.RequestInfo, rd.User, queueNoteFn)
if idle { if idle {
cfgCtlr.maybeReapLocked(plName, plState) cfgCtlr.maybeReapReadLocked(plName, plState)
} }
return selectedFlowSchema, plState.pl, false, req, startWaitingTime return selectedFlowSchema, plState.pl, false, req, startWaitingTime
} }
...@@ -820,8 +833,8 @@ func (cfgCtlr *configController) startRequest(ctx context.Context, rd RequestDig ...@@ -820,8 +833,8 @@ func (cfgCtlr *configController) startRequest(ctx context.Context, rd RequestDig
// priority level if it has no more use. Call this after getting a // priority level if it has no more use. Call this after getting a
// clue that the given priority level is undesired and idle. // clue that the given priority level is undesired and idle.
func (cfgCtlr *configController) maybeReap(plName string) { func (cfgCtlr *configController) maybeReap(plName string) {
cfgCtlr.lock.Lock() cfgCtlr.lock.RLock()
defer cfgCtlr.lock.Unlock() defer cfgCtlr.lock.RUnlock()
plState := cfgCtlr.priorityLevelStates[plName] plState := cfgCtlr.priorityLevelStates[plName]
if plState == nil { if plState == nil {
klog.V(7).Infof("plName=%s, plState==nil", plName) klog.V(7).Infof("plName=%s, plState==nil", plName)
...@@ -843,7 +856,7 @@ func (cfgCtlr *configController) maybeReap(plName string) { ...@@ -843,7 +856,7 @@ func (cfgCtlr *configController) maybeReap(plName string) {
// it has no more use. Call this if both (1) plState.queues is // it has no more use. Call this if both (1) plState.queues is
// non-nil and reported being idle, and (2) cfgCtlr's lock has not // non-nil and reported being idle, and (2) cfgCtlr's lock has not
// been released since then. // been released since then.
func (cfgCtlr *configController) maybeReapLocked(plName string, plState *priorityLevelState) { func (cfgCtlr *configController) maybeReapReadLocked(plName string, plState *priorityLevelState) {
if !(plState.quiescing && plState.numPending == 0) { if !(plState.quiescing && plState.numPending == 0) {
return return
} }
......
...@@ -77,3 +77,15 @@ func (c *CommandHeaderRoundTripper) ParseCommandHeaders(cmd *cobra.Command, args ...@@ -77,3 +77,15 @@ func (c *CommandHeaderRoundTripper) ParseCommandHeaders(cmd *cobra.Command, args
c.Headers[kubectlCommandHeader] = strings.Join(cmdStrs, " ") c.Headers[kubectlCommandHeader] = strings.Join(cmdStrs, " ")
} }
} }
// CancelRequest is propagated to the Delegate RoundTripper within
// if the wrapped RoundTripper implements this function.
func (c *CommandHeaderRoundTripper) CancelRequest(req *http.Request) {
type canceler interface {
CancelRequest(*http.Request)
}
// If possible, call "CancelRequest" on the wrapped Delegate RoundTripper.
if cr, ok := c.Delegate.(canceler); ok {
cr.CancelRequest(req)
}
}
...@@ -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.2-k3s1" gitVersion = "v1.22.3-k3s1"
gitCommit = "c4ebe1924cba7aeae2a97729df3e0ac4a2ce5459" gitCommit = "5d8c744cb54fc54d261b2f14e8464049aad7e32c"
gitTreeState = "clean" gitTreeState = "clean"
buildDate = "2021-09-16T19:43:43Z" buildDate = "2021-10-28T15:23:07Z"
) )
...@@ -165,7 +165,7 @@ func Convert_Map_string_To_runtime_Object_To_Slice_v1_NamedExtension(in *map[str ...@@ -165,7 +165,7 @@ func Convert_Map_string_To_runtime_Object_To_Slice_v1_NamedExtension(in *map[str
newExtension := (*in)[key] newExtension := (*in)[key]
oldExtension := runtime.RawExtension{} oldExtension := runtime.RawExtension{}
if err := runtime.Convert_runtime_Object_To_runtime_RawExtension(&newExtension, &oldExtension, s); err != nil { if err := runtime.Convert_runtime_Object_To_runtime_RawExtension(&newExtension, &oldExtension, s); err != nil {
return nil return err
} }
namedExtension := NamedExtension{key, oldExtension} namedExtension := NamedExtension{key, oldExtension}
*out = append(*out, namedExtension) *out = append(*out, namedExtension)
......
...@@ -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.2-k3s1" gitVersion = "v1.22.3-k3s1"
gitCommit = "c4ebe1924cba7aeae2a97729df3e0ac4a2ce5459" gitCommit = "5d8c744cb54fc54d261b2f14e8464049aad7e32c"
gitTreeState = "clean" gitTreeState = "clean"
buildDate = "2021-09-16T19:43:43Z" buildDate = "2021-10-28T15:23:07Z"
) )
...@@ -26,7 +26,6 @@ import ( ...@@ -26,7 +26,6 @@ import (
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/httpstream" "k8s.io/apimachinery/pkg/util/httpstream"
"k8s.io/apimachinery/pkg/util/httpstream/spdy"
utilnet "k8s.io/apimachinery/pkg/util/net" utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/proxy" "k8s.io/apimachinery/pkg/util/proxy"
auditinternal "k8s.io/apiserver/pkg/apis/audit" auditinternal "k8s.io/apiserver/pkg/apis/audit"
...@@ -161,23 +160,21 @@ func (r *proxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { ...@@ -161,23 +160,21 @@ func (r *proxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
// we need to wrap the roundtripper in another roundtripper which will apply the front proxy headers proxyRoundTripper := handlingInfo.proxyRoundTripper
proxyRoundTripper, upgrade, err := maybeWrapForConnectionUpgrades(handlingInfo.restConfig, handlingInfo.proxyRoundTripper, req) upgrade := httpstream.IsUpgradeRequest(req)
if err != nil {
proxyError(w, req, err.Error(), http.StatusInternalServerError)
return
}
proxyRoundTripper = transport.NewAuthProxyRoundTripper(user.GetName(), user.GetGroups(), user.GetExtra(), proxyRoundTripper) proxyRoundTripper = transport.NewAuthProxyRoundTripper(user.GetName(), user.GetGroups(), user.GetExtra(), proxyRoundTripper)
// if we are upgrading, then the upgrade path tries to use this request with the TLS config we provide, but it does // If we are upgrading, then the upgrade path tries to use this request with the TLS config we provide, but it does
// NOT use the roundtripper. Its a direct call that bypasses the round tripper. This means that we have to // NOT use the proxyRoundTripper. It's a direct dial that bypasses the proxyRoundTripper. This means that we have to
// attach the "correct" user headers to the request ahead of time. After the initial upgrade, we'll be back // attach the "correct" user headers to the request ahead of time.
// at the roundtripper flow, so we only have to muck with this request, but we do have to do it.
if upgrade { if upgrade {
transport.SetAuthProxyHeaders(newReq, user.GetName(), user.GetGroups(), user.GetExtra()) transport.SetAuthProxyHeaders(newReq, user.GetName(), user.GetGroups(), user.GetExtra())
} }
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.RequireSameHostRedirects = utilfeature.DefaultFeatureGate.Enabled(genericfeatures.ValidateProxyRedirects)
handler.ServeHTTP(w, newReq) handler.ServeHTTP(w, newReq)
} }
...@@ -212,27 +209,6 @@ func newRequestForProxy(location *url.URL, req *http.Request) (*http.Request, co ...@@ -212,27 +209,6 @@ func newRequestForProxy(location *url.URL, req *http.Request) (*http.Request, co
return newReq, cancelFn return newReq, cancelFn
} }
// maybeWrapForConnectionUpgrades wraps the roundtripper for upgrades. The bool indicates if it was wrapped
func maybeWrapForConnectionUpgrades(restConfig *restclient.Config, rt http.RoundTripper, req *http.Request) (http.RoundTripper, bool, error) {
if !httpstream.IsUpgradeRequest(req) {
return rt, false, nil
}
tlsConfig, err := restclient.TLSConfigFor(restConfig)
if err != nil {
return nil, true, err
}
followRedirects := utilfeature.DefaultFeatureGate.Enabled(genericfeatures.StreamingProxyRedirects)
requireSameHostRedirects := utilfeature.DefaultFeatureGate.Enabled(genericfeatures.ValidateProxyRedirects)
upgradeRoundTripper := spdy.NewRoundTripper(tlsConfig, followRedirects, requireSameHostRedirects)
wrappedRT, err := restclient.HTTPWrappersForConfig(restConfig, upgradeRoundTripper)
if err != nil {
return nil, true, err
}
return wrappedRT, true, nil
}
// responder implements rest.Responder for assisting a connector in writing objects or errors. // responder implements rest.Responder for assisting a connector in writing objects or errors.
type responder struct { type responder struct {
w http.ResponseWriter w http.ResponseWriter
......
...@@ -425,8 +425,12 @@ func addCmdHeaderHooks(cmds *cobra.Command, kubeConfigFlags *genericclioptions.C ...@@ -425,8 +425,12 @@ func addCmdHeaderHooks(cmds *cobra.Command, kubeConfigFlags *genericclioptions.C
// Wraps CommandHeaderRoundTripper around standard RoundTripper. // Wraps CommandHeaderRoundTripper around standard RoundTripper.
kubeConfigFlags.WrapConfigFn = func(c *rest.Config) *rest.Config { kubeConfigFlags.WrapConfigFn = func(c *rest.Config) *rest.Config {
c.Wrap(func(rt http.RoundTripper) http.RoundTripper { c.Wrap(func(rt http.RoundTripper) http.RoundTripper {
crt.Delegate = rt // Must be separate RoundTripper; not "crt" closure.
return crt // Fixes: https://github.com/kubernetes/kubectl/issues/1098
return &genericclioptions.CommandHeaderRoundTripper{
Delegate: rt,
Headers: crt.Headers,
}
}) })
return c return c
} }
......
...@@ -577,7 +577,7 @@ func startPVCProtectionController(ctx ControllerContext) (http.Handler, bool, er ...@@ -577,7 +577,7 @@ func startPVCProtectionController(ctx ControllerContext) (http.Handler, bool, er
ctx.InformerFactory.Core().V1().Pods(), ctx.InformerFactory.Core().V1().Pods(),
ctx.ClientBuilder.ClientOrDie("pvc-protection-controller"), ctx.ClientBuilder.ClientOrDie("pvc-protection-controller"),
utilfeature.DefaultFeatureGate.Enabled(features.StorageObjectInUseProtection), utilfeature.DefaultFeatureGate.Enabled(features.StorageObjectInUseProtection),
utilfeature.DefaultFeatureGate.Enabled(features.StorageObjectInUseProtection), utilfeature.DefaultFeatureGate.Enabled(features.GenericEphemeralVolume),
) )
if err != nil { if err != nil {
return nil, true, fmt.Errorf("failed to start the pvc protection controller: %v", err) return nil, true, fmt.Errorf("failed to start the pvc protection controller: %v", err)
......
...@@ -21,7 +21,7 @@ import ( ...@@ -21,7 +21,7 @@ import (
"reflect" "reflect"
"time" "time"
"k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
eventsv1beta1 "k8s.io/api/events/v1beta1" eventsv1beta1 "k8s.io/api/events/v1beta1"
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation" apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
...@@ -140,7 +140,7 @@ func legacyValidateEvent(event *core.Event) field.ErrorList { ...@@ -140,7 +140,7 @@ func legacyValidateEvent(event *core.Event) field.ErrorList {
} }
} else { } else {
if len(event.InvolvedObject.Namespace) == 0 && event.Namespace != metav1.NamespaceSystem { if len(event.InvolvedObject.Namespace) == 0 && event.Namespace != metav1.NamespaceDefault && event.Namespace != metav1.NamespaceSystem {
allErrs = append(allErrs, field.Invalid(field.NewPath("involvedObject", "namespace"), event.InvolvedObject.Namespace, "does not match event.namespace")) allErrs = append(allErrs, field.Invalid(field.NewPath("involvedObject", "namespace"), event.InvolvedObject.Namespace, "does not match event.namespace"))
} }
if len(event.ReportingController) == 0 { if len(event.ReportingController) == 0 {
......
...@@ -189,7 +189,7 @@ func Convert_networking_IngressBackend_To_v1beta1_IngressBackend(in *networking. ...@@ -189,7 +189,7 @@ func Convert_networking_IngressBackend_To_v1beta1_IngressBackend(in *networking.
func Convert_v1beta1_IngressSpec_To_networking_IngressSpec(in *extensionsv1beta1.IngressSpec, out *networking.IngressSpec, s conversion.Scope) error { func Convert_v1beta1_IngressSpec_To_networking_IngressSpec(in *extensionsv1beta1.IngressSpec, out *networking.IngressSpec, s conversion.Scope) error {
if err := autoConvert_v1beta1_IngressSpec_To_networking_IngressSpec(in, out, s); err != nil { if err := autoConvert_v1beta1_IngressSpec_To_networking_IngressSpec(in, out, s); err != nil {
return nil return err
} }
if in.Backend != nil { if in.Backend != nil {
out.DefaultBackend = &networking.IngressBackend{} out.DefaultBackend = &networking.IngressBackend{}
...@@ -202,7 +202,7 @@ func Convert_v1beta1_IngressSpec_To_networking_IngressSpec(in *extensionsv1beta1 ...@@ -202,7 +202,7 @@ func Convert_v1beta1_IngressSpec_To_networking_IngressSpec(in *extensionsv1beta1
func Convert_networking_IngressSpec_To_v1beta1_IngressSpec(in *networking.IngressSpec, out *extensionsv1beta1.IngressSpec, s conversion.Scope) error { func Convert_networking_IngressSpec_To_v1beta1_IngressSpec(in *networking.IngressSpec, out *extensionsv1beta1.IngressSpec, s conversion.Scope) error {
if err := autoConvert_networking_IngressSpec_To_v1beta1_IngressSpec(in, out, s); err != nil { if err := autoConvert_networking_IngressSpec_To_v1beta1_IngressSpec(in, out, s); err != nil {
return nil return err
} }
if in.DefaultBackend != nil { if in.DefaultBackend != nil {
out.Backend = &extensionsv1beta1.IngressBackend{} out.Backend = &extensionsv1beta1.IngressBackend{}
......
...@@ -52,7 +52,7 @@ func Convert_networking_IngressBackend_To_v1beta1_IngressBackend(in *networking. ...@@ -52,7 +52,7 @@ func Convert_networking_IngressBackend_To_v1beta1_IngressBackend(in *networking.
} }
func Convert_v1beta1_IngressSpec_To_networking_IngressSpec(in *v1beta1.IngressSpec, out *networking.IngressSpec, s conversion.Scope) error { func Convert_v1beta1_IngressSpec_To_networking_IngressSpec(in *v1beta1.IngressSpec, out *networking.IngressSpec, s conversion.Scope) error {
if err := autoConvert_v1beta1_IngressSpec_To_networking_IngressSpec(in, out, s); err != nil { if err := autoConvert_v1beta1_IngressSpec_To_networking_IngressSpec(in, out, s); err != nil {
return nil return err
} }
if in.Backend != nil { if in.Backend != nil {
out.DefaultBackend = &networking.IngressBackend{} out.DefaultBackend = &networking.IngressBackend{}
...@@ -65,7 +65,7 @@ func Convert_v1beta1_IngressSpec_To_networking_IngressSpec(in *v1beta1.IngressSp ...@@ -65,7 +65,7 @@ func Convert_v1beta1_IngressSpec_To_networking_IngressSpec(in *v1beta1.IngressSp
func Convert_networking_IngressSpec_To_v1beta1_IngressSpec(in *networking.IngressSpec, out *v1beta1.IngressSpec, s conversion.Scope) error { func Convert_networking_IngressSpec_To_v1beta1_IngressSpec(in *networking.IngressSpec, out *v1beta1.IngressSpec, s conversion.Scope) error {
if err := autoConvert_networking_IngressSpec_To_v1beta1_IngressSpec(in, out, s); err != nil { if err := autoConvert_networking_IngressSpec_To_v1beta1_IngressSpec(in, out, s); err != nil {
return nil return err
} }
if in.DefaultBackend != nil { if in.DefaultBackend != nil {
out.Backend = &v1beta1.IngressBackend{} out.Backend = &v1beta1.IngressBackend{}
......
...@@ -287,7 +287,11 @@ func (rq *Controller) Run(workers int, stopCh <-chan struct{}) { ...@@ -287,7 +287,11 @@ func (rq *Controller) Run(workers int, stopCh <-chan struct{}) {
go wait.Until(rq.worker(rq.missingUsageQueue), time.Second, stopCh) go wait.Until(rq.worker(rq.missingUsageQueue), time.Second, stopCh)
} }
// the timer for how often we do a full recalculation across all quotas // the timer for how often we do a full recalculation across all quotas
if rq.resyncPeriod() > 0 {
go wait.Until(func() { rq.enqueueAll() }, rq.resyncPeriod(), stopCh) go wait.Until(func() { rq.enqueueAll() }, rq.resyncPeriod(), stopCh)
} else {
klog.Warningf("periodic quota controller resync disabled")
}
<-stopCh <-stopCh
} }
......
...@@ -936,13 +936,13 @@ func isInitContainer(pod *v1.Pod, container *v1.Container) bool { ...@@ -936,13 +936,13 @@ func isInitContainer(pod *v1.Pod, container *v1.Container) bool {
func isNUMAAffinitiesEqual(numaAffinity1, numaAffinity2 []int) bool { func isNUMAAffinitiesEqual(numaAffinity1, numaAffinity2 []int) bool {
bitMask1, err := bitmask.NewBitMask(numaAffinity1...) bitMask1, err := bitmask.NewBitMask(numaAffinity1...)
if err != nil { if err != nil {
klog.ErrorS(err, "failed to create bit mask", numaAffinity1) klog.ErrorS(err, "failed to create bit mask", "numaAffinity1", numaAffinity1)
return false return false
} }
bitMask2, err := bitmask.NewBitMask(numaAffinity2...) bitMask2, err := bitmask.NewBitMask(numaAffinity2...)
if err != nil { if err != nil {
klog.ErrorS(err, "failed to create bit mask", numaAffinity2) klog.ErrorS(err, "failed to create bit mask", "numaAffinity2", numaAffinity2)
return false return false
} }
......
...@@ -2227,6 +2227,8 @@ func (kl *Kubelet) HandlePodAdditions(pods []*v1.Pod) { ...@@ -2227,6 +2227,8 @@ func (kl *Kubelet) HandlePodAdditions(pods []*v1.Pod) {
} }
mirrorPod, _ := kl.podManager.GetMirrorPodByPod(pod) mirrorPod, _ := kl.podManager.GetMirrorPodByPod(pod)
kl.dispatchWork(pod, kubetypes.SyncPodCreate, mirrorPod, start) kl.dispatchWork(pod, kubetypes.SyncPodCreate, mirrorPod, start)
// TODO: move inside syncPod and make reentrant
// https://github.com/kubernetes/kubernetes/issues/105014
kl.probeManager.AddPod(pod) kl.probeManager.AddPod(pod)
} }
} }
...@@ -2261,6 +2263,9 @@ func (kl *Kubelet) HandlePodRemoves(pods []*v1.Pod) { ...@@ -2261,6 +2263,9 @@ func (kl *Kubelet) HandlePodRemoves(pods []*v1.Pod) {
if err := kl.deletePod(pod); err != nil { if err := kl.deletePod(pod); err != nil {
klog.V(2).InfoS("Failed to delete pod", "pod", klog.KObj(pod), "err", err) klog.V(2).InfoS("Failed to delete pod", "pod", klog.KObj(pod), "err", err)
} }
// TODO: move inside syncTerminatingPod|syncTerminatedPod (we should stop probing
// once the pod kill is acknowledged and during eviction)
// https://github.com/kubernetes/kubernetes/issues/105014
kl.probeManager.RemovePod(pod) kl.probeManager.RemovePod(pod)
} }
} }
......
...@@ -978,7 +978,8 @@ func (kl *Kubelet) filterOutTerminatedPods(pods []*v1.Pod) []*v1.Pod { ...@@ -978,7 +978,8 @@ func (kl *Kubelet) filterOutTerminatedPods(pods []*v1.Pod) []*v1.Pod {
if kl.podWorkers.IsPodKnownTerminated(p.UID) { if kl.podWorkers.IsPodKnownTerminated(p.UID) {
continue continue
} }
if p.Status.Phase == v1.PodSucceeded || p.Status.Phase == v1.PodFailed { // terminal pods are considered inactive UNLESS they are actively terminating
if kl.isAdmittedPodTerminal(p) && !kl.podWorkers.IsPodTerminationRequested(p.UID) {
continue continue
} }
filteredPods = append(filteredPods, p) filteredPods = append(filteredPods, p)
...@@ -986,6 +987,28 @@ func (kl *Kubelet) filterOutTerminatedPods(pods []*v1.Pod) []*v1.Pod { ...@@ -986,6 +987,28 @@ func (kl *Kubelet) filterOutTerminatedPods(pods []*v1.Pod) []*v1.Pod {
return filteredPods return filteredPods
} }
// isAdmittedPodTerminal returns true if the provided config source pod is in
// a terminal phase, or if the Kubelet has already indicated the pod has reached
// a terminal phase but the config source has not accepted it yet. This method
// should only be used within the pod configuration loops that notify the pod
// worker, other components should treat the pod worker as authoritative.
func (kl *Kubelet) isAdmittedPodTerminal(pod *v1.Pod) bool {
// pods are considered inactive if the config source has observed a
// terminal phase (if the Kubelet recorded that the pod reached a terminal
// phase the pod should never be restarted)
if pod.Status.Phase == v1.PodSucceeded || pod.Status.Phase == v1.PodFailed {
return true
}
// a pod that has been marked terminal within the Kubelet is considered
// inactive (may have been rejected by Kubelet admision)
if status, ok := kl.statusManager.GetPodStatus(pod.UID); ok {
if status.Phase == v1.PodSucceeded || status.Phase == v1.PodFailed {
return true
}
}
return false
}
// removeOrphanedPodStatuses removes obsolete entries in podStatus where // removeOrphanedPodStatuses removes obsolete entries in podStatus where
// the pod is no longer considered bound to this node. // the pod is no longer considered bound to this node.
func (kl *Kubelet) removeOrphanedPodStatuses(pods []*v1.Pod, mirrorPods []*v1.Pod) { func (kl *Kubelet) removeOrphanedPodStatuses(pods []*v1.Pod, mirrorPods []*v1.Pod) {
...@@ -1067,13 +1090,16 @@ func (kl *Kubelet) HandlePodCleanups() error { ...@@ -1067,13 +1090,16 @@ func (kl *Kubelet) HandlePodCleanups() error {
// cleanup of pod cgroups. // cleanup of pod cgroups.
runningPods := make(map[types.UID]sets.Empty) runningPods := make(map[types.UID]sets.Empty)
possiblyRunningPods := make(map[types.UID]sets.Empty) possiblyRunningPods := make(map[types.UID]sets.Empty)
restartablePods := make(map[types.UID]sets.Empty)
for uid, sync := range workingPods { for uid, sync := range workingPods {
switch sync { switch sync {
case SyncPodWork: case SyncPod:
runningPods[uid] = struct{}{} runningPods[uid] = struct{}{}
possiblyRunningPods[uid] = struct{}{} possiblyRunningPods[uid] = struct{}{}
case TerminatingPodWork: case TerminatingPod:
possiblyRunningPods[uid] = struct{}{} possiblyRunningPods[uid] = struct{}{}
case TerminatedAndRecreatedPod:
restartablePods[uid] = struct{}{}
} }
} }
...@@ -1089,8 +1115,8 @@ func (kl *Kubelet) HandlePodCleanups() error { ...@@ -1089,8 +1115,8 @@ func (kl *Kubelet) HandlePodCleanups() error {
return err return err
} }
for _, runningPod := range runningRuntimePods { for _, runningPod := range runningRuntimePods {
switch workType, ok := workingPods[runningPod.ID]; { switch workerState, ok := workingPods[runningPod.ID]; {
case ok && workType == SyncPodWork, ok && workType == TerminatingPodWork: case ok && workerState == SyncPod, ok && workerState == TerminatingPod:
// if the pod worker is already in charge of this pod, we don't need to do anything // if the pod worker is already in charge of this pod, we don't need to do anything
continue continue
default: default:
...@@ -1157,6 +1183,32 @@ func (kl *Kubelet) HandlePodCleanups() error { ...@@ -1157,6 +1183,32 @@ func (kl *Kubelet) HandlePodCleanups() error {
} }
kl.backOff.GC() kl.backOff.GC()
// If two pods with the same UID are observed in rapid succession, we need to
// resynchronize the pod worker after the first pod completes and decide whether
// to restart the pod. This happens last to avoid confusing the desired state
// in other components and to increase the likelihood transient OS failures during
// container start are mitigated. In general only static pods will ever reuse UIDs
// since the apiserver uses randomly generated UUIDv4 UIDs with a very low
// probability of collision.
for uid := range restartablePods {
pod, ok := allPodsByUID[uid]
if !ok {
continue
}
if kl.isAdmittedPodTerminal(pod) {
klog.V(3).InfoS("Pod is restartable after termination due to UID reuse, but pod phase is terminal", "pod", klog.KObj(pod), "podUID", pod.UID)
continue
}
start := kl.clock.Now()
mirrorPod, _ := kl.podManager.GetMirrorPodByPod(pod)
klog.V(3).InfoS("Pod is restartable after termination due to UID reuse", "pod", klog.KObj(pod), "podUID", pod.UID)
kl.dispatchWork(pod, kubetypes.SyncPodCreate, mirrorPod, start)
// TODO: move inside syncPod and make reentrant
// https://github.com/kubernetes/kubernetes/issues/105014
kl.probeManager.AddPod(pod)
}
return nil return nil
} }
...@@ -1398,6 +1450,20 @@ func (kl *Kubelet) generateAPIPodStatus(pod *v1.Pod, podStatus *kubecontainer.Po ...@@ -1398,6 +1450,20 @@ func (kl *Kubelet) generateAPIPodStatus(pod *v1.Pod, podStatus *kubecontainer.Po
allStatus := append(append([]v1.ContainerStatus{}, s.ContainerStatuses...), s.InitContainerStatuses...) allStatus := append(append([]v1.ContainerStatus{}, s.ContainerStatuses...), s.InitContainerStatuses...)
s.Phase = getPhase(&pod.Spec, allStatus) s.Phase = getPhase(&pod.Spec, allStatus)
klog.V(4).InfoS("Got phase for pod", "pod", klog.KObj(pod), "oldPhase", oldPodStatus.Phase, "phase", s.Phase) klog.V(4).InfoS("Got phase for pod", "pod", klog.KObj(pod), "oldPhase", oldPodStatus.Phase, "phase", s.Phase)
// Perform a three-way merge between the statuses from the status manager,
// runtime, and generated status to ensure terminal status is correctly set.
if s.Phase != v1.PodFailed && s.Phase != v1.PodSucceeded {
switch {
case oldPodStatus.Phase == v1.PodFailed || oldPodStatus.Phase == v1.PodSucceeded:
klog.V(4).InfoS("Status manager phase was terminal, updating phase to match", "pod", klog.KObj(pod), "phase", oldPodStatus.Phase)
s.Phase = oldPodStatus.Phase
case pod.Status.Phase == v1.PodFailed || pod.Status.Phase == v1.PodSucceeded:
klog.V(4).InfoS("API phase was terminal, updating phase to match", "pod", klog.KObj(pod), "phase", pod.Status.Phase)
s.Phase = pod.Status.Phase
}
}
if s.Phase == oldPodStatus.Phase { if s.Phase == oldPodStatus.Phase {
// preserve the reason and message which is associated with the phase // preserve the reason and message which is associated with the phase
s.Reason = oldPodStatus.Reason s.Reason = oldPodStatus.Reason
......
...@@ -67,6 +67,11 @@ func newFakePodStateProvider() *fakePodStateProvider { ...@@ -67,6 +67,11 @@ func newFakePodStateProvider() *fakePodStateProvider {
} }
} }
func (f *fakePodStateProvider) IsPodTerminationRequested(uid types.UID) bool {
_, found := f.removed[uid]
return found
}
func (f *fakePodStateProvider) ShouldPodRuntimeBeRemoved(uid types.UID) bool { func (f *fakePodStateProvider) ShouldPodRuntimeBeRemoved(uid types.UID) bool {
_, found := f.terminated[uid] _, found := f.terminated[uid]
return found return found
......
...@@ -78,6 +78,7 @@ var ( ...@@ -78,6 +78,7 @@ var (
// podStateProvider can determine if none of the elements are necessary to retain (pod content) // podStateProvider can determine if none of the elements are necessary to retain (pod content)
// or if none of the runtime elements are necessary to retain (containers) // or if none of the runtime elements are necessary to retain (containers)
type podStateProvider interface { type podStateProvider interface {
IsPodTerminationRequested(kubetypes.UID) bool
ShouldPodContentBeRemoved(kubetypes.UID) bool ShouldPodContentBeRemoved(kubetypes.UID) bool
ShouldPodRuntimeBeRemoved(kubetypes.UID) bool ShouldPodRuntimeBeRemoved(kubetypes.UID) bool
} }
...@@ -805,12 +806,14 @@ func (m *kubeGenericRuntimeManager) SyncPod(pod *v1.Pod, podStatus *kubecontaine ...@@ -805,12 +806,14 @@ func (m *kubeGenericRuntimeManager) SyncPod(pod *v1.Pod, podStatus *kubecontaine
// or CRI if the Pod has been deleted while the POD is // or CRI if the Pod has been deleted while the POD is
// being created. If the pod has been deleted then it's // being created. If the pod has been deleted then it's
// not a real error. // not a real error.
// TODO: this is probably not needed now that termination is part of the sync loop //
if m.podStateProvider.ShouldPodContentBeRemoved(pod.UID) { // SyncPod can still be running when we get here, which
// means the PodWorker has not acked the deletion.
if m.podStateProvider.IsPodTerminationRequested(pod.UID) {
klog.V(4).InfoS("Pod was deleted and sandbox failed to be created", "pod", klog.KObj(pod), "podUID", pod.UID) klog.V(4).InfoS("Pod was deleted and sandbox failed to be created", "pod", klog.KObj(pod), "podUID", pod.UID)
return return
} }
metrics.StartedPodsErrorsTotal.WithLabelValues(err.Error()).Inc() metrics.StartedPodsErrorsTotal.Inc()
createSandboxResult.Fail(kubecontainer.ErrCreatePodSandbox, msg) createSandboxResult.Fail(kubecontainer.ErrCreatePodSandbox, msg)
klog.ErrorS(err, "CreatePodSandbox for pod failed", "pod", klog.KObj(pod)) klog.ErrorS(err, "CreatePodSandbox for pod failed", "pod", klog.KObj(pod))
ref, referr := ref.GetReference(legacyscheme.Scheme, pod) ref, referr := ref.GetReference(legacyscheme.Scheme, pod)
......
...@@ -460,14 +460,13 @@ var ( ...@@ -460,14 +460,13 @@ var (
}, },
) )
// StartedPodsErrorsTotal is a counter that tracks the number of errors creating pod sandboxes // StartedPodsErrorsTotal is a counter that tracks the number of errors creating pod sandboxes
StartedPodsErrorsTotal = metrics.NewCounterVec( StartedPodsErrorsTotal = metrics.NewCounter(
&metrics.CounterOpts{ &metrics.CounterOpts{
Subsystem: KubeletSubsystem, Subsystem: KubeletSubsystem,
Name: StartedPodsErrorsTotalKey, Name: StartedPodsErrorsTotalKey,
Help: "Cumulative number of errors when starting pods", Help: "Cumulative number of errors when starting pods",
StabilityLevel: metrics.ALPHA, StabilityLevel: metrics.ALPHA,
}, },
[]string{"message"},
) )
// StartedContainersTotal is a counter that tracks the number of container creation operations // StartedContainersTotal is a counter that tracks the number of container creation operations
StartedContainersTotal = metrics.NewCounterVec( StartedContainersTotal = metrics.NewCounterVec(
......
...@@ -28,6 +28,7 @@ import ( ...@@ -28,6 +28,7 @@ import (
"k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/tools/record" "k8s.io/client-go/tools/record"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
"k8s.io/klog/v2" "k8s.io/klog/v2"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/kubelet/events" "k8s.io/kubernetes/pkg/kubelet/events"
...@@ -91,7 +92,7 @@ type UpdatePodOptions struct { ...@@ -91,7 +92,7 @@ type UpdatePodOptions struct {
type PodWorkType int type PodWorkType int
const ( const (
// SyncPodSync is when the pod is expected to be started and running. // SyncPodWork is when the pod is expected to be started and running.
SyncPodWork PodWorkType = iota SyncPodWork PodWorkType = iota
// TerminatingPodWork is when the pod is no longer being set up, but some // TerminatingPodWork is when the pod is no longer being set up, but some
// containers may be running and are being torn down. // containers may be running and are being torn down.
...@@ -101,6 +102,26 @@ const ( ...@@ -101,6 +102,26 @@ const (
TerminatedPodWork TerminatedPodWork
) )
// PodWorkType classifies the status of pod as seen by the pod worker - setup (sync),
// teardown of containers (terminating), cleanup (terminated), or recreated with the
// same UID (kill -> create while terminating)
type PodWorkerState int
const (
// SyncPod is when the pod is expected to be started and running.
SyncPod PodWorkerState = iota
// TerminatingPod is when the pod is no longer being set up, but some
// containers may be running and are being torn down.
TerminatingPod
// TerminatedPod indicates the pod is stopped, can have no more running
// containers, and any foreground cleanup can be executed.
TerminatedPod
// TerminatedAndRecreatedPod indicates that after the pod was terminating a
// request to recreate the pod was received. The pod is terminated and can
// now be restarted by sending a create event to the pod worker.
TerminatedAndRecreatedPod
)
// podWork is the internal changes // podWork is the internal changes
type podWork struct { type podWork struct {
// WorkType is the type of sync to perform - sync (create), terminating (stop // WorkType is the type of sync to perform - sync (create), terminating (stop
...@@ -127,8 +148,8 @@ type PodWorkers interface { ...@@ -127,8 +148,8 @@ type PodWorkers interface {
// and have been terminated for a significant period of time. Once this method // and have been terminated for a significant period of time. Once this method
// has been called once, the workers are assumed to be fully initialized and // has been called once, the workers are assumed to be fully initialized and
// subsequent calls to ShouldPodContentBeRemoved on unknown pods will return // subsequent calls to ShouldPodContentBeRemoved on unknown pods will return
// true. // true. It returns a map describing the state of each known pod worker.
SyncKnownPods(desiredPods []*v1.Pod) map[types.UID]PodWorkType SyncKnownPods(desiredPods []*v1.Pod) map[types.UID]PodWorkerState
// IsPodKnownTerminated returns true if the provided pod UID is known by the pod // IsPodKnownTerminated returns true if the provided pod UID is known by the pod
// worker to be terminated. If the pod has been force deleted and the pod worker // worker to be terminated. If the pod has been force deleted and the pod worker
...@@ -254,6 +275,11 @@ type podSyncStatus struct { ...@@ -254,6 +275,11 @@ type podSyncStatus struct {
// to remove the pod. A terminal pod (Succeeded/Failed) will have // to remove the pod. A terminal pod (Succeeded/Failed) will have
// termination status until the pod is deleted. // termination status until the pod is deleted.
finished bool finished bool
// restartRequested is true if the pod worker was informed the pod is
// expected to exist (update type of create, update, or sync) after
// it has been killed. When known pods are synced, any pod that is
// terminated and has restartRequested will have its history cleared.
restartRequested bool
// notifyPostTerminating will be closed once the pod transitions to // notifyPostTerminating will be closed once the pod transitions to
// terminated. After the pod is in terminated state, nothing should be // terminated. After the pod is in terminated state, nothing should be
// added to this list. // added to this list.
...@@ -476,6 +502,22 @@ func (p *podWorkers) IsPodForMirrorPodTerminatingByFullName(podFullName string) ...@@ -476,6 +502,22 @@ func (p *podWorkers) IsPodForMirrorPodTerminatingByFullName(podFullName string)
return ok return ok
} }
func isPodStatusCacheTerminal(status *kubecontainer.PodStatus) bool {
runningContainers := 0
runningSandboxes := 0
for _, container := range status.ContainerStatuses {
if container.State == kubecontainer.ContainerStateRunning {
runningContainers++
}
}
for _, sb := range status.SandboxStatuses {
if sb.State == runtimeapi.PodSandboxState_SANDBOX_READY {
runningSandboxes++
}
}
return runningContainers == 0 && runningSandboxes == 0
}
// UpdatePod carries a configuration change or termination state to a pod. A pod is either runnable, // UpdatePod carries a configuration change or termination state to a pod. A pod is either runnable,
// terminating, or terminated, and will transition to terminating if deleted on the apiserver, it is // terminating, or terminated, and will transition to terminating if deleted on the apiserver, it is
// discovered to have a terminal phase (Succeeded or Failed), or if it is evicted by the kubelet. // discovered to have a terminal phase (Succeeded or Failed), or if it is evicted by the kubelet.
...@@ -511,9 +553,38 @@ func (p *podWorkers) UpdatePod(options UpdatePodOptions) { ...@@ -511,9 +553,38 @@ func (p *podWorkers) UpdatePod(options UpdatePodOptions) {
status = &podSyncStatus{ status = &podSyncStatus{
syncedAt: now, syncedAt: now,
} }
// if this pod is being synced for the first time, we need to make sure it is an active pod
if !isRuntimePod && (pod.Status.Phase == v1.PodFailed || pod.Status.Phase == v1.PodSucceeded) {
// check to see if the pod is not running and the pod is terminal.
// If this succeeds then record in the podWorker that it is terminated.
if statusCache, err := p.podCache.Get(pod.UID); err == nil {
if isPodStatusCacheTerminal(statusCache) {
status = &podSyncStatus{
terminatedAt: now,
terminatingAt: now,
syncedAt: now,
startedTerminating: true,
finished: true,
}
}
}
}
p.podSyncStatuses[uid] = status p.podSyncStatuses[uid] = status
} }
// if an update is received that implies the pod should be running, but we are already terminating a pod by
// that UID, assume that two pods with the same UID were created in close temporal proximity (usually static
// pod but it's possible for an apiserver to extremely rarely do something similar) - flag the sync status
// to indicate that after the pod terminates it should be reset to "not running" to allow a subsequent add/update
// to start the pod worker again
if status.IsTerminationRequested() {
if options.UpdateType == kubetypes.SyncPodCreate {
status.restartRequested = true
klog.V(4).InfoS("Pod is terminating but has been requested to restart with same UID, will be reconciled later", "pod", klog.KObj(pod), "podUID", pod.UID)
return
}
}
// once a pod is terminated by UID, it cannot reenter the pod worker (until the UID is purged by housekeeping) // once a pod is terminated by UID, it cannot reenter the pod worker (until the UID is purged by housekeeping)
if status.IsFinished() { if status.IsFinished() {
klog.V(4).InfoS("Pod is finished processing, no further updates", "pod", klog.KObj(pod), "podUID", pod.UID) klog.V(4).InfoS("Pod is finished processing, no further updates", "pod", klog.KObj(pod), "podUID", pod.UID)
...@@ -965,8 +1036,8 @@ func (p *podWorkers) contextForWorker(uid types.UID) context.Context { ...@@ -965,8 +1036,8 @@ func (p *podWorkers) contextForWorker(uid types.UID) context.Context {
// to UpdatePods for new pods. It returns a map of known workers that are not finished // to UpdatePods for new pods. It returns a map of known workers that are not finished
// with a value of SyncPodTerminated, SyncPodKill, or SyncPodSync depending on whether // with a value of SyncPodTerminated, SyncPodKill, or SyncPodSync depending on whether
// the pod is terminated, terminating, or syncing. // the pod is terminated, terminating, or syncing.
func (p *podWorkers) SyncKnownPods(desiredPods []*v1.Pod) map[types.UID]PodWorkType { func (p *podWorkers) SyncKnownPods(desiredPods []*v1.Pod) map[types.UID]PodWorkerState {
workers := make(map[types.UID]PodWorkType) workers := make(map[types.UID]PodWorkerState)
known := make(map[types.UID]struct{}) known := make(map[types.UID]struct{})
for _, pod := range desiredPods { for _, pod := range desiredPods {
known[pod.UID] = struct{}{} known[pod.UID] = struct{}{}
...@@ -977,16 +1048,20 @@ func (p *podWorkers) SyncKnownPods(desiredPods []*v1.Pod) map[types.UID]PodWorkT ...@@ -977,16 +1048,20 @@ func (p *podWorkers) SyncKnownPods(desiredPods []*v1.Pod) map[types.UID]PodWorkT
p.podsSynced = true p.podsSynced = true
for uid, status := range p.podSyncStatuses { for uid, status := range p.podSyncStatuses {
if _, exists := known[uid]; !exists { if _, exists := known[uid]; !exists || status.restartRequested {
p.removeTerminatedWorker(uid) p.removeTerminatedWorker(uid)
} }
switch { switch {
case !status.terminatedAt.IsZero(): case !status.terminatedAt.IsZero():
workers[uid] = TerminatedPodWork if status.restartRequested {
workers[uid] = TerminatedAndRecreatedPod
} else {
workers[uid] = TerminatedPod
}
case !status.terminatingAt.IsZero(): case !status.terminatingAt.IsZero():
workers[uid] = TerminatingPodWork workers[uid] = TerminatingPod
default: default:
workers[uid] = SyncPodWork workers[uid] = SyncPod
} }
} }
return workers return workers
...@@ -1009,7 +1084,11 @@ func (p *podWorkers) removeTerminatedWorker(uid types.UID) { ...@@ -1009,7 +1084,11 @@ func (p *podWorkers) removeTerminatedWorker(uid types.UID) {
return return
} }
if status.restartRequested {
klog.V(4).InfoS("Pod has been terminated but another pod with the same UID was created, remove history to allow restart", "podUID", uid)
} else {
klog.V(4).InfoS("Pod has been terminated and is no longer known to the kubelet, remove all history", "podUID", uid) klog.V(4).InfoS("Pod has been terminated and is no longer known to the kubelet, remove all history", "podUID", uid)
}
delete(p.podSyncStatuses, uid) delete(p.podSyncStatuses, uid)
delete(p.podUpdates, uid) delete(p.podUpdates, uid)
delete(p.lastUndeliveredWorkUpdate, uid) delete(p.lastUndeliveredWorkUpdate, uid)
......
...@@ -333,7 +333,7 @@ func (m *manager) TerminatePod(pod *v1.Pod) { ...@@ -333,7 +333,7 @@ func (m *manager) TerminatePod(pod *v1.Pod) {
} }
status := *oldStatus.DeepCopy() status := *oldStatus.DeepCopy()
for i := range status.ContainerStatuses { for i := range status.ContainerStatuses {
if status.ContainerStatuses[i].State.Terminated != nil || status.ContainerStatuses[i].State.Waiting != nil { if status.ContainerStatuses[i].State.Terminated != nil {
continue continue
} }
status.ContainerStatuses[i].State = v1.ContainerState{ status.ContainerStatuses[i].State = v1.ContainerState{
...@@ -345,7 +345,7 @@ func (m *manager) TerminatePod(pod *v1.Pod) { ...@@ -345,7 +345,7 @@ func (m *manager) TerminatePod(pod *v1.Pod) {
} }
} }
for i := range status.InitContainerStatuses { for i := range status.InitContainerStatuses {
if status.InitContainerStatuses[i].State.Terminated != nil || status.InitContainerStatuses[i].State.Waiting != nil { if status.InitContainerStatuses[i].State.Terminated != nil {
continue continue
} }
status.InitContainerStatuses[i].State = v1.ContainerState{ status.InitContainerStatuses[i].State = v1.ContainerState{
......
...@@ -1041,6 +1041,9 @@ func (proxier *Proxier) syncProxyRules() { ...@@ -1041,6 +1041,9 @@ func (proxier *Proxier) syncProxyRules() {
for _, extIP := range svcInfo.ExternalIPStrings() { for _, extIP := range svcInfo.ExternalIPStrings() {
staleServices.Insert(extIP) staleServices.Insert(extIP)
} }
for _, extIP := range svcInfo.LoadBalancerIPStrings() {
staleServices.Insert(extIP)
}
} }
} }
......
...@@ -759,6 +759,12 @@ func (rs *REST) handleClusterIPsForUpdatedService(oldService *api.Service, servi ...@@ -759,6 +759,12 @@ func (rs *REST) handleClusterIPsForUpdatedService(oldService *api.Service, servi
} }
// CASE B: // CASE B:
// if headless service then we bail out early (no clusterIPs management needed)
if len(oldService.Spec.ClusterIPs) > 0 && oldService.Spec.ClusterIPs[0] == api.ClusterIPNone {
return nil, nil, nil
}
// Update service from non-ExternalName to ExternalName, should release ClusterIP if exists. // Update service from non-ExternalName to ExternalName, should release ClusterIP if exists.
if oldService.Spec.Type != api.ServiceTypeExternalName && service.Spec.Type == api.ServiceTypeExternalName { if oldService.Spec.Type != api.ServiceTypeExternalName && service.Spec.Type == api.ServiceTypeExternalName {
toRelease = make(map[api.IPFamily]string) toRelease = make(map[api.IPFamily]string)
...@@ -775,11 +781,6 @@ func (rs *REST) handleClusterIPsForUpdatedService(oldService *api.Service, servi ...@@ -775,11 +781,6 @@ func (rs *REST) handleClusterIPsForUpdatedService(oldService *api.Service, servi
return nil, toRelease, nil return nil, toRelease, nil
} }
// if headless service then we bail out early (no clusterIPs management needed)
if len(oldService.Spec.ClusterIPs) > 0 && oldService.Spec.ClusterIPs[0] == api.ClusterIPNone {
return nil, nil, nil
}
// upgrade and downgrade are specific to dualstack // upgrade and downgrade are specific to dualstack
if !utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) { if !utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) {
return nil, nil, nil return nil, nil, nil
......
...@@ -453,5 +453,8 @@ func getValidCreationData(subscriptionID, resourceGroup, sourceResourceID, sourc ...@@ -453,5 +453,8 @@ func getValidCreationData(subscriptionID, resourceGroup, sourceResourceID, sourc
func isInstanceNotFoundError(err error) bool { func isInstanceNotFoundError(err error) bool {
errMsg := strings.ToLower(err.Error()) errMsg := strings.ToLower(err.Error())
if strings.Contains(errMsg, strings.ToLower(vmssVMNotActiveErrorMessage)) {
return true
}
return strings.Contains(errMsg, errStatusCode400) && strings.Contains(errMsg, errInvalidParameter) && strings.Contains(errMsg, errTargetInstanceIds) return strings.Contains(errMsg, errStatusCode400) && strings.Contains(errMsg, errInvalidParameter) && strings.Contains(errMsg, errTargetInstanceIds)
} }
...@@ -20,6 +20,7 @@ package azure ...@@ -20,6 +20,7 @@ package azure
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"math" "math"
"reflect" "reflect"
...@@ -304,7 +305,7 @@ func (az *Cloud) cleanBackendpoolForPrimarySLB(primarySLB *network.LoadBalancer, ...@@ -304,7 +305,7 @@ func (az *Cloud) cleanBackendpoolForPrimarySLB(primarySLB *network.LoadBalancer,
ipConf := (*bp.BackendIPConfigurations)[i] ipConf := (*bp.BackendIPConfigurations)[i]
ipConfigID := to.String(ipConf.ID) ipConfigID := to.String(ipConf.ID)
_, vmSetName, err := az.VMSet.GetNodeNameByIPConfigurationID(ipConfigID) _, vmSetName, err := az.VMSet.GetNodeNameByIPConfigurationID(ipConfigID)
if err != nil { if err != nil && !errors.Is(err, cloudprovider.InstanceNotFound) {
return nil, err return nil, err
} }
primaryVMSetName := az.VMSet.GetPrimaryVMSetName() primaryVMSetName := az.VMSet.GetPrimaryVMSetName()
...@@ -1129,13 +1130,10 @@ func (az *Cloud) reconcileLoadBalancer(clusterName string, service *v1.Service, ...@@ -1129,13 +1130,10 @@ func (az *Cloud) reconcileLoadBalancer(clusterName string, service *v1.Service,
for _, ipConf := range *bp.BackendIPConfigurations { for _, ipConf := range *bp.BackendIPConfigurations {
ipConfID := to.String(ipConf.ID) ipConfID := to.String(ipConf.ID)
nodeName, _, err := az.VMSet.GetNodeNameByIPConfigurationID(ipConfID) nodeName, _, err := az.VMSet.GetNodeNameByIPConfigurationID(ipConfID)
if err != nil { if err != nil && !errors.Is(err, cloudprovider.InstanceNotFound) {
return nil, err return nil, err
} }
if nodeName == "" {
// VM may under deletion
continue
}
// If a node is not supposed to be included in the LB, it // If a node is not supposed to be included in the LB, it
// would not be in the `nodes` slice. We need to check the nodes that // would not be in the `nodes` slice. We need to check the nodes that
// have been added to the LB's backendpool, find the unwanted ones and // have been added to the LB's backendpool, find the unwanted ones and
......
...@@ -458,6 +458,7 @@ func (as *availabilitySet) GetInstanceIDByNodeName(name string) (string, error) ...@@ -458,6 +458,7 @@ func (as *availabilitySet) GetInstanceIDByNodeName(name string) (string, error)
machine, err = as.getVirtualMachine(types.NodeName(name), azcache.CacheReadTypeUnsafe) machine, err = as.getVirtualMachine(types.NodeName(name), azcache.CacheReadTypeUnsafe)
if err == cloudprovider.InstanceNotFound { if err == cloudprovider.InstanceNotFound {
klog.Warningf("Unable to find node %s: %v", name, cloudprovider.InstanceNotFound)
return "", cloudprovider.InstanceNotFound return "", cloudprovider.InstanceNotFound
} }
if err != nil { if err != nil {
...@@ -965,13 +966,16 @@ func (as *availabilitySet) EnsureBackendPoolDeleted(service *v1.Service, backend ...@@ -965,13 +966,16 @@ func (as *availabilitySet) EnsureBackendPoolDeleted(service *v1.Service, backend
} }
} }
nicUpdaters := make([]func() error, 0) nicUpdaters := make([]func() error, 0)
errors := make([]error, 0) allErrs := make([]error, 0)
for i := range ipConfigurationIDs { for i := range ipConfigurationIDs {
ipConfigurationID := ipConfigurationIDs[i] ipConfigurationID := ipConfigurationIDs[i]
nodeName, _, err := as.GetNodeNameByIPConfigurationID(ipConfigurationID) nodeName, _, err := as.GetNodeNameByIPConfigurationID(ipConfigurationID)
if err != nil { if err != nil && !errors.Is(err, cloudprovider.InstanceNotFound) {
klog.Errorf("Failed to GetNodeNameByIPConfigurationID(%s): %v", ipConfigurationID, err) klog.Errorf("Failed to GetNodeNameByIPConfigurationID(%s): %v", ipConfigurationID, err)
errors = append(errors, err) allErrs = append(allErrs, err)
continue
}
if nodeName == "" {
continue continue
} }
...@@ -1038,9 +1042,9 @@ func (as *availabilitySet) EnsureBackendPoolDeleted(service *v1.Service, backend ...@@ -1038,9 +1042,9 @@ func (as *availabilitySet) EnsureBackendPoolDeleted(service *v1.Service, backend
if errs != nil { if errs != nil {
return utilerrors.Flatten(errs) return utilerrors.Flatten(errs)
} }
// Fail if there are other errors. // Fail if there are other allErrs.
if len(errors) > 0 { if len(allErrs) > 0 {
return utilerrors.Flatten(utilerrors.NewAggregate(errors)) return utilerrors.Flatten(utilerrors.NewAggregate(allErrs))
} }
isOperationSucceeded = true isOperationSucceeded = true
...@@ -1104,7 +1108,8 @@ func (as *availabilitySet) GetNodeNameByIPConfigurationID(ipConfigurationID stri ...@@ -1104,7 +1108,8 @@ func (as *availabilitySet) GetNodeNameByIPConfigurationID(ipConfigurationID stri
vm, err := as.getVirtualMachine(types.NodeName(vmName), azcache.CacheReadTypeDefault) vm, err := as.getVirtualMachine(types.NodeName(vmName), azcache.CacheReadTypeDefault)
if err != nil { if err != nil {
return "", "", fmt.Errorf("cannot get the virtual machine by node name %s", vmName) klog.Errorf("Unable to get the virtual machine by node name %s: %v", vmName, err)
return "", "", err
} }
asID := "" asID := ""
if vm.VirtualMachineProperties != nil && vm.AvailabilitySet != nil { if vm.VirtualMachineProperties != nil && vm.AvailabilitySet != nil {
...@@ -1116,7 +1121,7 @@ func (as *availabilitySet) GetNodeNameByIPConfigurationID(ipConfigurationID stri ...@@ -1116,7 +1121,7 @@ func (as *availabilitySet) GetNodeNameByIPConfigurationID(ipConfigurationID stri
asName, err := getAvailabilitySetNameByID(asID) asName, err := getAvailabilitySetNameByID(asID)
if err != nil { if err != nil {
return "", "", fmt.Errorf("cannot get the availability set name by the availability set ID %s", asID) return "", "", fmt.Errorf("cannot get the availability set name by the availability set ID %s: %v", asID, err)
} }
return vmName, strings.ToLower(asName), nil return vmName, strings.ToLower(asName), nil
} }
...@@ -195,6 +195,7 @@ func (ss *scaleSet) getVmssVMByNodeIdentity(node *nodeIdentity, crt azcache.Azur ...@@ -195,6 +195,7 @@ func (ss *scaleSet) getVmssVMByNodeIdentity(node *nodeIdentity, crt azcache.Azur
} }
if !found || vm == nil { if !found || vm == nil {
klog.Warningf("Unable to find node %s: %v", node.nodeName, cloudprovider.InstanceNotFound)
return "", "", nil, cloudprovider.InstanceNotFound return "", "", nil, cloudprovider.InstanceNotFound
} }
return vmssName, instanceID, vm, nil return vmssName, instanceID, vm, nil
...@@ -342,6 +343,7 @@ func (ss *scaleSet) GetInstanceIDByNodeName(name string) (string, error) { ...@@ -342,6 +343,7 @@ func (ss *scaleSet) GetInstanceIDByNodeName(name string) (string, error) {
_, _, vm, err := ss.getVmssVM(name, azcache.CacheReadTypeUnsafe) _, _, vm, err := ss.getVmssVM(name, azcache.CacheReadTypeUnsafe)
if err != nil { if err != nil {
klog.Errorf("Unable to find node %s: %v", name, err)
return "", err return "", err
} }
...@@ -389,6 +391,7 @@ func (ss *scaleSet) GetNodeNameByProviderID(providerID string) (types.NodeName, ...@@ -389,6 +391,7 @@ func (ss *scaleSet) GetNodeNameByProviderID(providerID string) (types.NodeName,
vm, err := ss.getVmssVMByInstanceID(resourceGroup, scaleSetName, instanceID, azcache.CacheReadTypeUnsafe) vm, err := ss.getVmssVMByInstanceID(resourceGroup, scaleSetName, instanceID, azcache.CacheReadTypeUnsafe)
if err != nil { if err != nil {
klog.Errorf("Unable to find node by providerID %s: %v", providerID, err)
return "", err return "", err
} }
...@@ -709,6 +712,7 @@ func (ss *scaleSet) getNodeIdentityByNodeName(nodeName string, crt azcache.Azure ...@@ -709,6 +712,7 @@ func (ss *scaleSet) getNodeIdentityByNodeName(nodeName string, crt azcache.Azure
return nil, err return nil, err
} }
if node.vmssName == "" { if node.vmssName == "" {
klog.Warningf("Unable to find node %s: %v", nodeName, cloudprovider.InstanceNotFound)
return nil, cloudprovider.InstanceNotFound return nil, cloudprovider.InstanceNotFound
} }
return node, nil return node, nil
...@@ -721,7 +725,7 @@ func (ss *scaleSet) listScaleSetVMs(scaleSetName, resourceGroup string) ([]compu ...@@ -721,7 +725,7 @@ func (ss *scaleSet) listScaleSetVMs(scaleSetName, resourceGroup string) ([]compu
allVMs, rerr := ss.VirtualMachineScaleSetVMsClient.List(ctx, resourceGroup, scaleSetName, string(compute.InstanceView)) allVMs, rerr := ss.VirtualMachineScaleSetVMsClient.List(ctx, resourceGroup, scaleSetName, string(compute.InstanceView))
if rerr != nil { if rerr != nil {
klog.Errorf("VirtualMachineScaleSetVMsClient.List failed: %v", rerr) klog.Errorf("VirtualMachineScaleSetVMsClient.List(%s, %s) failed: %v", resourceGroup, scaleSetName, rerr)
if rerr.IsNotFound() { if rerr.IsNotFound() {
return nil, cloudprovider.InstanceNotFound return nil, cloudprovider.InstanceNotFound
} }
...@@ -960,6 +964,11 @@ func (ss *scaleSet) EnsureHostInPool(service *v1.Service, nodeName types.NodeNam ...@@ -960,6 +964,11 @@ func (ss *scaleSet) EnsureHostInPool(service *v1.Service, nodeName types.NodeNam
vmName := mapNodeNameToVMName(nodeName) vmName := mapNodeNameToVMName(nodeName)
ssName, instanceID, vm, err := ss.getVmssVM(vmName, azcache.CacheReadTypeDefault) ssName, instanceID, vm, err := ss.getVmssVM(vmName, azcache.CacheReadTypeDefault)
if err != nil { if err != nil {
if errors.Is(err, cloudprovider.InstanceNotFound) {
klog.Infof("EnsureHostInPool: skipping node %s because it is not found", vmName)
return "", "", "", nil, nil
}
return "", "", "", nil, err return "", "", "", nil, err
} }
...@@ -1335,6 +1344,11 @@ func (ss *scaleSet) EnsureHostsInPool(service *v1.Service, nodes []*v1.Node, bac ...@@ -1335,6 +1344,11 @@ func (ss *scaleSet) EnsureHostsInPool(service *v1.Service, nodes []*v1.Node, bac
func (ss *scaleSet) ensureBackendPoolDeletedFromNode(nodeName, backendPoolID string) (string, string, string, *compute.VirtualMachineScaleSetVM, error) { func (ss *scaleSet) ensureBackendPoolDeletedFromNode(nodeName, backendPoolID string) (string, string, string, *compute.VirtualMachineScaleSetVM, error) {
ssName, instanceID, vm, err := ss.getVmssVM(nodeName, azcache.CacheReadTypeDefault) ssName, instanceID, vm, err := ss.getVmssVM(nodeName, azcache.CacheReadTypeDefault)
if err != nil { if err != nil {
if errors.Is(err, cloudprovider.InstanceNotFound) {
klog.Infof("ensureBackendPoolDeletedFromNode: skipping node %s because it is not found", nodeName)
return "", "", "", nil, nil
}
return "", "", "", nil, err return "", "", "", nil, err
} }
...@@ -1407,7 +1421,7 @@ func (ss *scaleSet) GetNodeNameByIPConfigurationID(ipConfigurationID string) (st ...@@ -1407,7 +1421,7 @@ func (ss *scaleSet) GetNodeNameByIPConfigurationID(ipConfigurationID string) (st
klog.V(4).Infof("Can not extract scale set name from ipConfigurationID (%s), assuming it is managed by availability set", ipConfigurationID) klog.V(4).Infof("Can not extract scale set name from ipConfigurationID (%s), assuming it is managed by availability set", ipConfigurationID)
name, rg, err := ss.availabilitySet.GetNodeNameByIPConfigurationID(ipConfigurationID) name, rg, err := ss.availabilitySet.GetNodeNameByIPConfigurationID(ipConfigurationID)
if err != nil { if err != nil && !errors.Is(err, cloudprovider.InstanceNotFound) {
klog.Errorf("GetNodeNameByIPConfigurationID: failed to invoke availabilitySet.GetNodeNameByIPConfigurationID: %s", err.Error()) klog.Errorf("GetNodeNameByIPConfigurationID: failed to invoke availabilitySet.GetNodeNameByIPConfigurationID: %s", err.Error())
return "", "", err return "", "", err
} }
...@@ -1419,6 +1433,7 @@ func (ss *scaleSet) GetNodeNameByIPConfigurationID(ipConfigurationID string) (st ...@@ -1419,6 +1433,7 @@ func (ss *scaleSet) GetNodeNameByIPConfigurationID(ipConfigurationID string) (st
instanceID := matches[3] instanceID := matches[3]
vm, err := ss.getVmssVMByInstanceID(resourceGroup, scaleSetName, instanceID, azcache.CacheReadTypeUnsafe) vm, err := ss.getVmssVMByInstanceID(resourceGroup, scaleSetName, instanceID, azcache.CacheReadTypeUnsafe)
if err != nil { if err != nil {
klog.Errorf("Unable to find node by ipConfigurationID %s: %v", ipConfigurationID, err)
return "", "", err return "", "", err
} }
...@@ -1580,7 +1595,7 @@ func (ss *scaleSet) EnsureBackendPoolDeleted(service *v1.Service, backendPoolID, ...@@ -1580,7 +1595,7 @@ func (ss *scaleSet) EnsureBackendPoolDeleted(service *v1.Service, backendPoolID,
hostUpdates := make([]func() error, 0, len(ipConfigurationIDs)) hostUpdates := make([]func() error, 0, len(ipConfigurationIDs))
nodeUpdates := make(map[vmssMetaInfo]map[string]compute.VirtualMachineScaleSetVM) nodeUpdates := make(map[vmssMetaInfo]map[string]compute.VirtualMachineScaleSetVM)
errors := make([]error, 0) allErrs := make([]error, 0)
for i := range ipConfigurationIDs { for i := range ipConfigurationIDs {
ipConfigurationID := ipConfigurationIDs[i] ipConfigurationID := ipConfigurationIDs[i]
...@@ -1600,14 +1615,16 @@ func (ss *scaleSet) EnsureBackendPoolDeleted(service *v1.Service, backendPoolID, ...@@ -1600,14 +1615,16 @@ func (ss *scaleSet) EnsureBackendPoolDeleted(service *v1.Service, backendPoolID,
} }
klog.Errorf("Failed to GetNodeNameByIPConfigurationID(%s): %v", ipConfigurationID, err) klog.Errorf("Failed to GetNodeNameByIPConfigurationID(%s): %v", ipConfigurationID, err)
errors = append(errors, err) allErrs = append(allErrs, err)
continue continue
} }
nodeResourceGroup, nodeVMSS, nodeInstanceID, nodeVMSSVM, err := ss.ensureBackendPoolDeletedFromNode(nodeName, backendPoolID) nodeResourceGroup, nodeVMSS, nodeInstanceID, nodeVMSSVM, err := ss.ensureBackendPoolDeletedFromNode(nodeName, backendPoolID)
if err != nil { if err != nil {
if !errors.Is(err, ErrorNotVmssInstance) { // Do nothing for the VMAS nodes.
klog.Errorf("EnsureBackendPoolDeleted(%s): backendPoolID(%s) - failed with error %v", getServiceName(service), backendPoolID, err) klog.Errorf("EnsureBackendPoolDeleted(%s): backendPoolID(%s) - failed with error %v", getServiceName(service), backendPoolID, err)
errors = append(errors, err) allErrs = append(allErrs, err)
}
continue continue
} }
...@@ -1653,8 +1670,8 @@ func (ss *scaleSet) EnsureBackendPoolDeleted(service *v1.Service, backendPoolID, ...@@ -1653,8 +1670,8 @@ func (ss *scaleSet) EnsureBackendPoolDeleted(service *v1.Service, backendPoolID,
} }
// Fail if there are other errors. // Fail if there are other errors.
if len(errors) > 0 { if len(allErrs) > 0 {
return utilerrors.Flatten(utilerrors.NewAggregate(errors)) return utilerrors.Flatten(utilerrors.NewAggregate(allErrs))
} }
// Ensure the backendPoolID is also deleted on VMSS itself. // Ensure the backendPoolID is also deleted on VMSS itself.
......
...@@ -76,7 +76,7 @@ func (err *Error) Error() error { ...@@ -76,7 +76,7 @@ func (err *Error) Error() error {
retryAfterSeconds = int(err.RetryAfter.Sub(curTime) / time.Second) retryAfterSeconds = int(err.RetryAfter.Sub(curTime) / time.Second)
} }
return fmt.Errorf("Retriable: %v, RetryAfter: %ds, HTTPStatusCode: %d, RawError: %v", return fmt.Errorf("Retriable: %v, RetryAfter: %ds, HTTPStatusCode: %d, RawError: %w",
err.Retriable, retryAfterSeconds, err.HTTPStatusCode, err.RawError) err.Retriable, retryAfterSeconds, err.HTTPStatusCode, err.RawError)
} }
......
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