文件列表具体为:
- pkg\api\pod\util.go
文件列表具体为:
ContainerType 类型定义,有3个类型:
// 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)
AllFeatureEnabledContainers() 方法 返回一个 ContainerType mask,其中包括所有容器类型,但受 feature gate 保护的容器类型除外。
// AllContainers specifies that all containers be visited
const AllContainers ContainerType = (InitContainers | Containers | EphemeralContainers)
// AllFeatureEnabledContainers returns a ContainerType mask which includes all container
// types except for the ones guarded by feature gate.
func AllFeatureEnabledContainers() ContainerType {
return AllContainers
}
备注: 没有什么 feature gate 啊
ContainerVisitor 类型定义:
// ContainerVisitor is called with each container spec, and returns true
// if visiting should continue.
type ContainerVisitor func(container *api.Container, containerType ContainerType) (shouldContinue bool)
每个容器规格都会调用 ContainerVisitor,如果应该继续访问,则返回 true 。
VisitContainers() 方法使用指向给定 pod spec 中每个容器规格的指针调用访问者函数,容器规格的类型在掩码中设置。
如果 visitor 返回 false,则表示访问短路。如果访问完成,VisitContainers 返回 true;如果访问短路,则返回 false。
// 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,
// visiting is short-circuited. VisitContainers returns true if visiting completes,
// false if visiting was short-circuited.
func VisitContainers(podSpec *api.PodSpec, mask ContainerType, visitor ContainerVisitor) bool {
// 如果 ContainerType 是 InitContainers
if mask&InitContainers != 0 {
// 对于每个 InitContainer
for i := range podSpec.InitContainers {
//调用 visitor() 方法
if !visitor(&podSpec.InitContainers[i], InitContainers) {
// 只要任何一个返回 false,则中断并返回 false
return false
}
}
}
// 如果 ContainerType 是普通 Containers
if mask&Containers != 0 {
// 对于每个 普通 Container
for i := range podSpec.Containers {
if !visitor(&podSpec.Containers[i], Containers) {
return false
}
}
}
// 如果 ContainerType 是普通 EphemeralContainers
if mask&EphemeralContainers != 0 {
// 对于每个 EphemeralContainers
for i := range podSpec.EphemeralContainers {
if !visitor((*api.Container)(&podSpec.EphemeralContainers[i].EphemeralContainerCommon), EphemeralContainers) {
return false
}
}
}
return true
}
Visitor() 方法会被每个对象名称调用,如果要继续访问,则返回 true
// Visitor is called with each object name, and returns true if visiting should continue
type Visitor func(name string) (shouldContinue bool)
skipEmptyNames 检查 name 是否为空,如果是,则跳过 visitor() 方法的调用,直接返回 true。
func skipEmptyNames(visitor Visitor) Visitor {
return func(name string) bool {
if len(name) == 0 {
// continue visiting
// 相当于如果 name 为空,则跳过 visitor() 方法直接返回 true
return true
}
// delegate to visitor
return visitor(name)
}
}
VisitPodSecretNames() 方法 会调用 visit() 函数,并输入 pod spec 引用的每个 secret 的名称。
如果 visitor 返回 false,则访问将被短路。
不会访问传递引用(例如 pod -> pvc -> pv -> secret)。
如果访问完成,则返回 true;如果访问短路,则返回 false。
// 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.
func VisitPodSecretNames(pod *api.Pod, visitor Visitor, containerType ContainerType) bool {
// 跳过空名称
visitor = skipEmptyNames(visitor)
// 对于每个 ImagePullSecrets
for _, reference := range pod.Spec.ImagePullSecrets {
if !visitor(reference.Name) {
return false
}
}
// 调用 VisitContainers() 方法,检查 container 的 secret
// 传入 ContainerVisitor 方法实现为调用 visitContainerSecretNames() 方法
VisitContainers(&pod.Spec, containerType, func(c *api.Container, containerType ContainerType) bool {
return visitContainerSecretNames(c, visitor)
})
// 对于每个 Volume,检查 Volume 的 secret
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
}
case source.CSI != nil:
if source.CSI.NodePublishSecretRef != nil && !visitor(source.CSI.NodePublishSecretRef.Name) {
return false
}
}
}
return true
}
visitContainerSecretNames() 方法游历 container.EnvFrom 和 container.Env,检查 SecretRef 和 SecretKeyRef 的 name:
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() 方法 会调用 visitor 函数,并输入 pod spec 引用的每个 config map 的名称。
如果 visitor 返回 false,则访问将被短路。
不会访问传递引用(例如 pod -> pvc -> pv -> secret)。
如果访问完成,则返回 true;如果访问短路,则返回 false。
func VisitPodConfigmapNames(pod *api.Pod, visitor Visitor, containerType ContainerType) bool {
visitor = skipEmptyNames(visitor)
VisitContainers(&pod.Spec, containerType, func(c *api.Container, containerType ContainerType) bool {
return visitContainerConfigmapNames(c, visitor)
})
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
}
visitContainerConfigmapNames() 方法游历 container.EnvFrom 和 container.Env,检查 ConfigMapRef 和 ConfigMapKeyRef 的 name:
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
}
如果 pod ready 则返回 true
// IsPodReady returns true if a pod is ready; false otherwise.
func IsPodReady(pod *api.Pod) bool {
return IsPodReadyConditionTrue(pod.Status)
}
IsPodReadyConditionTrue 检查 pod 的 PodStatus, 如果为 ConditionTrue 则返回 true:
// 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() 方法 从给定状态中提取 pod 就绪条件并返回。如果条件不存在,则返回 nil。
// 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() 方法从给定的状态中提取所提供的 condition 并返回。
如果 condition 不存在,则返回 nil 和-1,
如果 condition 存在,则返回所定位 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 {
// 如果 pod 的 status 为空
return -1, nil
}
// 游历 status.Conditions
for i := range status.Conditions {
// 如果等于指定的 conditionType
if status.Conditions[i].Type == conditionType {
// 返回 index 和 对应 conditionType 的 condition
// 这里只会找到的第一个符合 conditionType 要求的 condition
return i, &status.Conditions[i]
}
}
return -1, nil
}
UpdatePodCondition 更新现有 pod condition 或创建新的 pod condition。
如果状态已更改,则将 LastTransitionTime 设置为 now。
如果 pod condition 已更改或已添加,则返回 true。
// 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 {
// 设置 LastTransitionTime 为当前时间
condition.LastTransitionTime = metav1.Now()
// Try to find this pod condition.
conditionIndex, oldCondition := GetPodCondition(status, condition.Type)
//如果没有找到,说明之前没有设置这个类型的 condition
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 的 status 和 oldCondition 的 status 相同,则将 LastTransitionTime 设置为 oldCondition 的 LastTransitionTime
condition.LastTransitionTime = oldCondition.LastTransitionTime
}
// 检查传入的 condition 和现有的 conditon 是否完全等同
isEqual := condition.Status == oldCondition.Status &&
condition.Reason == oldCondition.Reason &&
condition.Message == oldCondition.Message &&
condition.LastProbeTime.Equal(&oldCondition.LastProbeTime) &&
condition.LastTransitionTime.Equal(&oldCondition.LastTransitionTime)
// 更新 condition
status.Conditions[conditionIndex] = *condition
// Return true if one of the fields have changed.
return !isEqual
}
如果其中一个容器使用非整数倍的 huge page 单位大小,则 usesIndivisibleHugePagesValues() 方法 返回 true
// usesIndivisibleHugePagesValues returns true if the one of the containers uses non-integer multiple
// of huge page unit size
func usesIndivisibleHugePagesValues(podSpec *api.PodSpec) bool {
// 初始化一个布尔变量,用于记录是否找到非整数倍的 huge page 单位大小
foundIndivisibleHugePagesValue := false
// 遍历 podSpec 中的所有容器
VisitContainers(podSpec, AllContainers, func(c *api.Container, containerType ContainerType) bool {
// 检查容器是否使用非整数倍的 huge page 单位大小
if checkContainerUseIndivisibleHugePagesValues(*c) {
// 如果找到,则将 foundIndivisibleHugePagesValue 设置为 true
foundIndivisibleHugePagesValue = true
}
// 如果还没有找到非整数倍的 huge page 单位大小,则继续遍历
return !foundIndivisibleHugePagesValue // continue visiting if we haven't seen an invalid value yet
})
// 如果找到非整数倍的 huge page 单位大小,则返回 true
if foundIndivisibleHugePagesValue {
return true
}
// 游历检查 pod spec 中的 Overhead
for resourceName, quantity := range podSpec.Overhead {
if helper.IsHugePageResourceName(resourceName) {
if !helper.IsHugePageResourceValueDivisible(resourceName, quantity) {
return true
}
}
}
return false
}
checkContainerUseIndivisibleHugePagesValues() 方法
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
}
hasInvalidTopologySpreadConstraintLabelSelector 如果 spec.TopologySpreadConstraints 有任何标签选择器无效的条目,则返回 true
// 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
}
TopologySpreadConstraints = 拓扑扩展限制
hasNonLocalProjectedTokenPath 如果 spec.Volumes 有任何条目具有非本地投影标记路径(non-local projected token path),则返回 true
// hasNonLocalProjectedTokenPath return true if spec.Volumes have any entry with non-local projected token path
func hasNonLocalProjectedTokenPath(spec *api.PodSpec) bool {
for _, volume := range spec.Volumes {
if volume.Projected != nil {
for _, source := range volume.Projected.Sources {
if source.ServiceAccountToken == nil {
continue
}
errs := apivalidation.ValidateLocalNonReservedPath(source.ServiceAccountToken.Path, nil)
if len(errs) != 0 {
return true
}
}
}
}
return false
}
GetValidationOptionsFromPodSpecAndMeta() 方法 根据 pod spec和元数据返回验证选项:
// GetValidationOptionsFromPodSpecAndMeta returns validation options based on pod specs and metadata
func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, podMeta, oldPodMeta *metav1.ObjectMeta) apivalidation.PodValidationOptions {
// 默认的 pod 验证选项基于 feature gate
opts := apivalidation.PodValidationOptions{
AllowInvalidPodDeletionCost: !utilfeature.DefaultFeatureGate.Enabled(features.PodDeletionCost),
// Do not allow pod spec to use non-integer multiple of huge page unit size default
AllowIndivisibleHugePagesValues: false,
AllowInvalidLabelValueInSelector: false,
AllowInvalidTopologySpreadConstraintLabelSelector: false,
AllowNamespacedSysctlsForHostNetAndHostIPC: false,
AllowNonLocalProjectedTokenPath: false,
AllowPodLifecycleSleepActionZeroValue: utilfeature.DefaultFeatureGate.Enabled(features.PodLifecycleSleepActionAllowZero),
PodLevelResourcesEnabled: utilfeature.DefaultFeatureGate.Enabled(features.PodLevelResources),
AllowInvalidLabelValueInRequiredNodeAffinity: false,
AllowSidecarResizePolicy: utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling),
}
// 如果旧的 spec 使用宽松的验证或启用了 RelaxedEnvironmentVariableValidation feature gate,
// 我们必须允许它
opts.AllowRelaxedEnvironmentVariableValidation = useRelaxedEnvironmentVariableValidation(podSpec, oldPodSpec)
opts.AllowRelaxedDNSSearchValidation = useRelaxedDNSSearchValidation(oldPodSpec)
opts.AllowOnlyRecursiveSELinuxChangePolicy = useOnlyRecursiveSELinuxChangePolicy(oldPodSpec)
if oldPodSpec != nil {
// 如果旧的 spec 使用非整数倍的 huge page 单位大小,我们必须允许它
opts.AllowIndivisibleHugePagesValues = usesIndivisibleHugePagesValues(oldPodSpec)
opts.AllowInvalidLabelValueInSelector = hasInvalidLabelValueInAffinitySelector(oldPodSpec)
opts.AllowInvalidLabelValueInRequiredNodeAffinity = hasInvalidLabelValueInRequiredNodeAffinity(oldPodSpec)
// 如果旧的 spec 有无效的标签选择器在 topologySpreadConstraint,我们必须允许它
opts.AllowInvalidTopologySpreadConstraintLabelSelector = hasInvalidTopologySpreadConstraintLabelSelector(oldPodSpec)
// 如果旧的 spec 有无效的投影标记卷路径,我们必须允许它
opts.AllowNonLocalProjectedTokenPath = hasNonLocalProjectedTokenPath(oldPodSpec)
// 如果旧的 spec 有无效的 sysctl 与 hostNet 或 hostIPC,我们必须允许它在更新时
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
}
}
}
opts.AllowPodLifecycleSleepActionZeroValue = opts.AllowPodLifecycleSleepActionZeroValue || podLifecycleSleepActionZeroValueInUse(oldPodSpec)
// 如果旧的 pod 有在可重启的 init 容器上设置的 resize 策略,我们必须允许它
opts.AllowSidecarResizePolicy = opts.AllowSidecarResizePolicy || hasRestartableInitContainerResizePolicy(oldPodSpec)
}
if oldPodMeta != nil && !opts.AllowInvalidPodDeletionCost {
// 这是一个更新,所以只验证现有的对象是否有效
_, err := helper.GetDeletionCostFromPodAnnotations(oldPodMeta.Annotations)
opts.AllowInvalidPodDeletionCost = err != nil
}
return opts
}
useRelaxedEnvironmentVariableValidation() 方法, 如果旧的 spec 使用宽松的验证或启用了 RelaxedEnvironmentVariableValidation feature gate,则返回 true
func useRelaxedEnvironmentVariableValidation(podSpec, oldPodSpec *api.PodSpec) bool {
if utilfeature.DefaultFeatureGate.Enabled(features.RelaxedEnvironmentVariableValidation) {
return true
}
var oldPodEnvVarNames, podEnvVarNames sets.Set[string]
if oldPodSpec != nil {
oldPodEnvVarNames = gatherPodEnvVarNames(oldPodSpec)
}
if podSpec != nil {
podEnvVarNames = gatherPodEnvVarNames(podSpec)
}
for env := range podEnvVarNames {
if relaxedEnvVarUsed(env, oldPodEnvVarNames) {
return true
}
}
return false
}
useRelaxedDNSSearchValidation() 方法, 如果旧的 spec 使用宽松的验证或启用了 RelaxedDNSSearchValidation feature gate,则返回 true
func useRelaxedDNSSearchValidation(oldPodSpec *api.PodSpec) bool {
// Return true early if feature gate is enabled
if utilfeature.DefaultFeatureGate.Enabled(features.RelaxedDNSSearchValidation) {
return true
}
// Return false early if there is no DNSConfig or Searches.
if oldPodSpec == nil || oldPodSpec.DNSConfig == nil || oldPodSpec.DNSConfig.Searches == nil {
return false
}
return hasDotOrUnderscore(oldPodSpec.DNSConfig.Searches)
}
hasDotOrUnderscore() 方法检查是否存在点或下划线:
// Helper function to check if any domain is a dot or contains an underscore.
func hasDotOrUnderscore(searches []string) bool {
for _, domain := range searches {
if domain == "." || strings.Contains(domain, "_") {
return true
}
}
return false
}
gatherPodEnvVarNames() 方法收集 pod 环境变量名称:
func gatherPodEnvVarNames(podSpec *api.PodSpec) sets.Set[string] {
podEnvVarNames := sets.Set[string]{}
for _, c := range podSpec.Containers {
for _, env := range c.Env {
podEnvVarNames.Insert(env.Name)
}
for _, env := range c.EnvFrom {
podEnvVarNames.Insert(env.Prefix)
}
}
for _, c := range podSpec.InitContainers {
for _, env := range c.Env {
podEnvVarNames.Insert(env.Name)
}
for _, env := range c.EnvFrom {
podEnvVarNames.Insert(env.Prefix)
}
}
for _, c := range podSpec.EphemeralContainers {
for _, env := range c.Env {
podEnvVarNames.Insert(env.Name)
}
for _, env := range c.EnvFrom {
podEnvVarNames.Insert(env.Prefix)
}
}
return podEnvVarNames
}
relaxedEnvVarUsed() 检查是否使用宽松的环境变量名称:
func relaxedEnvVarUsed(name string, oldPodEnvVarNames sets.Set[string]) bool {
// A length of 0 means this is not an update request,
// or the old pod does not exist in the env.
// We will let the feature gate decide whether to use relaxed rules.
if oldPodEnvVarNames.Len() == 0 {
return false
}
if len(validation.IsEnvVarName(name)) == 0 || len(validation.IsRelaxedEnvVarName(name)) != 0 {
// It's either a valid name by strict rules or an invalid name under relaxed rules.
// Either way, we'll use strict rules to validate.
return false
}
// The name in question failed strict rules but passed relaxed rules.
if oldPodEnvVarNames.Has(name) {
// This relaxed-rules name was already in use.
return true
}
return false
}
GetValidationOptionsFromPodTemplate() 方法 将返回指定模板的 pod 验证选项。
// GetValidationOptionsFromPodTemplate will return pod validation options for specified template.
func GetValidationOptionsFromPodTemplate(podTemplate, oldPodTemplate *api.PodTemplateSpec) apivalidation.PodValidationOptions {
var newPodSpec, oldPodSpec *api.PodSpec
var newPodMeta, oldPodMeta *metav1.ObjectMeta
// 我们必须小心这里关于 nil 指针的问题
// 特别是 replication controller 容易传递 nil
if podTemplate != nil {
newPodSpec = &podTemplate.Spec
newPodMeta = &podTemplate.ObjectMeta
}
if oldPodTemplate != nil {
oldPodSpec = &oldPodTemplate.Spec
oldPodMeta = &oldPodTemplate.ObjectMeta
}
return GetValidationOptionsFromPodSpecAndMeta(newPodSpec, oldPodSpec, newPodMeta, oldPodMeta)
}
DropDisabledTemplateFields() 方法 删除 pod 模板元数据和规范中禁用的字段。
对于包含 PodTemplateSpec 的所有资源,应在 PrepareForCreate/PrepareForUpdate 中调用此功能。
// 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)
}
dropDisabledFields() 方法 会删除 pod 元数据和规范中禁用的字段。
// 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{}
}
// 如果 feature 被禁用且未在 use,则删除 hostUsers 字段。
if !utilfeature.DefaultFeatureGate.Enabled(features.UserNamespacesSupport) && !hostUsersInUse(oldPodSpec) {
// 仅在 SecurityContext 不为 nil 时删除 podSpec 中的字段。
// 如果为 nil,则不需要设置 hostUsers=nil(它也将为 nil)。
if podSpec.SecurityContext != nil {
podSpec.SecurityContext.HostUsers = nil
}
}
// 如果 feature 被禁用且未在 use,则删除 SupplementalGroupsPolicy 字段。
if !utilfeature.DefaultFeatureGate.Enabled(features.SupplementalGroupsPolicy) && !supplementalGroupsPolicyInUse(oldPodSpec) {
// 仅在 SecurityContext 不为 nil 时删除 podSpec 中的字段。
// 如果为 nil,则不需要设置 supplementalGroupsPolicy=nil(它也将为 nil)。
if podSpec.SecurityContext != nil {
podSpec.SecurityContext.SupplementalGroupsPolicy = nil
}
}
dropDisabledPodLevelResources(podSpec, oldPodSpec)
dropDisabledProcMountField(podSpec, oldPodSpec)
dropDisabledNodeInclusionPolicyFields(podSpec, oldPodSpec)
dropDisabledMatchLabelKeysFieldInTopologySpread(podSpec, oldPodSpec)
dropDisabledMatchLabelKeysFieldInPodAffinity(podSpec, oldPodSpec)
dropDisabledDynamicResourceAllocationFields(podSpec, oldPodSpec)
dropDisabledClusterTrustBundleProjection(podSpec, oldPodSpec)
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
}
}
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.
}
if !utilfeature.DefaultFeatureGate.Enabled(features.RecursiveReadOnlyMounts) && !rroInUse(oldPodSpec) {
for i := range podSpec.Containers {
for j := range podSpec.Containers[i].VolumeMounts {
podSpec.Containers[i].VolumeMounts[j].RecursiveReadOnly = nil
}
}
for i := range podSpec.InitContainers {
for j := range podSpec.InitContainers[i].VolumeMounts {
podSpec.InitContainers[i].VolumeMounts[j].RecursiveReadOnly = nil
}
}
for i := range podSpec.EphemeralContainers {
for j := range podSpec.EphemeralContainers[i].VolumeMounts {
podSpec.EphemeralContainers[i].VolumeMounts[j].RecursiveReadOnly = nil
}
}
}
dropPodLifecycleSleepAction(podSpec, oldPodSpec)
dropImageVolumes(podSpec, oldPodSpec)
dropSELinuxChangePolicy(podSpec, oldPodSpec)
dropContainerStopSignals(podSpec, oldPodSpec)
}
DropDisabledPodFields 删除 Pod 元数据和规范中禁用的字段。
对于包含 Pod 的所有资源,应在 PrepareForCreate/PrepareForUpdate 中调用此功能。
// 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
podStatus *api.PodStatus
podAnnotations map[string]string
oldPodSpec *api.PodSpec
oldPodStatus *api.PodStatus
oldPodAnnotations map[string]string
)
if pod != nil {
podSpec = &pod.Spec
podStatus = &pod.Status
podAnnotations = pod.Annotations
}
if oldPod != nil {
oldPodSpec = &oldPod.Spec
oldPodStatus = &oldPod.Status
oldPodAnnotations = oldPod.Annotations
}
dropDisabledFields(podSpec, podAnnotations, oldPodSpec, oldPodAnnotations)
dropDisabledPodStatusFields(podStatus, oldPodStatus, podSpec, oldPodSpec)
}
dropDisabledPodStatusFields() 方法 删除 pod 状态中已禁用的字段
// dropDisabledPodStatusFields removes disabled fields from the pod status
func dropDisabledPodStatusFields(podStatus, oldPodStatus *api.PodStatus, podSpec, oldPodSpec *api.PodSpec) {
// 新的状态总是非 nil
if podStatus == nil {
podStatus = &api.PodStatus{}
}
if !utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) && !inPlacePodVerticalScalingInUse(oldPodSpec) {
// 删除 Resources 字段
dropResourcesField := func(csl []api.ContainerStatus) {
for i := range csl {
csl[i].Resources = nil
}
}
dropResourcesField(podStatus.ContainerStatuses)
dropResourcesField(podStatus.InitContainerStatuses)
dropResourcesField(podStatus.EphemeralContainerStatuses)
// 删除 AllocatedResources 字段
dropAllocatedResourcesField := func(csl []api.ContainerStatus) {
for i := range csl {
csl[i].AllocatedResources = nil
}
}
dropAllocatedResourcesField(podStatus.ContainerStatuses)
dropAllocatedResourcesField(podStatus.InitContainerStatuses)
dropAllocatedResourcesField(podStatus.EphemeralContainerStatuses)
}
if !utilfeature.DefaultFeatureGate.Enabled(features.DynamicResourceAllocation) && !dynamicResourceAllocationInUse(oldPodSpec) {
podStatus.ResourceClaimStatuses = nil
}
if !utilfeature.DefaultFeatureGate.Enabled(features.RecursiveReadOnlyMounts) && !rroInUse(oldPodSpec) {
for i := range podStatus.ContainerStatuses {
podStatus.ContainerStatuses[i].VolumeMounts = nil
}
for i := range podStatus.InitContainerStatuses {
podStatus.InitContainerStatuses[i].VolumeMounts = nil
}
for i := range podStatus.EphemeralContainerStatuses {
podStatus.EphemeralContainerStatuses[i].VolumeMounts = nil
}
}
if !utilfeature.DefaultFeatureGate.Enabled(features.ResourceHealthStatus) {
setAllocatedResourcesStatusToNil := func(csl []api.ContainerStatus) {
for i := range csl {
csl[i].AllocatedResourcesStatus = nil
}
}
setAllocatedResourcesStatusToNil(podStatus.ContainerStatuses)
setAllocatedResourcesStatusToNil(podStatus.InitContainerStatuses)
setAllocatedResourcesStatusToNil(podStatus.EphemeralContainerStatuses)
}
// drop ContainerStatus.User field to empty (disable SupplementalGroupsPolicy)
if !utilfeature.DefaultFeatureGate.Enabled(features.SupplementalGroupsPolicy) && !supplementalGroupsPolicyInUse(oldPodSpec) {
dropUserField := func(csl []api.ContainerStatus) {
for i := range csl {
csl[i].User = nil
}
}
dropUserField(podStatus.InitContainerStatuses)
dropUserField(podStatus.ContainerStatuses)
dropUserField(podStatus.EphemeralContainerStatuses)
}
if !utilfeature.DefaultFeatureGate.Enabled(features.PodObservedGenerationTracking) && !podObservedGenerationTrackingInUse(oldPodStatus) {
podStatus.ObservedGeneration = 0
for i := range podStatus.Conditions {
podStatus.Conditions[i].ObservedGeneration = 0
}
}
}