mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 18:53:35 +00:00
rebase: update K8s packages to v0.32.1
Update K8s packages in go.mod to v0.32.1 Signed-off-by: Praveen M <m.praveen@ibm.com>
This commit is contained in:
37
vendor/k8s.io/kubernetes/pkg/kubelet/lifecycle/admission_failure_handler_stub.go
generated
vendored
Normal file
37
vendor/k8s.io/kubernetes/pkg/kubelet/lifecycle/admission_failure_handler_stub.go
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package lifecycle
|
||||
|
||||
import (
|
||||
"k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// AdmissionFailureHandlerStub is an AdmissionFailureHandler that does not perform any handling of admission failure.
|
||||
// It simply passes the failure on.
|
||||
type AdmissionFailureHandlerStub struct{}
|
||||
|
||||
var _ AdmissionFailureHandler = &AdmissionFailureHandlerStub{}
|
||||
|
||||
// NewAdmissionFailureHandlerStub returns an instance of AdmissionFailureHandlerStub.
|
||||
func NewAdmissionFailureHandlerStub() *AdmissionFailureHandlerStub {
|
||||
return &AdmissionFailureHandlerStub{}
|
||||
}
|
||||
|
||||
// HandleAdmissionFailure simply passes admission rejection on, with no special handling.
|
||||
func (n *AdmissionFailureHandlerStub) HandleAdmissionFailure(admitPod *v1.Pod, failureReasons []PredicateFailureReason) ([]PredicateFailureReason, error) {
|
||||
return failureReasons, nil
|
||||
}
|
19
vendor/k8s.io/kubernetes/pkg/kubelet/lifecycle/doc.go
generated
vendored
Normal file
19
vendor/k8s.io/kubernetes/pkg/kubelet/lifecycle/doc.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
Copyright 2015 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 lifecycle contains handlers for pod lifecycle events and interfaces
|
||||
// to integrate with kubelet admission, synchronization, and eviction of pods.
|
||||
package lifecycle // import "k8s.io/kubernetes/pkg/kubelet/lifecycle"
|
243
vendor/k8s.io/kubernetes/pkg/kubelet/lifecycle/handlers.go
generated
vendored
Normal file
243
vendor/k8s.io/kubernetes/pkg/kubelet/lifecycle/handlers.go
generated
vendored
Normal file
@ -0,0 +1,243 @@
|
||||
/*
|
||||
Copyright 2014 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 lifecycle
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/metrics"
|
||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||
"k8s.io/kubernetes/pkg/kubelet/util/format"
|
||||
httpprobe "k8s.io/kubernetes/pkg/probe/http"
|
||||
"k8s.io/kubernetes/pkg/security/apparmor"
|
||||
)
|
||||
|
||||
const (
|
||||
maxRespBodyLength = 10 * 1 << 10 // 10KB
|
||||
|
||||
AppArmorNotAdmittedReason = "AppArmor"
|
||||
)
|
||||
|
||||
type handlerRunner struct {
|
||||
httpDoer kubetypes.HTTPDoer
|
||||
commandRunner kubecontainer.CommandRunner
|
||||
containerManager podStatusProvider
|
||||
eventRecorder record.EventRecorder
|
||||
}
|
||||
|
||||
type podStatusProvider interface {
|
||||
GetPodStatus(ctx context.Context, uid types.UID, name, namespace string) (*kubecontainer.PodStatus, error)
|
||||
}
|
||||
|
||||
// NewHandlerRunner returns a configured lifecycle handler for a container.
|
||||
func NewHandlerRunner(httpDoer kubetypes.HTTPDoer, commandRunner kubecontainer.CommandRunner, containerManager podStatusProvider, eventRecorder record.EventRecorder) kubecontainer.HandlerRunner {
|
||||
return &handlerRunner{
|
||||
httpDoer: httpDoer,
|
||||
commandRunner: commandRunner,
|
||||
containerManager: containerManager,
|
||||
eventRecorder: eventRecorder,
|
||||
}
|
||||
}
|
||||
|
||||
func (hr *handlerRunner) Run(ctx context.Context, containerID kubecontainer.ContainerID, pod *v1.Pod, container *v1.Container, handler *v1.LifecycleHandler) (string, error) {
|
||||
switch {
|
||||
case handler.Exec != nil:
|
||||
var msg string
|
||||
// TODO(tallclair): Pass a proper timeout value.
|
||||
output, err := hr.commandRunner.RunInContainer(ctx, containerID, handler.Exec.Command, 0)
|
||||
if err != nil {
|
||||
msg = fmt.Sprintf("Exec lifecycle hook (%v) for Container %q in Pod %q failed - error: %v, message: %q", handler.Exec.Command, container.Name, format.Pod(pod), err, string(output))
|
||||
klog.V(1).ErrorS(err, "Exec lifecycle hook for Container in Pod failed", "execCommand", handler.Exec.Command, "containerName", container.Name, "pod", klog.KObj(pod), "message", string(output))
|
||||
}
|
||||
return msg, err
|
||||
case handler.HTTPGet != nil:
|
||||
err := hr.runHTTPHandler(ctx, pod, container, handler, hr.eventRecorder)
|
||||
var msg string
|
||||
if err != nil {
|
||||
msg = fmt.Sprintf("HTTP lifecycle hook (%s) for Container %q in Pod %q failed - error: %v", handler.HTTPGet.Path, container.Name, format.Pod(pod), err)
|
||||
klog.V(1).ErrorS(err, "HTTP lifecycle hook for Container in Pod failed", "path", handler.HTTPGet.Path, "containerName", container.Name, "pod", klog.KObj(pod))
|
||||
}
|
||||
return msg, err
|
||||
case handler.Sleep != nil:
|
||||
err := hr.runSleepHandler(ctx, handler.Sleep.Seconds)
|
||||
var msg string
|
||||
if err != nil {
|
||||
msg = fmt.Sprintf("Sleep lifecycle hook (%d) for Container %q in Pod %q failed - error: %v", handler.Sleep.Seconds, container.Name, format.Pod(pod), err)
|
||||
klog.V(1).ErrorS(err, "Sleep lifecycle hook for Container in Pod failed", "sleepSeconds", handler.Sleep.Seconds, "containerName", container.Name, "pod", klog.KObj(pod))
|
||||
}
|
||||
return msg, err
|
||||
default:
|
||||
err := fmt.Errorf("invalid handler: %v", handler)
|
||||
msg := fmt.Sprintf("Cannot run handler: %v", err)
|
||||
klog.ErrorS(err, "Cannot run handler")
|
||||
return msg, err
|
||||
}
|
||||
}
|
||||
|
||||
// resolvePort attempts to turn an IntOrString port reference into a concrete port number.
|
||||
// If portReference has an int value, it is treated as a literal, and simply returns that value.
|
||||
// If portReference is a string, an attempt is first made to parse it as an integer. If that fails,
|
||||
// an attempt is made to find a port with the same name in the container spec.
|
||||
// If a port with the same name is found, it's ContainerPort value is returned. If no matching
|
||||
// port is found, an error is returned.
|
||||
func resolvePort(portReference intstr.IntOrString, container *v1.Container) (int, error) {
|
||||
if portReference.Type == intstr.Int {
|
||||
return portReference.IntValue(), nil
|
||||
}
|
||||
portName := portReference.StrVal
|
||||
port, err := strconv.Atoi(portName)
|
||||
if err == nil {
|
||||
return port, nil
|
||||
}
|
||||
for _, portSpec := range container.Ports {
|
||||
if portSpec.Name == portName {
|
||||
return int(portSpec.ContainerPort), nil
|
||||
}
|
||||
}
|
||||
return -1, fmt.Errorf("couldn't find port: %v in %v", portReference, container)
|
||||
}
|
||||
|
||||
func (hr *handlerRunner) runSleepHandler(ctx context.Context, seconds int64) error {
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.PodLifecycleSleepAction) {
|
||||
return nil
|
||||
}
|
||||
c := time.After(time.Duration(seconds) * time.Second)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// unexpected termination
|
||||
metrics.LifecycleHandlerSleepTerminated.Inc()
|
||||
return fmt.Errorf("container terminated before sleep hook finished")
|
||||
case <-c:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (hr *handlerRunner) runHTTPHandler(ctx context.Context, pod *v1.Pod, container *v1.Container, handler *v1.LifecycleHandler, eventRecorder record.EventRecorder) error {
|
||||
host := handler.HTTPGet.Host
|
||||
podIP := host
|
||||
if len(host) == 0 {
|
||||
status, err := hr.containerManager.GetPodStatus(ctx, pod.UID, pod.Name, pod.Namespace)
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "Unable to get pod info, event handlers may be invalid.", "pod", klog.KObj(pod))
|
||||
return err
|
||||
}
|
||||
if len(status.IPs) == 0 {
|
||||
return fmt.Errorf("failed to find networking container: %v", status)
|
||||
}
|
||||
host = status.IPs[0]
|
||||
podIP = host
|
||||
}
|
||||
|
||||
req, err := httpprobe.NewRequestForHTTPGetAction(handler.HTTPGet, container, podIP, "lifecycle")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := hr.httpDoer.Do(req)
|
||||
discardHTTPRespBody(resp)
|
||||
|
||||
if isHTTPResponseError(err) {
|
||||
klog.V(1).ErrorS(err, "HTTPS request to lifecycle hook got HTTP response, retrying with HTTP.", "pod", klog.KObj(pod), "host", req.URL.Host)
|
||||
|
||||
req := req.Clone(context.Background())
|
||||
req.URL.Scheme = "http"
|
||||
req.Header.Del("Authorization")
|
||||
resp, httpErr := hr.httpDoer.Do(req)
|
||||
|
||||
// clear err since the fallback succeeded
|
||||
if httpErr == nil {
|
||||
metrics.LifecycleHandlerHTTPFallbacks.Inc()
|
||||
if eventRecorder != nil {
|
||||
// report the fallback with an event
|
||||
eventRecorder.Event(pod, v1.EventTypeWarning, "LifecycleHTTPFallback", fmt.Sprintf("request to HTTPS lifecycle hook %s got HTTP response, retry with HTTP succeeded", req.URL.Host))
|
||||
}
|
||||
err = nil
|
||||
}
|
||||
discardHTTPRespBody(resp)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func discardHTTPRespBody(resp *http.Response) {
|
||||
if resp == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure the response body is fully read and closed
|
||||
// before we reconnect, so that we reuse the same TCP
|
||||
// connection.
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.ContentLength <= maxRespBodyLength {
|
||||
io.Copy(io.Discard, &io.LimitedReader{R: resp.Body, N: maxRespBodyLength})
|
||||
}
|
||||
}
|
||||
|
||||
// NewAppArmorAdmitHandler returns a PodAdmitHandler which is used to evaluate
|
||||
// if a pod can be admitted from the perspective of AppArmor.
|
||||
func NewAppArmorAdmitHandler(validator apparmor.Validator) PodAdmitHandler {
|
||||
return &appArmorAdmitHandler{
|
||||
Validator: validator,
|
||||
}
|
||||
}
|
||||
|
||||
type appArmorAdmitHandler struct {
|
||||
apparmor.Validator
|
||||
}
|
||||
|
||||
func (a *appArmorAdmitHandler) Admit(attrs *PodAdmitAttributes) PodAdmitResult {
|
||||
// If the pod is already running or terminated, no need to recheck AppArmor.
|
||||
if attrs.Pod.Status.Phase != v1.PodPending {
|
||||
return PodAdmitResult{Admit: true}
|
||||
}
|
||||
|
||||
err := a.Validate(attrs.Pod)
|
||||
if err == nil {
|
||||
return PodAdmitResult{Admit: true}
|
||||
}
|
||||
return PodAdmitResult{
|
||||
Admit: false,
|
||||
Reason: AppArmorNotAdmittedReason,
|
||||
Message: fmt.Sprintf("Cannot enforce AppArmor: %v", err),
|
||||
}
|
||||
}
|
||||
|
||||
func isHTTPResponseError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
urlErr := &url.Error{}
|
||||
if !errors.As(err, &urlErr) {
|
||||
return false
|
||||
}
|
||||
return strings.Contains(urlErr.Err.Error(), "server gave HTTP response to HTTPS client")
|
||||
}
|
122
vendor/k8s.io/kubernetes/pkg/kubelet/lifecycle/interfaces.go
generated
vendored
Normal file
122
vendor/k8s.io/kubernetes/pkg/kubelet/lifecycle/interfaces.go
generated
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
Copyright 2016 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 lifecycle
|
||||
|
||||
import "k8s.io/api/core/v1"
|
||||
|
||||
// PodAdmitAttributes is the context for a pod admission decision.
|
||||
// The member fields of this struct should never be mutated.
|
||||
type PodAdmitAttributes struct {
|
||||
// the pod to evaluate for admission
|
||||
Pod *v1.Pod
|
||||
// all pods bound to the kubelet excluding the pod being evaluated
|
||||
OtherPods []*v1.Pod
|
||||
}
|
||||
|
||||
// PodAdmitResult provides the result of a pod admission decision.
|
||||
type PodAdmitResult struct {
|
||||
// if true, the pod should be admitted.
|
||||
Admit bool
|
||||
// a brief single-word reason why the pod could not be admitted.
|
||||
Reason string
|
||||
// a brief message explaining why the pod could not be admitted.
|
||||
Message string
|
||||
}
|
||||
|
||||
// PodAdmitHandler is notified during pod admission.
|
||||
type PodAdmitHandler interface {
|
||||
// Admit evaluates if a pod can be admitted.
|
||||
Admit(attrs *PodAdmitAttributes) PodAdmitResult
|
||||
}
|
||||
|
||||
// PodAdmitTarget maintains a list of handlers to invoke.
|
||||
type PodAdmitTarget interface {
|
||||
// AddPodAdmitHandler adds the specified handler.
|
||||
AddPodAdmitHandler(a PodAdmitHandler)
|
||||
}
|
||||
|
||||
// PodSyncLoopHandler is invoked during each sync loop iteration.
|
||||
type PodSyncLoopHandler interface {
|
||||
// ShouldSync returns true if the pod needs to be synced.
|
||||
// This operation must return immediately as its called for each pod.
|
||||
// The provided pod should never be modified.
|
||||
ShouldSync(pod *v1.Pod) bool
|
||||
}
|
||||
|
||||
// PodSyncLoopTarget maintains a list of handlers to pod sync loop.
|
||||
type PodSyncLoopTarget interface {
|
||||
// AddPodSyncLoopHandler adds the specified handler.
|
||||
AddPodSyncLoopHandler(a PodSyncLoopHandler)
|
||||
}
|
||||
|
||||
// ShouldEvictResponse provides the result of a should evict request.
|
||||
type ShouldEvictResponse struct {
|
||||
// if true, the pod should be evicted.
|
||||
Evict bool
|
||||
// a brief CamelCase reason why the pod should be evicted.
|
||||
Reason string
|
||||
// a brief message why the pod should be evicted.
|
||||
Message string
|
||||
}
|
||||
|
||||
// PodSyncHandler is invoked during each sync pod operation.
|
||||
type PodSyncHandler interface {
|
||||
// ShouldEvict is invoked during each sync pod operation to determine
|
||||
// if the pod should be evicted from the kubelet. If so, the pod status
|
||||
// is updated to mark its phase as failed with the provided reason and message,
|
||||
// and the pod is immediately killed.
|
||||
// This operation must return immediately as its called for each sync pod.
|
||||
// The provided pod should never be modified.
|
||||
ShouldEvict(pod *v1.Pod) ShouldEvictResponse
|
||||
}
|
||||
|
||||
// PodSyncTarget maintains a list of handlers to pod sync.
|
||||
type PodSyncTarget interface {
|
||||
// AddPodSyncHandler adds the specified handler
|
||||
AddPodSyncHandler(a PodSyncHandler)
|
||||
}
|
||||
|
||||
// PodLifecycleTarget groups a set of lifecycle interfaces for convenience.
|
||||
type PodLifecycleTarget interface {
|
||||
PodAdmitTarget
|
||||
PodSyncLoopTarget
|
||||
PodSyncTarget
|
||||
}
|
||||
|
||||
// PodAdmitHandlers maintains a list of handlers to pod admission.
|
||||
type PodAdmitHandlers []PodAdmitHandler
|
||||
|
||||
// AddPodAdmitHandler adds the specified observer.
|
||||
func (handlers *PodAdmitHandlers) AddPodAdmitHandler(a PodAdmitHandler) {
|
||||
*handlers = append(*handlers, a)
|
||||
}
|
||||
|
||||
// PodSyncLoopHandlers maintains a list of handlers to pod sync loop.
|
||||
type PodSyncLoopHandlers []PodSyncLoopHandler
|
||||
|
||||
// AddPodSyncLoopHandler adds the specified observer.
|
||||
func (handlers *PodSyncLoopHandlers) AddPodSyncLoopHandler(a PodSyncLoopHandler) {
|
||||
*handlers = append(*handlers, a)
|
||||
}
|
||||
|
||||
// PodSyncHandlers maintains a list of handlers to pod sync.
|
||||
type PodSyncHandlers []PodSyncHandler
|
||||
|
||||
// AddPodSyncHandler adds the specified handler.
|
||||
func (handlers *PodSyncHandlers) AddPodSyncHandler(a PodSyncHandler) {
|
||||
*handlers = append(*handlers, a)
|
||||
}
|
372
vendor/k8s.io/kubernetes/pkg/kubelet/lifecycle/predicate.go
generated
vendored
Normal file
372
vendor/k8s.io/kubernetes/pkg/kubelet/lifecycle/predicate.go
generated
vendored
Normal file
@ -0,0 +1,372 @@
|
||||
/*
|
||||
Copyright 2016 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 lifecycle
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/component-helpers/scheduling/corev1"
|
||||
"k8s.io/klog/v2"
|
||||
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
||||
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/kubelet/types"
|
||||
"k8s.io/kubernetes/pkg/scheduler"
|
||||
schedulerframework "k8s.io/kubernetes/pkg/scheduler/framework"
|
||||
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/tainttoleration"
|
||||
)
|
||||
|
||||
const (
|
||||
// PodOSSelectorNodeLabelDoesNotMatch is used to denote that the pod was
|
||||
// rejected admission to the node because the pod's node selector
|
||||
// corresponding to kubernetes.io/os label didn't match the node label.
|
||||
PodOSSelectorNodeLabelDoesNotMatch = "PodOSSelectorNodeLabelDoesNotMatch"
|
||||
|
||||
// PodOSNotSupported is used to denote that the pod was rejected admission
|
||||
// to the node because the pod's OS field didn't match the node OS.
|
||||
PodOSNotSupported = "PodOSNotSupported"
|
||||
|
||||
// InvalidNodeInfo is used to denote that the pod was rejected admission
|
||||
// to the node because the kubelet was unable to retrieve the node info.
|
||||
InvalidNodeInfo = "InvalidNodeInfo"
|
||||
|
||||
// InitContainerRestartPolicyForbidden is used to denote that the pod was
|
||||
// rejected admission to the node because it uses a restart policy other
|
||||
// than Always for some of its init containers.
|
||||
InitContainerRestartPolicyForbidden = "InitContainerRestartPolicyForbidden"
|
||||
|
||||
// UnexpectedAdmissionError is used to denote that the pod was rejected
|
||||
// admission to the node because of an error during admission that could not
|
||||
// be categorized.
|
||||
UnexpectedAdmissionError = "UnexpectedAdmissionError"
|
||||
|
||||
// UnknownReason is used to denote that the pod was rejected admission to
|
||||
// the node because a predicate failed for a reason that could not be
|
||||
// determined.
|
||||
UnknownReason = "UnknownReason"
|
||||
|
||||
// UnexpectedPredicateFailureType is used to denote that the pod was
|
||||
// rejected admission to the node because a predicate returned a reason
|
||||
// object that was not an InsufficientResourceError or a PredicateFailureError.
|
||||
UnexpectedPredicateFailureType = "UnexpectedPredicateFailureType"
|
||||
|
||||
// Prefix for admission reason when kubelet rejects a pod due to insufficient
|
||||
// resources available.
|
||||
InsufficientResourcePrefix = "OutOf"
|
||||
|
||||
// These reasons are used to denote that the pod has reject admission
|
||||
// to the node because there's not enough resources to run the pod.
|
||||
OutOfCPU = "OutOfcpu"
|
||||
OutOfMemory = "OutOfmemory"
|
||||
OutOfEphemeralStorage = "OutOfephemeral-storage"
|
||||
OutOfPods = "OutOfpods"
|
||||
)
|
||||
|
||||
type getNodeAnyWayFuncType func() (*v1.Node, error)
|
||||
|
||||
type pluginResourceUpdateFuncType func(*schedulerframework.NodeInfo, *PodAdmitAttributes) error
|
||||
|
||||
// AdmissionFailureHandler is an interface which defines how to deal with a failure to admit a pod.
|
||||
// This allows for the graceful handling of pod admission failure.
|
||||
type AdmissionFailureHandler interface {
|
||||
HandleAdmissionFailure(admitPod *v1.Pod, failureReasons []PredicateFailureReason) ([]PredicateFailureReason, error)
|
||||
}
|
||||
|
||||
type predicateAdmitHandler struct {
|
||||
getNodeAnyWayFunc getNodeAnyWayFuncType
|
||||
pluginResourceUpdateFunc pluginResourceUpdateFuncType
|
||||
admissionFailureHandler AdmissionFailureHandler
|
||||
}
|
||||
|
||||
var _ PodAdmitHandler = &predicateAdmitHandler{}
|
||||
|
||||
// NewPredicateAdmitHandler returns a PodAdmitHandler which is used to evaluates
|
||||
// if a pod can be admitted from the perspective of predicates.
|
||||
func NewPredicateAdmitHandler(getNodeAnyWayFunc getNodeAnyWayFuncType, admissionFailureHandler AdmissionFailureHandler, pluginResourceUpdateFunc pluginResourceUpdateFuncType) PodAdmitHandler {
|
||||
return &predicateAdmitHandler{
|
||||
getNodeAnyWayFunc,
|
||||
pluginResourceUpdateFunc,
|
||||
admissionFailureHandler,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *predicateAdmitHandler) Admit(attrs *PodAdmitAttributes) PodAdmitResult {
|
||||
node, err := w.getNodeAnyWayFunc()
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "Cannot get Node info")
|
||||
return PodAdmitResult{
|
||||
Admit: false,
|
||||
Reason: InvalidNodeInfo,
|
||||
Message: "Kubelet cannot get node info.",
|
||||
}
|
||||
}
|
||||
admitPod := attrs.Pod
|
||||
|
||||
// perform the checks that preemption will not help first to avoid meaningless pod eviction
|
||||
if rejectPodAdmissionBasedOnOSSelector(admitPod, node) {
|
||||
return PodAdmitResult{
|
||||
Admit: false,
|
||||
Reason: PodOSSelectorNodeLabelDoesNotMatch,
|
||||
Message: "Failed to admit pod as the `kubernetes.io/os` label doesn't match node label",
|
||||
}
|
||||
}
|
||||
if rejectPodAdmissionBasedOnOSField(admitPod) {
|
||||
return PodAdmitResult{
|
||||
Admit: false,
|
||||
Reason: PodOSNotSupported,
|
||||
Message: "Failed to admit pod as the OS field doesn't match node OS",
|
||||
}
|
||||
}
|
||||
|
||||
pods := attrs.OtherPods
|
||||
nodeInfo := schedulerframework.NewNodeInfo(pods...)
|
||||
nodeInfo.SetNode(node)
|
||||
|
||||
// TODO: Remove this after the SidecarContainers feature gate graduates to GA.
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.SidecarContainers) {
|
||||
for _, c := range admitPod.Spec.InitContainers {
|
||||
if podutil.IsRestartableInitContainer(&c) {
|
||||
message := fmt.Sprintf("Init container %q may not have a non-default restartPolicy", c.Name)
|
||||
klog.InfoS("Failed to admit pod", "pod", klog.KObj(admitPod), "message", message)
|
||||
return PodAdmitResult{
|
||||
Admit: false,
|
||||
Reason: InitContainerRestartPolicyForbidden,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ensure the node has enough plugin resources for that required in pods
|
||||
if err = w.pluginResourceUpdateFunc(nodeInfo, attrs); err != nil {
|
||||
message := fmt.Sprintf("Update plugin resources failed due to %v, which is unexpected.", err)
|
||||
klog.InfoS("Failed to admit pod", "pod", klog.KObj(admitPod), "message", message)
|
||||
return PodAdmitResult{
|
||||
Admit: false,
|
||||
Reason: UnexpectedAdmissionError,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the requests of the extended resources that are missing in the
|
||||
// node info. This is required to support cluster-level resources, which
|
||||
// are extended resources unknown to nodes.
|
||||
//
|
||||
// Caveat: If a pod was manually bound to a node (e.g., static pod) where a
|
||||
// node-level extended resource it requires is not found, then kubelet will
|
||||
// not fail admission while it should. This issue will be addressed with
|
||||
// the Resource Class API in the future.
|
||||
podWithoutMissingExtendedResources := removeMissingExtendedResources(admitPod, nodeInfo)
|
||||
|
||||
reasons := generalFilter(podWithoutMissingExtendedResources, nodeInfo)
|
||||
fit := len(reasons) == 0
|
||||
if !fit {
|
||||
reasons, err = w.admissionFailureHandler.HandleAdmissionFailure(admitPod, reasons)
|
||||
fit = len(reasons) == 0 && err == nil
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("Unexpected error while attempting to recover from admission failure: %v", err)
|
||||
klog.InfoS("Failed to admit pod, unexpected error while attempting to recover from admission failure", "pod", klog.KObj(admitPod), "err", err)
|
||||
return PodAdmitResult{
|
||||
Admit: fit,
|
||||
Reason: UnexpectedAdmissionError,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
}
|
||||
if !fit {
|
||||
var reason string
|
||||
var message string
|
||||
if len(reasons) == 0 {
|
||||
message = fmt.Sprint("GeneralPredicates failed due to unknown reason, which is unexpected.")
|
||||
klog.InfoS("Failed to admit pod: GeneralPredicates failed due to unknown reason, which is unexpected", "pod", klog.KObj(admitPod))
|
||||
return PodAdmitResult{
|
||||
Admit: fit,
|
||||
Reason: UnknownReason,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
// If there are failed predicates, we only return the first one as a reason.
|
||||
r := reasons[0]
|
||||
switch re := r.(type) {
|
||||
case *PredicateFailureError:
|
||||
reason = re.PredicateName
|
||||
message = re.Error()
|
||||
klog.V(2).InfoS("Predicate failed on Pod", "pod", klog.KObj(admitPod), "err", message)
|
||||
case *InsufficientResourceError:
|
||||
switch re.ResourceName {
|
||||
case v1.ResourceCPU:
|
||||
reason = OutOfCPU
|
||||
case v1.ResourceMemory:
|
||||
reason = OutOfMemory
|
||||
case v1.ResourceEphemeralStorage:
|
||||
reason = OutOfEphemeralStorage
|
||||
case v1.ResourcePods:
|
||||
reason = OutOfPods
|
||||
default:
|
||||
reason = fmt.Sprintf("%s%s", InsufficientResourcePrefix, re.ResourceName)
|
||||
}
|
||||
message = re.Error()
|
||||
klog.V(2).InfoS("Predicate failed on Pod", "pod", klog.KObj(admitPod), "err", message)
|
||||
default:
|
||||
reason = UnexpectedPredicateFailureType
|
||||
message = fmt.Sprintf("GeneralPredicates failed due to %v, which is unexpected.", r)
|
||||
klog.InfoS("Failed to admit pod", "pod", klog.KObj(admitPod), "err", message)
|
||||
}
|
||||
return PodAdmitResult{
|
||||
Admit: fit,
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
return PodAdmitResult{
|
||||
Admit: true,
|
||||
}
|
||||
}
|
||||
|
||||
// rejectPodAdmissionBasedOnOSSelector rejects pod if it's nodeSelector doesn't match
|
||||
// We expect the kubelet status reconcile which happens every 10sec to update the node labels if there is a mismatch.
|
||||
func rejectPodAdmissionBasedOnOSSelector(pod *v1.Pod, node *v1.Node) bool {
|
||||
labels := node.Labels
|
||||
osName, osLabelExists := labels[v1.LabelOSStable]
|
||||
if !osLabelExists || osName != runtime.GOOS {
|
||||
if len(labels) == 0 {
|
||||
labels = make(map[string]string)
|
||||
}
|
||||
labels[v1.LabelOSStable] = runtime.GOOS
|
||||
}
|
||||
podLabelSelector, podOSLabelExists := pod.Labels[v1.LabelOSStable]
|
||||
if !podOSLabelExists {
|
||||
// If the labelselector didn't exist, let's keep the current behavior as is
|
||||
return false
|
||||
} else if podOSLabelExists && podLabelSelector != labels[v1.LabelOSStable] {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// rejectPodAdmissionBasedOnOSField rejects pods if their OS field doesn't match runtime.GOOS.
|
||||
// TODO: Relax this restriction when we start supporting LCOW in kubernetes where podOS may not match
|
||||
// node's OS.
|
||||
func rejectPodAdmissionBasedOnOSField(pod *v1.Pod) bool {
|
||||
if pod.Spec.OS == nil {
|
||||
return false
|
||||
}
|
||||
// If the pod OS doesn't match runtime.GOOS return false
|
||||
return string(pod.Spec.OS.Name) != runtime.GOOS
|
||||
}
|
||||
|
||||
func removeMissingExtendedResources(pod *v1.Pod, nodeInfo *schedulerframework.NodeInfo) *v1.Pod {
|
||||
filterExtendedResources := func(containers []v1.Container) {
|
||||
for i, c := range containers {
|
||||
// We only handle requests in Requests but not Limits because the
|
||||
// PodFitsResources predicate, to which the result pod will be passed,
|
||||
// does not use Limits.
|
||||
filteredResources := make(v1.ResourceList)
|
||||
for rName, rQuant := range c.Resources.Requests {
|
||||
if v1helper.IsExtendedResourceName(rName) {
|
||||
if _, found := nodeInfo.Allocatable.ScalarResources[rName]; !found {
|
||||
continue
|
||||
}
|
||||
}
|
||||
filteredResources[rName] = rQuant
|
||||
}
|
||||
containers[i].Resources.Requests = filteredResources
|
||||
}
|
||||
}
|
||||
podCopy := pod.DeepCopy()
|
||||
filterExtendedResources(podCopy.Spec.Containers)
|
||||
filterExtendedResources(podCopy.Spec.InitContainers)
|
||||
return podCopy
|
||||
}
|
||||
|
||||
// InsufficientResourceError is an error type that indicates what kind of resource limit is
|
||||
// hit and caused the unfitting failure.
|
||||
type InsufficientResourceError struct {
|
||||
ResourceName v1.ResourceName
|
||||
Requested int64
|
||||
Used int64
|
||||
Capacity int64
|
||||
}
|
||||
|
||||
func (e *InsufficientResourceError) Error() string {
|
||||
return fmt.Sprintf("Node didn't have enough resource: %s, requested: %d, used: %d, capacity: %d",
|
||||
e.ResourceName, e.Requested, e.Used, e.Capacity)
|
||||
}
|
||||
|
||||
// PredicateFailureReason interface represents the failure reason of a predicate.
|
||||
type PredicateFailureReason interface {
|
||||
GetReason() string
|
||||
}
|
||||
|
||||
// GetReason returns the reason of the InsufficientResourceError.
|
||||
func (e *InsufficientResourceError) GetReason() string {
|
||||
return fmt.Sprintf("Insufficient %v", e.ResourceName)
|
||||
}
|
||||
|
||||
// GetInsufficientAmount returns the amount of the insufficient resource of the error.
|
||||
func (e *InsufficientResourceError) GetInsufficientAmount() int64 {
|
||||
return e.Requested - (e.Capacity - e.Used)
|
||||
}
|
||||
|
||||
// PredicateFailureError describes a failure error of predicate.
|
||||
type PredicateFailureError struct {
|
||||
PredicateName string
|
||||
PredicateDesc string
|
||||
}
|
||||
|
||||
func (e *PredicateFailureError) Error() string {
|
||||
return fmt.Sprintf("Predicate %s failed: %s", e.PredicateName, e.PredicateDesc)
|
||||
}
|
||||
|
||||
// GetReason returns the reason of the PredicateFailureError.
|
||||
func (e *PredicateFailureError) GetReason() string {
|
||||
return e.PredicateDesc
|
||||
}
|
||||
|
||||
// generalFilter checks a group of filterings that the kubelet cares about.
|
||||
func generalFilter(pod *v1.Pod, nodeInfo *schedulerframework.NodeInfo) []PredicateFailureReason {
|
||||
admissionResults := scheduler.AdmissionCheck(pod, nodeInfo, true)
|
||||
var reasons []PredicateFailureReason
|
||||
for _, r := range admissionResults {
|
||||
if r.InsufficientResource != nil {
|
||||
reasons = append(reasons, &InsufficientResourceError{
|
||||
ResourceName: r.InsufficientResource.ResourceName,
|
||||
Requested: r.InsufficientResource.Requested,
|
||||
Used: r.InsufficientResource.Used,
|
||||
Capacity: r.InsufficientResource.Capacity,
|
||||
})
|
||||
} else {
|
||||
reasons = append(reasons, &PredicateFailureError{r.Name, r.Reason})
|
||||
}
|
||||
}
|
||||
|
||||
// Check taint/toleration except for static pods
|
||||
if !types.IsStaticPod(pod) {
|
||||
_, isUntolerated := corev1.FindMatchingUntoleratedTaint(nodeInfo.Node().Spec.Taints, pod.Spec.Tolerations, func(t *v1.Taint) bool {
|
||||
// Kubelet is only interested in the NoExecute taint.
|
||||
return t.Effect == v1.TaintEffectNoExecute
|
||||
})
|
||||
if isUntolerated {
|
||||
reasons = append(reasons, &PredicateFailureError{tainttoleration.Name, tainttoleration.ErrReasonNotMatch})
|
||||
}
|
||||
}
|
||||
|
||||
return reasons
|
||||
}
|
Reference in New Issue
Block a user