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"
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"
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 {
containerType := AllContainers
if ! utilfeature . DefaultFeatureGate . Enabled ( features . EphemeralContainers ) {
containerType &= ^ EphemeralContainers
}
return containerType
}
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
2020-11-06 14:22:53 -05: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
}
2021-05-28 01:38:42 -04:00
// hasSysctlsWithSlashNames returns true if the sysctl name contains a slash, otherwise it returns false
func hasSysctlsWithSlashNames ( podSpec * api . PodSpec ) bool {
if podSpec . SecurityContext == nil {
return false
}
securityContext := podSpec . SecurityContext
for _ , s := range securityContext . Sysctls {
if strings . Contains ( s . Name , "/" ) {
return true
}
}
return false
}
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
}
2021-05-24 07:58:59 -04:00
// haveSameExpandedDNSConfig returns true if the oldPodSpec already had
// ExpandedDNSConfig and podSpec has the same DNSConfig
func haveSameExpandedDNSConfig ( podSpec , oldPodSpec * api . PodSpec ) bool {
if oldPodSpec == nil || oldPodSpec . DNSConfig == nil {
return false
}
if podSpec == nil || podSpec . DNSConfig == nil {
return false
}
if len ( oldPodSpec . DNSConfig . Searches ) <= apivalidation . MaxDNSSearchPathsLegacy &&
len ( strings . Join ( oldPodSpec . DNSConfig . Searches , " " ) ) <= apivalidation . MaxDNSSearchListCharsLegacy {
// didn't have ExpandedDNSConfig
return false
}
if len ( oldPodSpec . DNSConfig . Searches ) != len ( podSpec . DNSConfig . Searches ) {
// updates DNSConfig
return false
}
for i , oldSearch := range oldPodSpec . DNSConfig . Searches {
if podSpec . DNSConfig . Searches [ i ] != oldSearch {
// updates DNSConfig
return false
}
}
return true
}
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 {
2020-11-06 14:22:53 -05:00
// Allow pod spec to use hugepages in downward API if feature is enabled
2021-02-17 15:39:42 -05:00
AllowDownwardAPIHugePages : utilfeature . DefaultFeatureGate . Enabled ( features . DownwardAPIHugePages ) ,
AllowInvalidPodDeletionCost : ! utilfeature . DefaultFeatureGate . Enabled ( features . PodDeletionCost ) ,
2021-01-28 02:32:51 -05:00
// Do not allow pod spec to use non-integer multiple of huge page unit size default
AllowIndivisibleHugePagesValues : false ,
2021-02-26 15:26:01 -05:00
AllowWindowsHostProcessField : utilfeature . DefaultFeatureGate . Enabled ( features . WindowsHostProcessContainers ) ,
2021-05-24 07:10:02 -04:00
// Allow pod spec with expanded DNS configuration
2021-05-24 07:58:59 -04:00
AllowExpandedDNSConfig : utilfeature . DefaultFeatureGate . Enabled ( features . ExpandedDNSConfig ) || haveSameExpandedDNSConfig ( podSpec , oldPodSpec ) ,
2021-10-20 15:40:49 -04:00
// Allow pod spec to use OS field
AllowOSField : utilfeature . DefaultFeatureGate . Enabled ( features . IdentifyPodOS ) ,
2021-05-28 01:38:42 -04:00
// The default sysctl value does not contain a forward slash, and in 1.24 we intend to relax this to be true by default
AllowSysctlRegexContainSlash : false ,
2020-11-06 14:22:53 -05:00
}
2021-02-17 15:39:42 -05:00
if oldPodSpec != nil {
// 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
} )
}
2021-02-26 15:26:01 -05:00
// if old spec has Windows Host Process fields set, we must allow it
opts . AllowWindowsHostProcessField = opts . AllowWindowsHostProcessField || setsWindowsHostProcess ( oldPodSpec )
2021-01-28 02:32:51 -05:00
2021-10-20 15:40:49 -04:00
// if old spec has OS field set, we must allow it
opts . AllowOSField = opts . AllowOSField || oldPodSpec . OS != nil
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
// if old spec used use relaxed validation for Update requests where the existing object's sysctl contains a slash, we must allow it.
opts . AllowSysctlRegexContainSlash = hasSysctlsWithSlashNames ( oldPodSpec )
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
}
// 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
podAnnotations map [ string ] string
oldPodSpec * api . PodSpec
oldPodAnnotations map [ string ] string
)
if pod != nil {
podSpec = & pod . Spec
podAnnotations = pod . Annotations
}
if oldPod != nil {
oldPodSpec = & oldPod . Spec
oldPodAnnotations = oldPod . Annotations
}
dropDisabledFields ( podSpec , podAnnotations , oldPodSpec , oldPodAnnotations )
}
// 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 )
}
}
}
2018-12-27 10:30:37 -05:00
if ! utilfeature . DefaultFeatureGate . Enabled ( features . LocalStorageCapacityIsolation ) && ! emptyDirSizeLimitInUse ( oldPodSpec ) {
2017-08-18 16:18:53 -04:00
for i := range podSpec . Volumes {
if podSpec . Volumes [ i ] . EmptyDir != nil {
podSpec . Volumes [ i ] . EmptyDir . SizeLimit = nil
}
}
}
2017-08-09 13:51:46 -04:00
2019-08-02 20:00:01 -04:00
if ! utilfeature . DefaultFeatureGate . Enabled ( features . EphemeralContainers ) && ! ephemeralContainersInUse ( oldPodSpec ) {
2018-08-09 09:24:23 -04:00
podSpec . EphemeralContainers = nil
}
2018-12-18 10:10:56 -05:00
2021-03-09 13:20:17 -05: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-04-26 15:24:11 -04: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
}
2018-12-17 12:49:29 -05:00
dropDisabledProcMountField ( podSpec , oldPodSpec )
2018-08-14 17:00:25 -04:00
dropDisabledCSIVolumeSourceAlphaFields ( podSpec , oldPodSpec )
2019-05-30 18:28:21 -04: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
}
2021-10-20 15:40:49 -04:00
if ! utilfeature . DefaultFeatureGate . Enabled ( features . IdentifyPodOS ) && ! podOSInUse ( oldPodSpec ) {
podSpec . OS = nil
}
2020-06-01 16:48:33 -04:00
2021-01-26 14:28:35 -05:00
dropDisabledPodAffinityTermFields ( podSpec , oldPodSpec )
2017-05-13 02:29:25 -04:00
}
2021-10-20 15:40:49 -04:00
// podOSInUse returns true if the pod spec is non-nil and has OS field set
func podOSInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil {
return false
}
if podSpec . OS != nil {
return true
}
return false
}
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
}
}
2018-08-14 17:00:25 -04: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 ) {
if ! utilfeature . DefaultFeatureGate . Enabled ( features . CSIInlineVolume ) && ! csiInUse ( oldPodSpec ) {
for i := range podSpec . Volumes {
podSpec . Volumes [ i ] . CSI = nil
}
}
}
2021-01-26 14:28:35 -05: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-08-02 20:00:01 -04:00
func ephemeralContainersInUse ( podSpec * api . PodSpec ) bool {
if podSpec == nil {
return false
}
return len ( podSpec . EphemeralContainers ) > 0
}
2019-04-26 15:24:11 -04: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-04-26 13:08:52 -04:00
}
2019-04-26 15:24:11 -04:00
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
}
2018-12-19 14:55:34 -05:00
// 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
}
2018-12-27 10:30:37 -05:00
2019-08-26 02:09:04 -04:00
// emptyDirSizeLimitInUse returns true if any pod's EmptyDir volumes use SizeLimit.
2018-12-27 10:30:37 -05: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
}
2018-12-27 16:41:57 -05:00
2021-03-09 13:20:17 -05: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 {
if podSpec == nil {
return false
}
var inUse bool
VisitContainers ( podSpec , AllContainers , func ( c * api . Container , containerType ContainerType ) bool {
// cannot be set for readiness probes
if ( c . LivenessProbe != nil && c . LivenessProbe . TerminationGracePeriodSeconds != nil ) ||
2021-03-11 19:40:40 -05:00
( c . StartupProbe != nil && c . StartupProbe . TerminationGracePeriodSeconds != nil ) {
2021-03-09 13:20:17 -05:00
inUse = true
return false
}
return true
} )
return inUse
}
2018-08-14 17:00:25 -04: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-06-21 13:42:53 -04:00
2020-07-07 05:10:29 -04:00
// 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
}
2021-02-26 15:26:01 -05:00
// setsWindowsHostProcess returns true if WindowsOptions.HostProcess is set (true or false)
// anywhere in the pod spec.
func setsWindowsHostProcess ( podSpec * api . PodSpec ) bool {
if podSpec == nil {
return false
}
// Check Pod's WindowsOptions.HostProcess
if podSpec . SecurityContext != nil && podSpec . SecurityContext . WindowsOptions != nil && podSpec . SecurityContext . WindowsOptions . HostProcess != nil {
return true
}
// Check WindowsOptions.HostProcess for each container
inUse := false
VisitContainers ( podSpec , AllContainers , func ( c * api . Container , containerType ContainerType ) bool {
if c . SecurityContext != nil && c . SecurityContext . WindowsOptions != nil && c . SecurityContext . WindowsOptions . HostProcess != nil {
inUse = true
return false
}
return true
} )
return inUse
}