2019-01-12 04:58:27 +00:00
/ *
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 pod
import (
2019-04-07 17:07:55 +00:00
"strings"
2020-12-01 01:06:26 +00:00
v1 "k8s.io/api/core/v1"
2019-01-12 04:58:27 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2020-12-01 01:06:26 +00:00
"k8s.io/apimachinery/pkg/util/sets"
2019-01-12 04:58:27 +00:00
utilfeature "k8s.io/apiserver/pkg/util/feature"
api "k8s.io/kubernetes/pkg/apis/core"
2020-12-01 01:06:26 +00:00
"k8s.io/kubernetes/pkg/apis/core/helper"
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
"k8s.io/kubernetes/pkg/apis/core/validation"
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
2019-01-12 04:58:27 +00:00
"k8s.io/kubernetes/pkg/features"
)
2020-03-26 21:07:15 +00: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-08-10 17:43:49 +00:00
// AllFeatureEnabledContainers returns a ContainerType mask which includes all container
// types except for the ones guarded by feature gate.
func AllFeatureEnabledContainers ( ) ContainerType {
containerType := AllContainers
if ! utilfeature . DefaultFeatureGate . Enabled ( features . EphemeralContainers ) {
containerType &= ^ EphemeralContainers
}
return containerType
}
2019-09-27 21:51:53 +00:00
// ContainerVisitor is called with each container spec, and returns true
// if visiting should continue.
2020-03-26 21:07:15 +00:00
type ContainerVisitor func ( container * api . Container , containerType ContainerType ) ( shouldContinue bool )
2019-09-27 21:51:53 +00:00
2020-08-10 17:43:49 +00: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-09-27 21:51:53 +00:00
// visiting is short-circuited. VisitContainers returns true if visiting completes,
// false if visiting was short-circuited.
2020-03-26 21:07:15 +00:00
func VisitContainers ( podSpec * api . PodSpec , mask ContainerType , visitor ContainerVisitor ) bool {
2020-08-10 17:43:49 +00:00
if mask & InitContainers != 0 {
2020-03-26 21:07:15 +00:00
for i := range podSpec . InitContainers {
if ! visitor ( & podSpec . InitContainers [ i ] , InitContainers ) {
return false
}
2019-09-27 21:51:53 +00:00
}
}
2020-08-10 17:43:49 +00:00
if mask & Containers != 0 {
2020-03-26 21:07:15 +00:00
for i := range podSpec . Containers {
if ! visitor ( & podSpec . Containers [ i ] , Containers ) {
return false
}
2019-09-27 21:51:53 +00:00
}
}
2020-08-10 17:43:49 +00:00
if mask & EphemeralContainers != 0 {
2019-09-27 21:51:53 +00:00
for i := range podSpec . EphemeralContainers {
2020-03-26 21:07:15 +00:00
if ! visitor ( ( * api . Container ) ( & podSpec . EphemeralContainers [ i ] . EphemeralContainerCommon ) , EphemeralContainers ) {
2019-09-27 21:51:53 +00:00
return false
}
}
}
return true
}
2019-01-12 04:58:27 +00:00
// Visitor is called with each object name, and returns true if visiting should continue
type Visitor func ( name string ) ( shouldContinue bool )
2021-03-19 18:50:37 +00: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 )
}
}
2019-01-12 04:58:27 +00: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-08-10 17:43:49 +00:00
func VisitPodSecretNames ( pod * api . Pod , visitor Visitor , containerType ContainerType ) bool {
2021-03-19 18:50:37 +00:00
visitor = skipEmptyNames ( visitor )
2019-01-12 04:58:27 +00:00
for _ , reference := range pod . Spec . ImagePullSecrets {
if ! visitor ( reference . Name ) {
return false
}
}
2020-08-10 17:43:49 +00:00
VisitContainers ( & pod . Spec , containerType , func ( c * api . Container , containerType ContainerType ) bool {
2019-09-27 21:51:53 +00:00
return visitContainerSecretNames ( c , visitor )
} )
2019-01-12 04:58:27 +00:00
var source * api . VolumeSource
for i := range pod . Spec . Volumes {
source = & pod . Spec . Volumes [ i ] . VolumeSource
switch {
case source . AzureFile != nil :
if len ( source . AzureFile . SecretName ) > 0 && ! visitor ( source . AzureFile . SecretName ) {
return false
}
case source . CephFS != nil :
if source . CephFS . SecretRef != nil && ! visitor ( source . CephFS . SecretRef . Name ) {
return false
}
case source . Cinder != nil :
if source . Cinder . SecretRef != nil && ! visitor ( source . Cinder . SecretRef . Name ) {
return false
}
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
}
case source . ScaleIO != nil :
if source . ScaleIO . SecretRef != nil && ! visitor ( source . ScaleIO . SecretRef . Name ) {
return false
}
case source . ISCSI != nil :
if source . ISCSI . SecretRef != nil && ! visitor ( source . ISCSI . SecretRef . Name ) {
return false
}
case source . StorageOS != nil :
if source . StorageOS . SecretRef != nil && ! visitor ( source . StorageOS . SecretRef . Name ) {
return false
}
2019-04-07 17:07:55 +00:00
case source . CSI != nil :
if source . CSI . NodePublishSecretRef != nil && ! visitor ( source . CSI . NodePublishSecretRef . Name ) {
return false
}
2019-01-12 04:58:27 +00:00
}
}
return true
}
func visitContainerSecretNames ( container * api . Container , visitor Visitor ) bool {
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
}
// 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-08-10 17:43:49 +00:00
func VisitPodConfigmapNames ( pod * api . Pod , visitor Visitor , containerType ContainerType ) bool {
2021-03-19 18:50:37 +00:00
visitor = skipEmptyNames ( visitor )
2020-08-10 17:43:49 +00:00
VisitContainers ( & pod . Spec , containerType , func ( c * api . Container , containerType ContainerType ) bool {
2019-09-27 21:51:53 +00:00
return visitContainerConfigmapNames ( c , visitor )
} )
2019-01-12 04:58:27 +00: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
}
// IsPodReady returns true if a pod is ready; false otherwise.
func IsPodReady ( pod * api . Pod ) bool {
return IsPodReadyConditionTrue ( pod . Status )
}
// IsPodReadyConditionTrue returns true if a pod is ready; false otherwise.
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 &&
condition . LastProbeTime . Equal ( & oldCondition . LastProbeTime ) &&
condition . LastTransitionTime . Equal ( & oldCondition . LastTransitionTime )
status . Conditions [ conditionIndex ] = * condition
// Return true if one of the fields have changed.
return ! isEqual
}
2020-12-01 01:06:26 +00:00
// usesHugePagesInProjectedVolume returns true if hugepages are used in downward api for volume
func usesHugePagesInProjectedVolume ( podSpec * api . PodSpec ) bool {
// determine if any container is using hugepages in downward api volume
for _ , volumeSource := range podSpec . Volumes {
if volumeSource . DownwardAPI != nil {
for _ , item := range volumeSource . DownwardAPI . Items {
if item . ResourceFieldRef != nil {
if strings . HasPrefix ( item . ResourceFieldRef . Resource , "requests.hugepages-" ) || strings . HasPrefix ( item . ResourceFieldRef . Resource , "limits.hugepages-" ) {
return true
}
}
}
}
}
return false
}
// usesHugePagesInProjectedEnv returns true if hugepages are used in downward api for volume
func usesHugePagesInProjectedEnv ( item api . Container ) bool {
for _ , env := range item . Env {
if env . ValueFrom != nil {
if env . ValueFrom . ResourceFieldRef != nil {
if strings . HasPrefix ( env . ValueFrom . ResourceFieldRef . Resource , "requests.hugepages-" ) || strings . HasPrefix ( env . ValueFrom . ResourceFieldRef . Resource , "limits.hugepages-" ) {
return true
}
}
}
}
return false
}
// usesMultipleHugePageResources returns true if the pod spec uses more than
// one size of hugepage
func usesMultipleHugePageResources ( podSpec * api . PodSpec ) bool {
hugePageResources := sets . NewString ( )
resourceSet := helper . ToPodResourcesSet ( podSpec )
for resourceStr := range resourceSet {
if v1helper . IsHugePageResourceName ( v1 . ResourceName ( resourceStr ) ) {
hugePageResources . Insert ( resourceStr )
}
}
return len ( hugePageResources ) > 1
}
2021-03-18 22:40:29 +00: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
}
// GetValidationOptionsFromPodSpecAndMeta returns validation options based on pod specs and metadata
func GetValidationOptionsFromPodSpecAndMeta ( podSpec , oldPodSpec * api . PodSpec , podMeta , oldPodMeta * metav1 . ObjectMeta ) apivalidation . PodValidationOptions {
2020-12-01 01:06:26 +00:00
// default pod validation options based on feature gate
opts := validation . PodValidationOptions {
// Allow multiple huge pages on pod create if feature is enabled
AllowMultipleHugePageResources : utilfeature . DefaultFeatureGate . Enabled ( features . HugePageStorageMediumSize ) ,
// Allow pod spec to use hugepages in downward API if feature is enabled
2021-03-18 22:40:29 +00:00
AllowDownwardAPIHugePages : utilfeature . DefaultFeatureGate . Enabled ( features . DownwardAPIHugePages ) ,
AllowInvalidPodDeletionCost : ! utilfeature . DefaultFeatureGate . Enabled ( features . PodDeletionCost ) ,
// Do not allow pod spec to use non-integer multiple of huge page unit size default
AllowIndivisibleHugePagesValues : false ,
}
if oldPodSpec != nil {
// if old spec used multiple huge page sizes, we must allow it
opts . AllowMultipleHugePageResources = opts . AllowMultipleHugePageResources || usesMultipleHugePageResources ( oldPodSpec )
// if old spec used hugepages in downward api, we must allow it
opts . AllowDownwardAPIHugePages = opts . AllowDownwardAPIHugePages || usesHugePagesInProjectedVolume ( oldPodSpec )
// determine if any container is using hugepages in env var
if ! opts . AllowDownwardAPIHugePages {
VisitContainers ( oldPodSpec , AllContainers , func ( c * api . Container , containerType ContainerType ) bool {
opts . AllowDownwardAPIHugePages = opts . AllowDownwardAPIHugePages || usesHugePagesInProjectedEnv ( * c )
return ! opts . AllowDownwardAPIHugePages
} )
}
// if old spec used non-integer multiple of huge page unit size, we must allow it
opts . AllowIndivisibleHugePagesValues = usesIndivisibleHugePagesValues ( oldPodSpec )
2020-12-01 01:06:26 +00:00
}
2021-03-18 22:40:29 +00: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-12-01 01:06:26 +00:00
return opts
}
// GetValidationOptionsFromPodTemplate will return pod validation options for specified template.
func GetValidationOptionsFromPodTemplate ( podTemplate , oldPodTemplate * api . PodTemplateSpec ) apivalidation . PodValidationOptions {
var newPodSpec , oldPodSpec * api . PodSpec
2021-03-18 22:40:29 +00:00
var newPodMeta , oldPodMeta * metav1 . ObjectMeta
2020-12-01 01:06:26 +00: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-03-18 22:40:29 +00:00
newPodMeta = & podTemplate . ObjectMeta
2020-12-01 01:06:26 +00:00
}
if oldPodTemplate != nil {
oldPodSpec = & oldPodTemplate . Spec
2021-03-18 22:40:29 +00:00
oldPodMeta = & oldPodTemplate . ObjectMeta
2020-12-01 01:06:26 +00:00
}
2021-03-18 22:40:29 +00:00
return GetValidationOptionsFromPodSpecAndMeta ( newPodSpec , oldPodSpec , newPodMeta , oldPodMeta )
2020-12-01 01:06:26 +00:00
}
2019-04-07 17:07:55 +00: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
podAnnotations map [ string ] string
oldPodSpec * api . PodSpec
oldPodAnnotations map [ string ] string
2019-09-27 21:51:53 +00:00
podStatus * api . PodStatus
oldPodStatus * api . PodStatus
2019-04-07 17:07:55 +00:00
)
if pod != nil {
podSpec = & pod . Spec
podAnnotations = pod . Annotations
2019-09-27 21:51:53 +00:00
podStatus = & pod . Status
2019-04-07 17:07:55 +00:00
}
if oldPod != nil {
oldPodSpec = & oldPod . Spec
oldPodAnnotations = oldPod . Annotations
2019-09-27 21:51:53 +00:00
oldPodStatus = & oldPod . Status
2019-04-07 17:07:55 +00:00
}
dropDisabledFields ( podSpec , podAnnotations , oldPodSpec , oldPodAnnotations )
2019-09-27 21:51:53 +00:00
dropPodStatusDisabledFields ( podStatus , oldPodStatus )
}
// dropPodStatusDisabledFields removes disabled fields from the pod status
func dropPodStatusDisabledFields ( podStatus * api . PodStatus , oldPodStatus * api . PodStatus ) {
// trim PodIPs down to only one entry (non dual stack).
if ! utilfeature . DefaultFeatureGate . Enabled ( features . IPv6DualStack ) &&
! multiplePodIPsInUse ( oldPodStatus ) {
if len ( podStatus . PodIPs ) != 0 {
podStatus . PodIPs = podStatus . PodIPs [ 0 : 1 ]
}
}
2019-04-07 17:07:55 +00: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 { }
}
if ! utilfeature . DefaultFeatureGate . Enabled ( features . AppArmor ) && ! appArmorInUse ( oldPodAnnotations ) {
for k := range podAnnotations {
2020-08-10 17:43:49 +00:00
if strings . HasPrefix ( k , v1 . AppArmorBetaContainerAnnotationKeyPrefix ) {
2019-04-07 17:07:55 +00:00
delete ( podAnnotations , k )
}
}
}
if ! utilfeature . DefaultFeatureGate . Enabled ( features . LocalStorageCapacityIsolation ) && ! emptyDirSizeLimitInUse ( oldPodSpec ) {
2019-01-12 04:58:27 +00:00
for i := range podSpec . Volumes {
if podSpec . Volumes [ i ] . EmptyDir != nil {
podSpec . Volumes [ i ] . EmptyDir . SizeLimit = nil
}
}
}
2019-04-07 17:07:55 +00:00
if ! utilfeature . DefaultFeatureGate . Enabled ( features . VolumeSubpath ) && ! subpathInUse ( oldPodSpec ) {
// drop subpath from the pod if the feature is disabled and the old spec did not specify subpaths
2020-03-26 21:07:15 +00:00
VisitContainers ( podSpec , AllContainers , func ( c * api . Container , containerType ContainerType ) bool {
2019-09-27 21:51:53 +00:00
for i := range c . VolumeMounts {
c . VolumeMounts [ i ] . SubPath = ""
2019-04-07 17:07:55 +00:00
}
2019-09-27 21:51:53 +00:00
return true
} )
}
if ! utilfeature . DefaultFeatureGate . Enabled ( features . EphemeralContainers ) && ! ephemeralContainersInUse ( oldPodSpec ) {
podSpec . EphemeralContainers = nil
2019-04-07 17:07:55 +00:00
}
2020-08-10 17:43:49 +00:00
if ! utilfeature . DefaultFeatureGate . Enabled ( features . VolumeSubpath ) && ! subpathExprInUse ( oldPodSpec ) {
// drop subpath env expansion from the pod if subpath feature is disabled and the old spec did not specify subpath env expansion
2020-03-26 21:07:15 +00:00
VisitContainers ( podSpec , AllContainers , func ( c * api . Container , containerType ContainerType ) bool {
2019-09-27 21:51:53 +00:00
for i := range c . VolumeMounts {
c . VolumeMounts [ i ] . SubPathExpr = ""
2019-04-07 17:07:55 +00:00
}
2019-09-27 21:51:53 +00:00
return true
} )
}
2021-03-18 22:40:29 +00:00
if ! utilfeature . DefaultFeatureGate . Enabled ( features . ProbeTerminationGracePeriod ) && ! probeGracePeriodInUse ( oldPodSpec ) {
// Set pod-level terminationGracePeriodSeconds to nil if the feature is disabled and it is not used
VisitContainers ( podSpec , AllContainers , func ( c * api . Container , containerType ContainerType ) bool {
if c . LivenessProbe != nil {
c . LivenessProbe . TerminationGracePeriodSeconds = nil
}
if c . StartupProbe != nil {
c . StartupProbe . TerminationGracePeriodSeconds = nil
}
// cannot be set for readiness probes
return true
} )
}
2019-01-12 04:58:27 +00:00
2020-03-26 21:07:15 +00:00
dropDisabledFSGroupFields ( podSpec , oldPodSpec )
2019-09-27 21:51:53 +00:00
if ! utilfeature . DefaultFeatureGate . Enabled ( features . PodOverhead ) && ! overheadInUse ( oldPodSpec ) {
// Set Overhead to nil only if the feature is disabled and it is not used
podSpec . Overhead = nil
}
2019-04-07 17:07:55 +00:00
dropDisabledProcMountField ( podSpec , oldPodSpec )
dropDisabledCSIVolumeSourceAlphaFields ( podSpec , oldPodSpec )
2020-08-10 17:43:49 +00:00
dropDisabledEphemeralVolumeSourceAlphaFields ( podSpec , oldPodSpec )
2019-04-07 17:07:55 +00:00
2019-08-30 18:33:25 +00:00
if ! utilfeature . DefaultFeatureGate . Enabled ( features . NonPreemptingPriority ) &&
! podPriorityInUse ( oldPodSpec ) {
// Set to nil pod's PreemptionPolicy fields if the feature is disabled and the old pod
// does not specify any values for these fields.
podSpec . PreemptionPolicy = nil
}
2019-09-27 21:51:53 +00:00
2020-08-10 17:43:49 +00:00
if ! utilfeature . DefaultFeatureGate . Enabled ( features . SetHostnameAsFQDN ) && ! setHostnameAsFQDNInUse ( oldPodSpec ) {
// Set SetHostnameAsFQDN to nil only if feature is disabled and it is not used
podSpec . SetHostnameAsFQDN = nil
2019-09-27 21:51:53 +00:00
}
2020-08-10 17:43:49 +00:00
2021-03-18 22:40:29 +00:00
dropDisabledPodAffinityTermFields ( podSpec , oldPodSpec )
2019-01-12 04:58:27 +00:00
}
2019-04-07 17:07:55 +00:00
// dropDisabledProcMountField removes disabled fields from PodSpec related
// to ProcMount only if it is not already used by the old spec
func dropDisabledProcMountField ( podSpec , oldPodSpec * api . PodSpec ) {
2019-08-30 18:33:25 +00:00
if ! utilfeature . DefaultFeatureGate . Enabled ( features . ProcMountType ) && ! procMountInUse ( oldPodSpec ) {
2019-04-07 17:07:55 +00:00
defaultProcMount := api . DefaultProcMount
2020-03-26 21:07:15 +00:00
VisitContainers ( podSpec , AllContainers , func ( c * api . Container , containerType ContainerType ) bool {
2019-09-27 21:51:53 +00: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
2019-04-07 17:07:55 +00:00
}
2019-09-27 21:51:53 +00:00
return true
} )
2019-01-12 04:58:27 +00:00
}
}
2020-03-26 21:07:15 +00:00
func dropDisabledFSGroupFields ( podSpec , oldPodSpec * api . PodSpec ) {
if ! utilfeature . DefaultFeatureGate . Enabled ( features . ConfigurableFSGroupPolicy ) && ! fsGroupPolicyInUse ( oldPodSpec ) {
// if oldPodSpec had no FSGroupChangePolicy set then we should prevent new pod from having this field
// if ConfigurableFSGroupPolicy feature is disabled
if podSpec . SecurityContext != nil {
podSpec . SecurityContext . FSGroupChangePolicy = nil
}
2019-01-12 04:58:27 +00:00
}
}
2019-04-07 17:07:55 +00:00
// dropDisabledCSIVolumeSourceAlphaFields removes disabled alpha fields from []CSIVolumeSource.
// This should be called from PrepareForCreate/PrepareForUpdate for all pod specs resources containing a CSIVolumeSource
func dropDisabledCSIVolumeSourceAlphaFields ( podSpec , oldPodSpec * api . PodSpec ) {
2019-08-30 18:33:25 +00:00
if ! utilfeature . DefaultFeatureGate . Enabled ( features . CSIInlineVolume ) && ! csiInUse ( oldPodSpec ) {
2019-04-07 17:07:55 +00:00
for i := range podSpec . Volumes {
podSpec . Volumes [ i ] . CSI = nil
}
}
}
2020-08-10 17:43:49 +00:00
// dropDisabledEphemeralVolumeSourceAlphaFields removes disabled alpha fields from []EphemeralVolumeSource.
// This should be called from PrepareForCreate/PrepareForUpdate for all pod specs resources containing a EphemeralVolumeSource
func dropDisabledEphemeralVolumeSourceAlphaFields ( podSpec , oldPodSpec * api . PodSpec ) {
2021-03-18 22:40:29 +00:00
if ! utilfeature . DefaultFeatureGate . Enabled ( features . GenericEphemeralVolume ) && ! ephemeralInUse ( oldPodSpec ) {
2020-08-10 17:43:49 +00:00
for i := range podSpec . Volumes {
podSpec . Volumes [ i ] . Ephemeral = nil
}
}
}
2021-03-18 22:40:29 +00:00
func dropPodAffinityTermNamespaceSelector ( terms [ ] api . PodAffinityTerm ) {
for i := range terms {
terms [ i ] . NamespaceSelector = nil
}
}
func dropWeightedPodAffinityTermNamespaceSelector ( terms [ ] api . WeightedPodAffinityTerm ) {
for i := range terms {
terms [ i ] . PodAffinityTerm . NamespaceSelector = nil
}
}
// dropDisabledPodAffinityTermFields removes disabled fields from PodSpec related
// to PodAffinityTerm only if it is not already used by the old spec
func dropDisabledPodAffinityTermFields ( podSpec , oldPodSpec * api . PodSpec ) {
if ! utilfeature . DefaultFeatureGate . Enabled ( features . PodAffinityNamespaceSelector ) &&
podSpec != nil && podSpec . Affinity != nil &&
! podAffinityNamespaceSelectorInUse ( oldPodSpec ) {
if podSpec . Affinity . PodAffinity != nil {
dropPodAffinityTermNamespaceSelector ( podSpec . Affinity . PodAffinity . RequiredDuringSchedulingIgnoredDuringExecution )
dropWeightedPodAffinityTermNamespaceSelector ( podSpec . Affinity . PodAffinity . PreferredDuringSchedulingIgnoredDuringExecution )
}
if podSpec . Affinity . PodAntiAffinity != nil {
dropPodAffinityTermNamespaceSelector ( podSpec . Affinity . PodAntiAffinity . RequiredDuringSchedulingIgnoredDuringExecution )
dropWeightedPodAffinityTermNamespaceSelector ( podSpec . Affinity . PodAntiAffinity . PreferredDuringSchedulingIgnoredDuringExecution )
}
}
}
func podAffinityNamespaceSelectorInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil || podSpec . Affinity == nil {
return false
}
if podSpec . Affinity . PodAffinity != nil {
for _ , t := range podSpec . Affinity . PodAffinity . RequiredDuringSchedulingIgnoredDuringExecution {
if t . NamespaceSelector != nil {
return true
}
}
for _ , t := range podSpec . Affinity . PodAffinity . PreferredDuringSchedulingIgnoredDuringExecution {
if t . PodAffinityTerm . NamespaceSelector != nil {
return true
}
}
}
if podSpec . Affinity . PodAntiAffinity != nil {
for _ , t := range podSpec . Affinity . PodAntiAffinity . RequiredDuringSchedulingIgnoredDuringExecution {
if t . NamespaceSelector != nil {
return true
}
}
for _ , t := range podSpec . Affinity . PodAntiAffinity . PreferredDuringSchedulingIgnoredDuringExecution {
if t . PodAffinityTerm . NamespaceSelector == nil {
return true
}
}
}
return false
}
2019-09-27 21:51:53 +00:00
func ephemeralContainersInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil {
return false
}
return len ( podSpec . EphemeralContainers ) > 0
}
2020-03-26 21:07:15 +00:00
func fsGroupPolicyInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil {
return false
}
securityContext := podSpec . SecurityContext
if securityContext != nil && securityContext . FSGroupChangePolicy != nil {
return true
}
return false
}
2019-04-07 17:07:55 +00:00
// subpathInUse returns true if the pod spec is non-nil and has a volume mount that makes use of the subPath feature
func subpathInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil {
return false
}
2019-09-27 21:51:53 +00:00
var inUse bool
2020-03-26 21:07:15 +00:00
VisitContainers ( podSpec , AllContainers , func ( c * api . Container , containerType ContainerType ) bool {
2019-09-27 21:51:53 +00:00
for i := range c . VolumeMounts {
if len ( c . VolumeMounts [ i ] . SubPath ) > 0 {
inUse = true
return false
2019-04-07 17:07:55 +00:00
}
}
2019-09-27 21:51:53 +00:00
return true
} )
return inUse
2019-04-07 17:07:55 +00:00
}
2019-09-27 21:51:53 +00:00
// overheadInUse returns true if the pod spec is non-nil and has Overhead set
func overheadInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil {
return false
}
if podSpec . Overhead != nil {
return true
}
return false
}
2019-07-14 07:58:54 +00:00
// procMountInUse returns true if the pod spec is non-nil and has a SecurityContext's ProcMount field set to a non-default value
2019-04-07 17:07:55 +00:00
func procMountInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil {
return false
}
2019-09-27 21:51:53 +00:00
var inUse bool
2020-03-26 21:07:15 +00:00
VisitContainers ( podSpec , AllContainers , func ( c * api . Container , containerType ContainerType ) bool {
2019-09-27 21:51:53 +00:00
if c . SecurityContext == nil || c . SecurityContext . ProcMount == nil {
return true
2019-04-07 17:07:55 +00:00
}
2019-09-27 21:51:53 +00:00
if * c . SecurityContext . ProcMount != api . DefaultProcMount {
inUse = true
return false
2019-04-07 17:07:55 +00:00
}
2019-09-27 21:51:53 +00:00
return true
} )
return inUse
2019-04-07 17:07:55 +00:00
}
// appArmorInUse returns true if the pod has apparmor related information
func appArmorInUse ( podAnnotations map [ string ] string ) bool {
for k := range podAnnotations {
2020-08-10 17:43:49 +00:00
if strings . HasPrefix ( k , v1 . AppArmorBetaContainerAnnotationKeyPrefix ) {
2019-04-07 17:07:55 +00:00
return true
}
}
return false
}
// podPriorityInUse returns true if the pod spec is non-nil and has Priority or PriorityClassName set.
func podPriorityInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil {
return false
}
if podSpec . Priority != nil || podSpec . PriorityClassName != "" {
return true
}
return false
}
2019-12-12 01:27:03 +00:00
// emptyDirSizeLimitInUse returns true if any pod's EmptyDir volumes use SizeLimit.
2019-04-07 17:07:55 +00:00
func emptyDirSizeLimitInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil {
return false
}
for i := range podSpec . Volumes {
if podSpec . Volumes [ i ] . EmptyDir != nil {
if podSpec . Volumes [ i ] . EmptyDir . SizeLimit != nil {
return true
}
}
}
return false
}
2021-03-18 22:40:29 +00:00
// subpathExprInUse returns true if the pod spec is non-nil and has a volume mount that makes use of the subPathExpr feature
func subpathExprInUse ( podSpec * api . PodSpec ) bool {
2019-04-07 17:07:55 +00:00
if podSpec == nil {
return false
}
2019-09-27 21:51:53 +00:00
var inUse bool
2020-03-26 21:07:15 +00:00
VisitContainers ( podSpec , AllContainers , func ( c * api . Container , containerType ContainerType ) bool {
2021-03-18 22:40:29 +00:00
for i := range c . VolumeMounts {
if len ( c . VolumeMounts [ i ] . SubPathExpr ) > 0 {
inUse = true
return false
}
2019-04-07 17:07:55 +00:00
}
2019-09-27 21:51:53 +00:00
return true
} )
return inUse
2019-04-07 17:07:55 +00:00
}
2021-03-18 22:40:29 +00:00
// probeGracePeriodInUse returns true if the pod spec is non-nil and has a probe that makes use
// of the probe-level terminationGracePeriodSeconds feature
func probeGracePeriodInUse ( podSpec * api . PodSpec ) bool {
2019-04-07 17:07:55 +00:00
if podSpec == nil {
return false
}
2019-09-27 21:51:53 +00:00
var inUse bool
2020-03-26 21:07:15 +00:00
VisitContainers ( podSpec , AllContainers , func ( c * api . Container , containerType ContainerType ) bool {
2021-03-18 22:40:29 +00:00
// cannot be set for readiness probes
if ( c . LivenessProbe != nil && c . LivenessProbe . TerminationGracePeriodSeconds != nil ) ||
( c . StartupProbe != nil && c . StartupProbe . TerminationGracePeriodSeconds != nil ) {
inUse = true
return false
2019-04-07 17:07:55 +00:00
}
2019-09-27 21:51:53 +00:00
return true
} )
return inUse
}
2019-04-07 17:07:55 +00:00
// csiInUse returns true if any pod's spec include inline CSI volumes.
func csiInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil {
return false
}
for i := range podSpec . Volumes {
if podSpec . Volumes [ i ] . CSI != nil {
return true
}
}
return false
}
2019-09-27 21:51:53 +00:00
2021-03-18 22:40:29 +00:00
// ephemeralInUse returns true if any pod's spec include inline CSI volumes.
func ephemeralInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil {
return false
}
for i := range podSpec . Volumes {
if podSpec . Volumes [ i ] . Ephemeral != nil {
return true
}
}
return false
}
2019-09-27 21:51:53 +00:00
// podPriorityInUse returns true if status is not nil and number of PodIPs is greater than one
func multiplePodIPsInUse ( podStatus * api . PodStatus ) bool {
if podStatus == nil {
return false
}
if len ( podStatus . PodIPs ) > 1 {
return true
}
return false
}
2020-08-10 17:43:49 +00:00
// setHostnameAsFQDNInUse returns true if any pod's spec defines setHostnameAsFQDN field.
func setHostnameAsFQDNInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil || podSpec . SetHostnameAsFQDN == nil {
return false
}
return * podSpec . SetHostnameAsFQDN
}
// SeccompAnnotationForField takes a pod seccomp profile field and returns the
// converted annotation value
func SeccompAnnotationForField ( field * api . SeccompProfile ) string {
// If only seccomp fields are specified, add the corresponding annotations.
// This ensures that the fields are enforced even if the node version
// trails the API version
switch field . Type {
case api . SeccompProfileTypeUnconfined :
return v1 . SeccompProfileNameUnconfined
case api . SeccompProfileTypeRuntimeDefault :
return v1 . SeccompProfileRuntimeDefault
case api . SeccompProfileTypeLocalhost :
if field . LocalhostProfile != nil {
return v1 . SeccompLocalhostProfileNamePrefix + * field . LocalhostProfile
}
}
// we can only reach this code path if the LocalhostProfile is nil but the
// provided field type is SeccompProfileTypeLocalhost or if an unrecognized
// type is specified
return ""
}
// SeccompFieldForAnnotation takes a pod annotation and returns the converted
// seccomp profile field.
func SeccompFieldForAnnotation ( annotation string ) * api . SeccompProfile {
// If only seccomp annotations are specified, copy the values into the
// corresponding fields. This ensures that existing applications continue
// to enforce seccomp, and prevents the kubelet from needing to resolve
// annotations & fields.
if annotation == v1 . SeccompProfileNameUnconfined {
return & api . SeccompProfile { Type : api . SeccompProfileTypeUnconfined }
}
if annotation == api . SeccompProfileRuntimeDefault || annotation == api . DeprecatedSeccompProfileDockerDefault {
return & api . SeccompProfile { Type : api . SeccompProfileTypeRuntimeDefault }
}
if strings . HasPrefix ( annotation , v1 . SeccompLocalhostProfileNamePrefix ) {
localhostProfile := strings . TrimPrefix ( annotation , v1 . SeccompLocalhostProfileNamePrefix )
if localhostProfile != "" {
return & api . SeccompProfile {
Type : api . SeccompProfileTypeLocalhost ,
LocalhostProfile : & localhostProfile ,
}
}
}
// we can only reach this code path if the localhostProfile name has a zero
// length or if the annotation has an unrecognized value
return nil
}