2015-11-06 06:34:42 -05:00
/ *
2016-06-02 20:25:58 -04:00
Copyright 2015 The Kubernetes Authors .
2015-11-06 06:34:42 -05:00
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 pod
2017-04-10 18:16:45 -04:00
import (
2019-01-07 15:10:11 -05:00
"strings"
2023-03-23 14:19:41 -04:00
"github.com/google/go-cmp/cmp"
2020-07-17 14:56:52 -04:00
v1 "k8s.io/api/core/v1"
2017-04-10 18:16:45 -04:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2022-08-11 09:21:06 -04:00
metavalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
2017-08-18 16:18:53 -04:00
utilfeature "k8s.io/apiserver/pkg/util/feature"
2017-11-08 17:34:54 -05:00
api "k8s.io/kubernetes/pkg/apis/core"
2020-11-06 14:22:53 -05:00
"k8s.io/kubernetes/pkg/apis/core/helper"
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
2017-08-18 16:18:53 -04:00
"k8s.io/kubernetes/pkg/features"
2017-04-10 18:16:45 -04:00
)
2017-02-23 00:35:44 -05:00
2020-03-05 14:48:00 -05:00
// ContainerType signifies container type
type ContainerType int
const (
// Containers is for normal containers
Containers ContainerType = 1 << iota
// InitContainers is for init containers
InitContainers
// EphemeralContainers is for ephemeral containers
EphemeralContainers
)
// AllContainers specifies that all containers be visited
const AllContainers ContainerType = ( InitContainers | Containers | EphemeralContainers )
2020-03-20 13:21:24 -04:00
// AllFeatureEnabledContainers returns a ContainerType mask which includes all container
// types except for the ones guarded by feature gate.
func AllFeatureEnabledContainers ( ) ContainerType {
2022-07-24 10:36:42 -04:00
return AllContainers
2020-03-20 13:21:24 -04:00
}
2019-06-14 11:20:16 -04:00
// ContainerVisitor is called with each container spec, and returns true
// if visiting should continue.
2020-03-05 14:48:00 -05:00
type ContainerVisitor func ( container * api . Container , containerType ContainerType ) ( shouldContinue bool )
2019-06-14 11:20:16 -04:00
2020-03-20 13:21:24 -04:00
// VisitContainers invokes the visitor function with a pointer to every container
// spec in the given pod spec with type set in mask. If visitor returns false,
2019-06-14 11:20:16 -04:00
// visiting is short-circuited. VisitContainers returns true if visiting completes,
// false if visiting was short-circuited.
2020-03-05 14:48:00 -05:00
func VisitContainers ( podSpec * api . PodSpec , mask ContainerType , visitor ContainerVisitor ) bool {
2020-03-20 13:21:24 -04:00
if mask & InitContainers != 0 {
2020-03-05 14:48:00 -05:00
for i := range podSpec . InitContainers {
if ! visitor ( & podSpec . InitContainers [ i ] , InitContainers ) {
return false
}
2019-06-14 11:20:16 -04:00
}
}
2020-03-20 13:21:24 -04:00
if mask & Containers != 0 {
2020-03-05 14:48:00 -05:00
for i := range podSpec . Containers {
if ! visitor ( & podSpec . Containers [ i ] , Containers ) {
return false
}
2019-06-14 11:20:16 -04:00
}
}
2020-03-20 13:21:24 -04:00
if mask & EphemeralContainers != 0 {
2018-08-09 09:24:23 -04:00
for i := range podSpec . EphemeralContainers {
2020-03-05 14:48:00 -05:00
if ! visitor ( ( * api . Container ) ( & podSpec . EphemeralContainers [ i ] . EphemeralContainerCommon ) , EphemeralContainers ) {
2018-08-09 09:24:23 -04:00
return false
}
}
}
2019-06-14 11:20:16 -04:00
return true
}
2017-05-05 02:14:38 -04:00
// Visitor is called with each object name, and returns true if visiting should continue
type Visitor func ( name string ) ( shouldContinue bool )
2021-02-27 14:09:57 -05:00
func skipEmptyNames ( visitor Visitor ) Visitor {
return func ( name string ) bool {
if len ( name ) == 0 {
// continue visiting
return true
}
// delegate to visitor
return visitor ( name )
}
}
2017-02-23 00:35:44 -05:00
// VisitPodSecretNames invokes the visitor function with the name of every secret
// referenced by the pod spec. If visitor returns false, visiting is short-circuited.
// Transitive references (e.g. pod -> pvc -> pv -> secret) are not visited.
// Returns true if visiting completed, false if visiting was short-circuited.
2020-03-20 10:59:44 -04:00
func VisitPodSecretNames ( pod * api . Pod , visitor Visitor , containerType ContainerType ) bool {
2021-02-27 14:09:57 -05:00
visitor = skipEmptyNames ( visitor )
2017-02-23 00:35:44 -05:00
for _ , reference := range pod . Spec . ImagePullSecrets {
if ! visitor ( reference . Name ) {
return false
}
}
2020-03-20 10:59:44 -04:00
VisitContainers ( & pod . Spec , containerType , func ( c * api . Container , containerType ContainerType ) bool {
2019-06-14 11:20:16 -04:00
return visitContainerSecretNames ( c , visitor )
} )
2017-02-23 00:35:44 -05:00
var source * api . VolumeSource
for i := range pod . Spec . Volumes {
source = & pod . Spec . Volumes [ i ] . VolumeSource
switch {
case source . AzureFile != nil :
2017-02-27 01:00:58 -05:00
if len ( source . AzureFile . SecretName ) > 0 && ! visitor ( source . AzureFile . SecretName ) {
2017-02-23 00:35:44 -05:00
return false
}
case source . CephFS != nil :
if source . CephFS . SecretRef != nil && ! visitor ( source . CephFS . SecretRef . Name ) {
return false
}
2018-05-14 17:09:05 -04:00
case source . Cinder != nil :
if source . Cinder . SecretRef != nil && ! visitor ( source . Cinder . SecretRef . Name ) {
return false
}
2017-02-23 00:35:44 -05:00
case source . FlexVolume != nil :
if source . FlexVolume . SecretRef != nil && ! visitor ( source . FlexVolume . SecretRef . Name ) {
return false
}
case source . Projected != nil :
for j := range source . Projected . Sources {
if source . Projected . Sources [ j ] . Secret != nil {
if ! visitor ( source . Projected . Sources [ j ] . Secret . Name ) {
return false
}
}
}
case source . RBD != nil :
if source . RBD . SecretRef != nil && ! visitor ( source . RBD . SecretRef . Name ) {
return false
}
case source . Secret != nil :
if ! visitor ( source . Secret . SecretName ) {
return false
}
2016-11-19 15:46:23 -05:00
case source . ScaleIO != nil :
if source . ScaleIO . SecretRef != nil && ! visitor ( source . ScaleIO . SecretRef . Name ) {
return false
}
2017-03-17 16:42:15 -04:00
case source . ISCSI != nil :
if source . ISCSI . SecretRef != nil && ! visitor ( source . ISCSI . SecretRef . Name ) {
return false
}
2017-02-24 10:47:40 -05:00
case source . StorageOS != nil :
if source . StorageOS . SecretRef != nil && ! visitor ( source . StorageOS . SecretRef . Name ) {
return false
}
2018-08-14 17:00:25 -04:00
case source . CSI != nil :
if source . CSI . NodePublishSecretRef != nil && ! visitor ( source . CSI . NodePublishSecretRef . Name ) {
return false
}
2017-02-23 00:35:44 -05:00
}
}
return true
}
2017-05-05 02:14:38 -04:00
func visitContainerSecretNames ( container * api . Container , visitor Visitor ) bool {
2017-02-23 00:35:44 -05:00
for _ , env := range container . EnvFrom {
if env . SecretRef != nil {
if ! visitor ( env . SecretRef . Name ) {
return false
}
}
}
for _ , envVar := range container . Env {
if envVar . ValueFrom != nil && envVar . ValueFrom . SecretKeyRef != nil {
if ! visitor ( envVar . ValueFrom . SecretKeyRef . Name ) {
return false
}
}
}
return true
}
2017-04-10 18:16:45 -04:00
2017-05-05 02:14:38 -04:00
// VisitPodConfigmapNames invokes the visitor function with the name of every configmap
// referenced by the pod spec. If visitor returns false, visiting is short-circuited.
// Transitive references (e.g. pod -> pvc -> pv -> secret) are not visited.
// Returns true if visiting completed, false if visiting was short-circuited.
2020-03-20 10:59:44 -04:00
func VisitPodConfigmapNames ( pod * api . Pod , visitor Visitor , containerType ContainerType ) bool {
2021-02-27 14:09:57 -05:00
visitor = skipEmptyNames ( visitor )
2020-03-20 10:59:44 -04:00
VisitContainers ( & pod . Spec , containerType , func ( c * api . Container , containerType ContainerType ) bool {
2019-06-14 11:20:16 -04:00
return visitContainerConfigmapNames ( c , visitor )
} )
2017-05-05 02:14:38 -04:00
var source * api . VolumeSource
for i := range pod . Spec . Volumes {
source = & pod . Spec . Volumes [ i ] . VolumeSource
switch {
case source . Projected != nil :
for j := range source . Projected . Sources {
if source . Projected . Sources [ j ] . ConfigMap != nil {
if ! visitor ( source . Projected . Sources [ j ] . ConfigMap . Name ) {
return false
}
}
}
case source . ConfigMap != nil :
if ! visitor ( source . ConfigMap . Name ) {
return false
}
}
}
return true
}
func visitContainerConfigmapNames ( container * api . Container , visitor Visitor ) bool {
for _ , env := range container . EnvFrom {
if env . ConfigMapRef != nil {
if ! visitor ( env . ConfigMapRef . Name ) {
return false
}
}
}
for _ , envVar := range container . Env {
if envVar . ValueFrom != nil && envVar . ValueFrom . ConfigMapKeyRef != nil {
if ! visitor ( envVar . ValueFrom . ConfigMapKeyRef . Name ) {
return false
}
}
}
return true
}
2017-04-10 18:16:45 -04:00
// IsPodReady returns true if a pod is ready; false otherwise.
func IsPodReady ( pod * api . Pod ) bool {
return IsPodReadyConditionTrue ( pod . Status )
}
2018-02-09 01:53:53 -05:00
// IsPodReadyConditionTrue returns true if a pod is ready; false otherwise.
2017-04-10 18:16:45 -04:00
func IsPodReadyConditionTrue ( status api . PodStatus ) bool {
condition := GetPodReadyCondition ( status )
return condition != nil && condition . Status == api . ConditionTrue
}
// GetPodReadyCondition extracts the pod ready condition from the given status and returns that.
// Returns nil if the condition is not present.
func GetPodReadyCondition ( status api . PodStatus ) * api . PodCondition {
_ , condition := GetPodCondition ( & status , api . PodReady )
return condition
}
// GetPodCondition extracts the provided condition from the given status and returns that.
// Returns nil and -1 if the condition is not present, and the index of the located condition.
func GetPodCondition ( status * api . PodStatus , conditionType api . PodConditionType ) ( int , * api . PodCondition ) {
if status == nil {
return - 1 , nil
}
for i := range status . Conditions {
if status . Conditions [ i ] . Type == conditionType {
return i , & status . Conditions [ i ]
}
}
return - 1 , nil
}
// UpdatePodCondition updates existing pod condition or creates a new one. Sets LastTransitionTime to now if the
// status has changed.
// Returns true if pod condition has changed or has been added.
func UpdatePodCondition ( status * api . PodStatus , condition * api . PodCondition ) bool {
condition . LastTransitionTime = metav1 . Now ( )
// Try to find this pod condition.
conditionIndex , oldCondition := GetPodCondition ( status , condition . Type )
if oldCondition == nil {
// We are adding new pod condition.
status . Conditions = append ( status . Conditions , * condition )
return true
}
// We are updating an existing condition, so we need to check if it has changed.
if condition . Status == oldCondition . Status {
condition . LastTransitionTime = oldCondition . LastTransitionTime
}
isEqual := condition . Status == oldCondition . Status &&
condition . Reason == oldCondition . Reason &&
condition . Message == oldCondition . Message &&
2017-08-04 11:04:14 -04:00
condition . LastProbeTime . Equal ( & oldCondition . LastProbeTime ) &&
condition . LastTransitionTime . Equal ( & oldCondition . LastTransitionTime )
2017-04-10 18:16:45 -04:00
status . Conditions [ conditionIndex ] = * condition
// Return true if one of the fields have changed.
return ! isEqual
}
2017-08-18 16:18:53 -04:00
2021-01-28 02:32:51 -05:00
func checkContainerUseIndivisibleHugePagesValues ( container api . Container ) bool {
for resourceName , quantity := range container . Resources . Limits {
if helper . IsHugePageResourceName ( resourceName ) {
if ! helper . IsHugePageResourceValueDivisible ( resourceName , quantity ) {
return true
}
}
}
for resourceName , quantity := range container . Resources . Requests {
if helper . IsHugePageResourceName ( resourceName ) {
if ! helper . IsHugePageResourceValueDivisible ( resourceName , quantity ) {
return true
}
}
}
return false
}
// usesIndivisibleHugePagesValues returns true if the one of the containers uses non-integer multiple
// of huge page unit size
func usesIndivisibleHugePagesValues ( podSpec * api . PodSpec ) bool {
foundIndivisibleHugePagesValue := false
VisitContainers ( podSpec , AllContainers , func ( c * api . Container , containerType ContainerType ) bool {
if checkContainerUseIndivisibleHugePagesValues ( * c ) {
foundIndivisibleHugePagesValue = true
}
return ! foundIndivisibleHugePagesValue // continue visiting if we haven't seen an invalid value yet
} )
if foundIndivisibleHugePagesValue {
return true
}
for resourceName , quantity := range podSpec . Overhead {
if helper . IsHugePageResourceName ( resourceName ) {
if ! helper . IsHugePageResourceValueDivisible ( resourceName , quantity ) {
return true
}
}
}
return false
}
2022-08-11 09:21:06 -04:00
// hasInvalidTopologySpreadConstraintLabelSelector return true if spec.TopologySpreadConstraints have any entry with invalid labelSelector
func hasInvalidTopologySpreadConstraintLabelSelector ( spec * api . PodSpec ) bool {
for _ , constraint := range spec . TopologySpreadConstraints {
errs := metavalidation . ValidateLabelSelector ( constraint . LabelSelector , metavalidation . LabelSelectorValidationOptions { AllowInvalidLabelValueInSelector : false } , nil )
if len ( errs ) != 0 {
return true
}
}
return false
}
2021-02-17 15:39:42 -05:00
// GetValidationOptionsFromPodSpecAndMeta returns validation options based on pod specs and metadata
func GetValidationOptionsFromPodSpecAndMeta ( podSpec , oldPodSpec * api . PodSpec , podMeta , oldPodMeta * metav1 . ObjectMeta ) apivalidation . PodValidationOptions {
2020-11-06 14:22:53 -05:00
// default pod validation options based on feature gate
2021-04-08 20:44:54 -04:00
opts := apivalidation . PodValidationOptions {
2021-02-17 15:39:42 -05:00
AllowInvalidPodDeletionCost : ! utilfeature . DefaultFeatureGate . Enabled ( features . PodDeletionCost ) ,
2023-05-27 11:44:23 -04:00
// Allow pod spec to use status.hostIPs in downward API if feature is enabled
AllowHostIPsField : utilfeature . DefaultFeatureGate . Enabled ( features . PodHostIPs ) ,
2021-01-28 02:32:51 -05:00
// Do not allow pod spec to use non-integer multiple of huge page unit size default
2023-03-19 06:25:23 -04:00
AllowIndivisibleHugePagesValues : false ,
2022-08-11 09:21:06 -04:00
AllowInvalidLabelValueInSelector : false ,
AllowInvalidTopologySpreadConstraintLabelSelector : false ,
2023-02-28 18:52:45 -05:00
AllowMutableNodeSelectorAndNodeAffinity : utilfeature . DefaultFeatureGate . Enabled ( features . PodSchedulingReadiness ) ,
2023-07-17 01:59:24 -04:00
AllowNamespacedSysctlsForHostNetAndHostIPC : false ,
2020-11-06 14:22:53 -05:00
}
2021-02-17 15:39:42 -05:00
if oldPodSpec != nil {
2023-05-27 11:44:23 -04:00
// if old spec has status.hostIPs downwardAPI set, we must allow it
opts . AllowHostIPsField = opts . AllowHostIPsField || hasUsedDownwardAPIFieldPathWithPodSpec ( oldPodSpec , "status.hostIPs" )
2021-01-28 02:32:51 -05:00
// if old spec used non-integer multiple of huge page unit size, we must allow it
opts . AllowIndivisibleHugePagesValues = usesIndivisibleHugePagesValues ( oldPodSpec )
2021-05-28 01:38:42 -04:00
2022-01-13 09:10:10 -05:00
opts . AllowInvalidLabelValueInSelector = hasInvalidLabelValueInAffinitySelector ( oldPodSpec )
2022-08-11 09:21:06 -04:00
// if old spec has invalid labelSelector in topologySpreadConstraint, we must allow it
opts . AllowInvalidTopologySpreadConstraintLabelSelector = hasInvalidTopologySpreadConstraintLabelSelector ( oldPodSpec )
2023-07-17 01:59:24 -04:00
// if old spec has invalid sysctl with hostNet or hostIPC, we must allow it when update
if oldPodSpec . SecurityContext != nil && len ( oldPodSpec . SecurityContext . Sysctls ) != 0 {
for _ , s := range oldPodSpec . SecurityContext . Sysctls {
err := apivalidation . ValidateHostSysctl ( s . Name , oldPodSpec . SecurityContext , nil )
if err != nil {
opts . AllowNamespacedSysctlsForHostNetAndHostIPC = true
break
}
}
}
2020-11-06 14:22:53 -05:00
}
2021-02-17 15:39:42 -05:00
if oldPodMeta != nil && ! opts . AllowInvalidPodDeletionCost {
// This is an update, so validate only if the existing object was valid.
_ , err := helper . GetDeletionCostFromPodAnnotations ( oldPodMeta . Annotations )
opts . AllowInvalidPodDeletionCost = err != nil
2020-11-06 14:22:53 -05:00
}
2021-01-28 02:32:51 -05:00
2020-11-06 14:22:53 -05:00
return opts
}
2023-05-27 11:44:23 -04:00
func hasUsedDownwardAPIFieldPathWithPodSpec ( podSpec * api . PodSpec , fieldPath string ) bool {
if podSpec == nil {
return false
}
for _ , vol := range podSpec . Volumes {
if hasUsedDownwardAPIFieldPathWithVolume ( & vol , fieldPath ) {
return true
}
}
for _ , c := range podSpec . InitContainers {
if hasUsedDownwardAPIFieldPathWithContainer ( & c , fieldPath ) {
return true
}
}
for _ , c := range podSpec . Containers {
if hasUsedDownwardAPIFieldPathWithContainer ( & c , fieldPath ) {
return true
}
}
return false
}
func hasUsedDownwardAPIFieldPathWithVolume ( volume * api . Volume , fieldPath string ) bool {
if volume == nil || volume . DownwardAPI == nil {
return false
}
for _ , file := range volume . DownwardAPI . Items {
if file . FieldRef != nil &&
file . FieldRef . FieldPath == fieldPath {
return true
}
}
return false
}
func hasUsedDownwardAPIFieldPathWithContainer ( container * api . Container , fieldPath string ) bool {
if container == nil {
return false
}
for _ , env := range container . Env {
if env . ValueFrom != nil &&
env . ValueFrom . FieldRef != nil &&
env . ValueFrom . FieldRef . FieldPath == fieldPath {
return true
}
}
return false
}
2020-11-06 14:22:53 -05:00
// GetValidationOptionsFromPodTemplate will return pod validation options for specified template.
func GetValidationOptionsFromPodTemplate ( podTemplate , oldPodTemplate * api . PodTemplateSpec ) apivalidation . PodValidationOptions {
var newPodSpec , oldPodSpec * api . PodSpec
2021-02-17 15:39:42 -05:00
var newPodMeta , oldPodMeta * metav1 . ObjectMeta
2020-11-06 14:22:53 -05:00
// we have to be careful about nil pointers here
// replication controller in particular is prone to passing nil
if podTemplate != nil {
newPodSpec = & podTemplate . Spec
2021-02-17 15:39:42 -05:00
newPodMeta = & podTemplate . ObjectMeta
2020-11-06 14:22:53 -05:00
}
if oldPodTemplate != nil {
oldPodSpec = & oldPodTemplate . Spec
2021-02-17 15:39:42 -05:00
oldPodMeta = & oldPodTemplate . ObjectMeta
2020-11-06 14:22:53 -05:00
}
2021-02-17 15:39:42 -05:00
return GetValidationOptionsFromPodSpecAndMeta ( newPodSpec , oldPodSpec , newPodMeta , oldPodMeta )
2020-11-06 14:22:53 -05:00
}
2019-01-07 14:17:29 -05:00
// DropDisabledTemplateFields removes disabled fields from the pod template metadata and spec.
// This should be called from PrepareForCreate/PrepareForUpdate for all resources containing a PodTemplateSpec
func DropDisabledTemplateFields ( podTemplate , oldPodTemplate * api . PodTemplateSpec ) {
var (
podSpec * api . PodSpec
podAnnotations map [ string ] string
oldPodSpec * api . PodSpec
oldPodAnnotations map [ string ] string
)
if podTemplate != nil {
podSpec = & podTemplate . Spec
podAnnotations = podTemplate . Annotations
}
if oldPodTemplate != nil {
oldPodSpec = & oldPodTemplate . Spec
oldPodAnnotations = oldPodTemplate . Annotations
}
dropDisabledFields ( podSpec , podAnnotations , oldPodSpec , oldPodAnnotations )
}
// DropDisabledPodFields removes disabled fields from the pod metadata and spec.
// This should be called from PrepareForCreate/PrepareForUpdate for all resources containing a Pod
func DropDisabledPodFields ( pod , oldPod * api . Pod ) {
var (
podSpec * api . PodSpec
2021-11-03 18:43:43 -04:00
podStatus * api . PodStatus
2019-01-07 14:17:29 -05:00
podAnnotations map [ string ] string
oldPodSpec * api . PodSpec
2021-11-03 18:43:43 -04:00
oldPodStatus * api . PodStatus
2019-01-07 14:17:29 -05:00
oldPodAnnotations map [ string ] string
)
if pod != nil {
podSpec = & pod . Spec
2021-11-03 18:43:43 -04:00
podStatus = & pod . Status
2019-01-07 14:17:29 -05:00
podAnnotations = pod . Annotations
}
if oldPod != nil {
oldPodSpec = & oldPod . Spec
2021-11-03 18:43:43 -04:00
oldPodStatus = & oldPod . Status
2019-01-07 14:17:29 -05:00
oldPodAnnotations = oldPod . Annotations
}
dropDisabledFields ( podSpec , podAnnotations , oldPodSpec , oldPodAnnotations )
2021-11-03 18:43:43 -04:00
dropDisabledPodStatusFields ( podStatus , oldPodStatus , podSpec , oldPodSpec )
2019-01-07 14:17:29 -05:00
}
// dropDisabledFields removes disabled fields from the pod metadata and spec.
func dropDisabledFields (
podSpec * api . PodSpec , podAnnotations map [ string ] string ,
oldPodSpec * api . PodSpec , oldPodAnnotations map [ string ] string ,
) {
// the new spec must always be non-nil
if podSpec == nil {
podSpec = & api . PodSpec { }
}
2019-01-07 15:10:11 -05:00
if ! utilfeature . DefaultFeatureGate . Enabled ( features . AppArmor ) && ! appArmorInUse ( oldPodAnnotations ) {
for k := range podAnnotations {
2020-03-18 10:51:47 -04:00
if strings . HasPrefix ( k , v1 . AppArmorBetaContainerAnnotationKeyPrefix ) {
2019-01-07 15:10:11 -05:00
delete ( podAnnotations , k )
}
}
}
2022-07-07 10:49:39 -04:00
// If the feature is disabled and not in use, drop the hostUsers field.
2023-06-22 09:18:22 -04:00
if ! utilfeature . DefaultFeatureGate . Enabled ( features . UserNamespacesSupport ) && ! hostUsersInUse ( oldPodSpec ) {
2022-07-07 10:49:39 -04:00
// Drop the field in podSpec only if SecurityContext is not nil.
// If it is nil, there is no need to set hostUsers=nil (it will be nil too).
if podSpec . SecurityContext != nil {
podSpec . SecurityContext . HostUsers = nil
}
}
2022-10-28 15:05:46 -04:00
// If the feature is disabled and not in use, drop the schedulingGates field.
if ! utilfeature . DefaultFeatureGate . Enabled ( features . PodSchedulingReadiness ) && ! schedulingGatesInUse ( oldPodSpec ) {
podSpec . SchedulingGates = nil
}
2018-12-17 12:49:29 -05:00
dropDisabledProcMountField ( podSpec , oldPodSpec )
2018-08-14 17:00:25 -04:00
2022-02-23 07:11:59 -05:00
dropDisabledTopologySpreadConstraintsFields ( podSpec , oldPodSpec )
2022-05-10 00:54:49 -04:00
dropDisabledNodeInclusionPolicyFields ( podSpec , oldPodSpec )
2023-02-25 23:25:59 -05:00
dropDisabledMatchLabelKeysFieldInTopologySpread ( podSpec , oldPodSpec )
dropDisabledMatchLabelKeysFieldInPodAffinity ( podSpec , oldPodSpec )
2022-11-04 08:46:49 -04:00
dropDisabledDynamicResourceAllocationFields ( podSpec , oldPodSpec )
2022-10-21 22:50:01 -04:00
dropDisabledClusterTrustBundleProjection ( podSpec , oldPodSpec )
2021-11-03 18:43:43 -04:00
if ! utilfeature . DefaultFeatureGate . Enabled ( features . InPlacePodVerticalScaling ) && ! inPlacePodVerticalScalingInUse ( oldPodSpec ) {
// Drop ResizePolicy fields. Don't drop updates to Resources field as template.spec.resources
// field is mutable for certain controllers. Let ValidatePodUpdate handle it.
for i := range podSpec . Containers {
podSpec . Containers [ i ] . ResizePolicy = nil
}
for i := range podSpec . InitContainers {
podSpec . InitContainers [ i ] . ResizePolicy = nil
}
for i := range podSpec . EphemeralContainers {
podSpec . EphemeralContainers [ i ] . ResizePolicy = nil
}
}
2023-05-09 12:34:46 -04:00
if ! utilfeature . DefaultFeatureGate . Enabled ( features . SidecarContainers ) && ! restartableInitContainersInUse ( oldPodSpec ) {
// Drop the RestartPolicy field of init containers.
for i := range podSpec . InitContainers {
podSpec . InitContainers [ i ] . RestartPolicy = nil
}
// For other types of containers, validateContainers will handle them.
}
2023-08-18 02:09:41 -04:00
if ! utilfeature . DefaultFeatureGate . Enabled ( features . PodLifecycleSleepAction ) && ! podLifecycleSleepActionInUse ( oldPodSpec ) {
for i := range podSpec . Containers {
if podSpec . Containers [ i ] . Lifecycle == nil {
continue
}
if podSpec . Containers [ i ] . Lifecycle . PreStop != nil {
podSpec . Containers [ i ] . Lifecycle . PreStop . Sleep = nil
}
if podSpec . Containers [ i ] . Lifecycle . PostStart != nil {
podSpec . Containers [ i ] . Lifecycle . PostStart . Sleep = nil
}
}
for i := range podSpec . InitContainers {
if podSpec . InitContainers [ i ] . Lifecycle == nil {
continue
}
if podSpec . InitContainers [ i ] . Lifecycle . PreStop != nil {
podSpec . InitContainers [ i ] . Lifecycle . PreStop . Sleep = nil
}
if podSpec . InitContainers [ i ] . Lifecycle . PostStart != nil {
podSpec . InitContainers [ i ] . Lifecycle . PostStart . Sleep = nil
}
}
for i := range podSpec . EphemeralContainers {
if podSpec . EphemeralContainers [ i ] . Lifecycle == nil {
continue
}
if podSpec . EphemeralContainers [ i ] . Lifecycle . PreStop != nil {
podSpec . EphemeralContainers [ i ] . Lifecycle . PreStop . Sleep = nil
}
if podSpec . EphemeralContainers [ i ] . Lifecycle . PostStart != nil {
podSpec . EphemeralContainers [ i ] . Lifecycle . PostStart . Sleep = nil
}
}
}
}
func podLifecycleSleepActionInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil {
return false
}
var inUse bool
VisitContainers ( podSpec , AllContainers , func ( c * api . Container , containerType ContainerType ) bool {
if c . Lifecycle == nil {
return true
}
if c . Lifecycle . PreStop != nil && c . Lifecycle . PreStop . Sleep != nil {
inUse = true
return false
}
if c . Lifecycle . PostStart != nil && c . Lifecycle . PostStart . Sleep != nil {
inUse = true
return false
}
return true
} )
return inUse
2021-11-03 18:43:43 -04:00
}
// dropDisabledPodStatusFields removes disabled fields from the pod status
func dropDisabledPodStatusFields ( podStatus , oldPodStatus * api . PodStatus , podSpec , oldPodSpec * api . PodSpec ) {
// the new status is always be non-nil
if podStatus == nil {
podStatus = & api . PodStatus { }
}
if ! utilfeature . DefaultFeatureGate . Enabled ( features . InPlacePodVerticalScaling ) && ! inPlacePodVerticalScalingInUse ( oldPodSpec ) {
2023-03-09 22:35:35 -05:00
// Drop Resize, AllocatedResources, and Resources fields
2021-11-03 18:43:43 -04:00
dropResourcesFields := func ( csl [ ] api . ContainerStatus ) {
for i := range csl {
2023-03-09 22:35:35 -05:00
csl [ i ] . AllocatedResources = nil
2021-11-03 18:43:43 -04:00
csl [ i ] . Resources = nil
}
}
dropResourcesFields ( podStatus . ContainerStatuses )
dropResourcesFields ( podStatus . InitContainerStatuses )
dropResourcesFields ( podStatus . EphemeralContainerStatuses )
podStatus . Resize = ""
}
2023-04-14 03:50:52 -04:00
if ! utilfeature . DefaultFeatureGate . Enabled ( features . DynamicResourceAllocation ) && ! dynamicResourceAllocationInUse ( oldPodSpec ) {
podStatus . ResourceClaimStatuses = nil
}
2023-03-03 03:51:07 -05:00
// drop HostIPs to empty (disable PodHostIPs).
if ! utilfeature . DefaultFeatureGate . Enabled ( features . PodHostIPs ) && ! hostIPsInUse ( oldPodStatus ) {
podStatus . HostIPs = nil
}
}
func hostIPsInUse ( podStatus * api . PodStatus ) bool {
if podStatus == nil {
return false
}
return len ( podStatus . HostIPs ) > 0
2022-11-04 08:46:49 -04:00
}
// dropDisabledDynamicResourceAllocationFields removes pod claim references from
// container specs and pod-level resource claims unless they are already used
// by the old pod spec.
func dropDisabledDynamicResourceAllocationFields ( podSpec , oldPodSpec * api . PodSpec ) {
if ! utilfeature . DefaultFeatureGate . Enabled ( features . DynamicResourceAllocation ) && ! dynamicResourceAllocationInUse ( oldPodSpec ) {
dropResourceClaimRequests ( podSpec . Containers )
dropResourceClaimRequests ( podSpec . InitContainers )
dropEphemeralResourceClaimRequests ( podSpec . EphemeralContainers )
podSpec . ResourceClaims = nil
}
}
func dynamicResourceAllocationInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil {
return false
}
// We only need to check this field because the containers cannot have
// resource requirements entries for claims without a corresponding
// entry at the pod spec level.
return len ( podSpec . ResourceClaims ) > 0
}
func dropResourceClaimRequests ( containers [ ] api . Container ) {
for i := range containers {
containers [ i ] . Resources . Claims = nil
}
}
func dropEphemeralResourceClaimRequests ( containers [ ] api . EphemeralContainer ) {
for i := range containers {
containers [ i ] . Resources . Claims = nil
}
2022-02-23 07:11:59 -05:00
}
// dropDisabledTopologySpreadConstraintsFields removes disabled fields from PodSpec related
// to TopologySpreadConstraints only if it is not already used by the old spec.
func dropDisabledTopologySpreadConstraintsFields ( podSpec , oldPodSpec * api . PodSpec ) {
if ! utilfeature . DefaultFeatureGate . Enabled ( features . MinDomainsInPodTopologySpread ) &&
! minDomainsInUse ( oldPodSpec ) &&
podSpec != nil {
for i := range podSpec . TopologySpreadConstraints {
podSpec . TopologySpreadConstraints [ i ] . MinDomains = nil
}
}
}
// minDomainsInUse returns true if the pod spec is non-nil
// and has non-nil MinDomains field in TopologySpreadConstraints.
func minDomainsInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil {
return false
}
for _ , c := range podSpec . TopologySpreadConstraints {
if c . MinDomains != nil {
return true
}
}
return false
2017-05-13 02:29:25 -04:00
}
2018-12-17 12:49:29 -05:00
// dropDisabledProcMountField removes disabled fields from PodSpec related
2018-12-20 12:28:08 -05:00
// to ProcMount only if it is not already used by the old spec
2018-12-17 12:49:29 -05:00
func dropDisabledProcMountField ( podSpec , oldPodSpec * api . PodSpec ) {
2018-12-20 12:28:08 -05:00
if ! utilfeature . DefaultFeatureGate . Enabled ( features . ProcMountType ) && ! procMountInUse ( oldPodSpec ) {
defaultProcMount := api . DefaultProcMount
2020-03-05 14:48:00 -05:00
VisitContainers ( podSpec , AllContainers , func ( c * api . Container , containerType ContainerType ) bool {
2019-06-14 11:20:16 -04:00
if c . SecurityContext != nil && c . SecurityContext . ProcMount != nil {
// The ProcMount field was improperly forced to non-nil in 1.12.
// If the feature is disabled, and the existing object is not using any non-default values, and the ProcMount field is present in the incoming object, force to the default value.
// Note: we cannot force the field to nil when the feature is disabled because it causes a diff against previously persisted data.
c . SecurityContext . ProcMount = & defaultProcMount
2018-03-20 15:38:42 -04:00
}
2019-06-14 11:20:16 -04:00
return true
} )
2018-03-20 15:38:42 -04:00
}
}
2022-05-10 00:54:49 -04:00
// dropDisabledNodeInclusionPolicyFields removes disabled fields from PodSpec related
// to NodeInclusionPolicy only if it is not used by the old spec.
func dropDisabledNodeInclusionPolicyFields ( podSpec , oldPodSpec * api . PodSpec ) {
if ! utilfeature . DefaultFeatureGate . Enabled ( features . NodeInclusionPolicyInPodTopologySpread ) && podSpec != nil {
if ! nodeTaintsPolicyInUse ( oldPodSpec ) {
for i := range podSpec . TopologySpreadConstraints {
podSpec . TopologySpreadConstraints [ i ] . NodeTaintsPolicy = nil
}
}
if ! nodeAffinityPolicyInUse ( oldPodSpec ) {
for i := range podSpec . TopologySpreadConstraints {
podSpec . TopologySpreadConstraints [ i ] . NodeAffinityPolicy = nil
}
}
}
}
2023-02-25 23:25:59 -05:00
// dropDisabledMatchLabelKeysFieldInPodAffinity removes disabled fields from PodSpec related
// to MatchLabelKeys in required/preferred PodAffinity/PodAntiAffinity only if it is not already used by the old spec.
func dropDisabledMatchLabelKeysFieldInPodAffinity ( podSpec , oldPodSpec * api . PodSpec ) {
if podSpec == nil || podSpec . Affinity == nil || utilfeature . DefaultFeatureGate . Enabled ( features . MatchLabelKeysInPodAffinity ) || matchLabelKeysFieldInPodAffinityInUse ( oldPodSpec ) {
return
}
if affinity := podSpec . Affinity . PodAffinity ; affinity != nil {
dropMatchLabelKeysFieldInPodAffnityTerm ( affinity . RequiredDuringSchedulingIgnoredDuringExecution )
dropMatchLabelKeysFieldInWeightedPodAffnityTerm ( affinity . PreferredDuringSchedulingIgnoredDuringExecution )
}
if antiaffinity := podSpec . Affinity . PodAntiAffinity ; antiaffinity != nil {
dropMatchLabelKeysFieldInPodAffnityTerm ( antiaffinity . RequiredDuringSchedulingIgnoredDuringExecution )
dropMatchLabelKeysFieldInWeightedPodAffnityTerm ( antiaffinity . PreferredDuringSchedulingIgnoredDuringExecution )
}
}
// dropDisabledMatchLabelKeysFieldInTopologySpread removes disabled fields from PodSpec related
// to MatchLabelKeys in TopologySpread only if it is not already used by the old spec.
func dropDisabledMatchLabelKeysFieldInTopologySpread ( podSpec , oldPodSpec * api . PodSpec ) {
if ! utilfeature . DefaultFeatureGate . Enabled ( features . MatchLabelKeysInPodTopologySpread ) && ! matchLabelKeysInTopologySpreadInUse ( oldPodSpec ) {
2022-07-30 01:21:16 -04:00
for i := range podSpec . TopologySpreadConstraints {
podSpec . TopologySpreadConstraints [ i ] . MatchLabelKeys = nil
}
}
}
2023-02-25 23:25:59 -05:00
// dropMatchLabelKeysFieldInWeightedPodAffnityTerm removes MatchLabelKeys and MismatchLabelKeys fields from WeightedPodAffinityTerm
func dropMatchLabelKeysFieldInWeightedPodAffnityTerm ( terms [ ] api . WeightedPodAffinityTerm ) {
for i := range terms {
terms [ i ] . PodAffinityTerm . MatchLabelKeys = nil
terms [ i ] . PodAffinityTerm . MismatchLabelKeys = nil
}
}
// dropMatchLabelKeysFieldInPodAffnityTerm removes MatchLabelKeys and MismatchLabelKeys fields from PodAffinityTerm
func dropMatchLabelKeysFieldInPodAffnityTerm ( terms [ ] api . PodAffinityTerm ) {
for i := range terms {
terms [ i ] . MatchLabelKeys = nil
terms [ i ] . MismatchLabelKeys = nil
}
}
// matchLabelKeysFieldInPodAffinityInUse returns true if given affinityTerms have MatchLabelKeys field set.
func matchLabelKeysFieldInPodAffinityInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil || podSpec . Affinity == nil {
return false
}
if affinity := podSpec . Affinity . PodAffinity ; affinity != nil {
for _ , c := range affinity . RequiredDuringSchedulingIgnoredDuringExecution {
if len ( c . MatchLabelKeys ) > 0 || len ( c . MismatchLabelKeys ) > 0 {
return true
}
}
for _ , c := range affinity . PreferredDuringSchedulingIgnoredDuringExecution {
if len ( c . PodAffinityTerm . MatchLabelKeys ) > 0 || len ( c . PodAffinityTerm . MismatchLabelKeys ) > 0 {
return true
}
}
}
if antiAffinity := podSpec . Affinity . PodAntiAffinity ; antiAffinity != nil {
for _ , c := range antiAffinity . RequiredDuringSchedulingIgnoredDuringExecution {
if len ( c . MatchLabelKeys ) > 0 || len ( c . MismatchLabelKeys ) > 0 {
return true
}
}
for _ , c := range antiAffinity . PreferredDuringSchedulingIgnoredDuringExecution {
if len ( c . PodAffinityTerm . MatchLabelKeys ) > 0 || len ( c . PodAffinityTerm . MismatchLabelKeys ) > 0 {
return true
}
}
}
return false
}
// matchLabelKeysInTopologySpreadInUse returns true if the pod spec is non-nil
2022-07-30 01:21:16 -04:00
// and has MatchLabelKeys field set in TopologySpreadConstraints.
2023-02-25 23:25:59 -05:00
func matchLabelKeysInTopologySpreadInUse ( podSpec * api . PodSpec ) bool {
2022-07-30 01:21:16 -04:00
if podSpec == nil {
return false
}
for _ , c := range podSpec . TopologySpreadConstraints {
if len ( c . MatchLabelKeys ) > 0 {
return true
}
}
return false
}
2022-05-10 00:54:49 -04:00
// nodeAffinityPolicyInUse returns true if the pod spec is non-nil and has NodeAffinityPolicy field set
// in TopologySpreadConstraints
func nodeAffinityPolicyInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil {
return false
}
for _ , c := range podSpec . TopologySpreadConstraints {
if c . NodeAffinityPolicy != nil {
return true
}
}
return false
}
// nodeTaintsPolicyInUse returns true if the pod spec is non-nil and has NodeTaintsPolicy field set
// in TopologySpreadConstraints
func nodeTaintsPolicyInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil {
return false
}
for _ , c := range podSpec . TopologySpreadConstraints {
if c . NodeTaintsPolicy != nil {
return true
}
}
return false
}
2022-07-07 10:49:39 -04:00
// hostUsersInUse returns true if the pod spec has spec.hostUsers field set.
func hostUsersInUse ( podSpec * api . PodSpec ) bool {
if podSpec != nil && podSpec . SecurityContext != nil && podSpec . SecurityContext . HostUsers != nil {
return true
}
return false
}
2021-11-03 18:43:43 -04:00
// inPlacePodVerticalScalingInUse returns true if pod spec is non-nil and ResizePolicy is set
func inPlacePodVerticalScalingInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil {
return false
}
var inUse bool
VisitContainers ( podSpec , Containers , func ( c * api . Container , containerType ContainerType ) bool {
if len ( c . ResizePolicy ) > 0 {
inUse = true
return false
}
return true
} )
return inUse
}
2019-06-10 21:47:00 -04:00
// procMountInUse returns true if the pod spec is non-nil and has a SecurityContext's ProcMount field set to a non-default value
2018-12-20 12:28:08 -05:00
func procMountInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil {
return false
}
2019-06-14 11:20:16 -04:00
var inUse bool
2020-03-05 14:48:00 -05:00
VisitContainers ( podSpec , AllContainers , func ( c * api . Container , containerType ContainerType ) bool {
2019-06-14 11:20:16 -04:00
if c . SecurityContext == nil || c . SecurityContext . ProcMount == nil {
return true
2018-12-20 12:28:08 -05:00
}
2019-06-14 11:20:16 -04:00
if * c . SecurityContext . ProcMount != api . DefaultProcMount {
inUse = true
return false
2018-12-20 12:28:08 -05:00
}
2019-06-14 11:20:16 -04:00
return true
} )
return inUse
2018-12-20 12:28:08 -05:00
}
2018-12-19 14:55:34 -05:00
2019-01-07 15:10:11 -05:00
// appArmorInUse returns true if the pod has apparmor related information
func appArmorInUse ( podAnnotations map [ string ] string ) bool {
for k := range podAnnotations {
2020-03-18 10:51:47 -04:00
if strings . HasPrefix ( k , v1 . AppArmorBetaContainerAnnotationKeyPrefix ) {
2019-01-07 15:10:11 -05:00
return true
}
}
return false
}
2022-10-28 15:05:46 -04:00
// schedulingGatesInUse returns true if the pod spec is non-nil and it has SchedulingGates field set.
func schedulingGatesInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil {
return false
}
return len ( podSpec . SchedulingGates ) != 0
}
2023-05-09 12:34:46 -04:00
// restartableInitContainersInUse returns true if the pod spec is non-nil and
// it has any init container with ContainerRestartPolicyAlways.
func restartableInitContainersInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil {
return false
}
var inUse bool
VisitContainers ( podSpec , InitContainers , func ( c * api . Container , containerType ContainerType ) bool {
if c . RestartPolicy != nil && * c . RestartPolicy == api . ContainerRestartPolicyAlways {
inUse = true
return false
}
return true
} )
return inUse
}
2022-10-21 22:50:01 -04:00
func clusterTrustBundleProjectionInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil {
return false
}
for _ , v := range podSpec . Volumes {
if v . Projected == nil {
continue
}
for _ , s := range v . Projected . Sources {
if s . ClusterTrustBundle != nil {
return true
}
}
}
return false
}
func dropDisabledClusterTrustBundleProjection ( podSpec , oldPodSpec * api . PodSpec ) {
if utilfeature . DefaultFeatureGate . Enabled ( features . ClusterTrustBundleProjection ) {
return
}
if podSpec == nil {
return
}
// If the pod was already using it, it can keep using it.
if clusterTrustBundleProjectionInUse ( oldPodSpec ) {
return
}
2022-10-22 02:13:42 -04:00
for i := range podSpec . Volumes {
if podSpec . Volumes [ i ] . Projected == nil {
2022-10-21 22:50:01 -04:00
continue
}
2022-10-22 02:13:42 -04:00
for j := range podSpec . Volumes [ i ] . Projected . Sources {
podSpec . Volumes [ i ] . Projected . Sources [ j ] . ClusterTrustBundle = nil
2022-10-21 22:50:01 -04:00
}
}
}
2022-01-13 09:10:10 -05:00
func hasInvalidLabelValueInAffinitySelector ( spec * api . PodSpec ) bool {
if spec . Affinity != nil {
if spec . Affinity . PodAffinity != nil {
for _ , term := range spec . Affinity . PodAffinity . RequiredDuringSchedulingIgnoredDuringExecution {
allErrs := apivalidation . ValidatePodAffinityTermSelector ( term , false , nil )
if len ( allErrs ) != 0 {
return true
}
}
for _ , term := range spec . Affinity . PodAffinity . PreferredDuringSchedulingIgnoredDuringExecution {
allErrs := apivalidation . ValidatePodAffinityTermSelector ( term . PodAffinityTerm , false , nil )
if len ( allErrs ) != 0 {
return true
}
}
}
if spec . Affinity . PodAntiAffinity != nil {
for _ , term := range spec . Affinity . PodAntiAffinity . RequiredDuringSchedulingIgnoredDuringExecution {
allErrs := apivalidation . ValidatePodAffinityTermSelector ( term , false , nil )
if len ( allErrs ) != 0 {
return true
}
}
for _ , term := range spec . Affinity . PodAntiAffinity . PreferredDuringSchedulingIgnoredDuringExecution {
allErrs := apivalidation . ValidatePodAffinityTermSelector ( term . PodAffinityTerm , false , nil )
if len ( allErrs ) != 0 {
return true
}
}
}
}
return false
}
2021-11-03 18:43:43 -04:00
func MarkPodProposedForResize ( oldPod , newPod * api . Pod ) {
for i , c := range newPod . Spec . Containers {
if c . Resources . Requests == nil {
continue
}
2023-03-23 14:19:41 -04:00
if cmp . Equal ( oldPod . Spec . Containers [ i ] . Resources , c . Resources ) {
2021-11-03 18:43:43 -04:00
continue
}
findContainerStatus := func ( css [ ] api . ContainerStatus , cName string ) ( api . ContainerStatus , bool ) {
for i := range css {
if css [ i ] . Name == cName {
return css [ i ] , true
}
}
return api . ContainerStatus { } , false
}
if cs , ok := findContainerStatus ( newPod . Status . ContainerStatuses , c . Name ) ; ok {
2023-03-23 14:19:41 -04:00
if ! cmp . Equal ( c . Resources . Requests , cs . AllocatedResources ) {
2021-11-03 18:43:43 -04:00
newPod . Status . Resize = api . PodResizeStatusProposed
break
}
}
}
}