学习理解 Kubernetes 代码实现
源码
- 1: pod
- 1.1: 概述
- 1.2: pod的定义
- 1.3: pkg\api\pod\util.go
- 1.3.1: ContainerType
- 1.3.2: ContainerVisitor
- 1.3.3: Visitor
- 1.3.4: PodCondition
- 1.3.5: ValidationOptions
- 1.3.6: Drop
- 1.4: pkg\api\pod\warnings.go
1 - pod
1.1 - 概述
相关代码文件
搜索条件:
- “files to include”:
*\pod\*.go
- “files to exclude”:
*_test.go,docs.go,doc.go,test\**\*.go
找到的相关代码文件为:
文件列表具体为:
- pkg\api\pod\util.go
- pkg\api\pod\warnings.go
- pkg\api\v1\pod\util.go
- pkg\kubelet\pod\mirror_client.go
- pkg\kubelet\pod\pod_manager.go
- pkg\registry\core\pod\strategy.go
- pkg\util\pod\pod.go
1.2 - pod的定义
概述
pod的结构体定义和属性,来自 cursor 的提示,感谢 cursor 的强大。
Pod 是 Kubernetes 中最小的可部署单元,它的定义位于 staging/src/k8s.io/api/core/v1/types.go
文件中。一个 Pod 包含以下主要部分:
- 基本结构:
type Pod struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
Spec PodSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
Status PodStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}
- 主要组件:
-
metadata
: 包含 Pod 的元数据信息,如名称、命名空间、标签等 -
spec
: Pod 的规格定义,包含以下重要字段:containers
: Pod 中的容器列表(至少需要一个容器)initContainers
: 初始化容器列表volumes
: 可挂载的存储卷列表restartPolicy
: 重启策略(Always、OnFailure、Never)nodeSelector
: 节点选择器serviceAccountName
: 服务账号名称securityContext
: 安全上下文imagePullSecrets
: 镜像拉取密钥affinity
: 亲和性设置tolerations
: 容忍设置- 等等…
-
status
: Pod 的当前状态信息,包含:phase
: Pod 的生命周期阶段(Pending、Running、Succeeded、Failed、Unknown)conditions
: Pod 的条件状态message
: 状态描述信息reason
: 状态原因hostIP
: 主机 IPpodIP
: Pod IPcontainerStatuses
: 容器状态列表initContainerStatuses
: 初始化容器状态列表qosClass
: 服务质量等级(Guaranteed、Burstable、BestEffort)
- 生命周期: Pod 有五个主要阶段:
Pending
: Pod 已被系统接受,但容器镜像尚未创建Running
: Pod 已绑定到节点,所有容器已启动Succeeded
: 所有容器已成功终止Failed
: 所有容器已终止,至少有一个容器失败Unknown
: 无法获取 Pod 状态
- 条件状态: Pod 可以有以下条件状态:
ContainersReady
: 所有容器是否就绪PodInitialized
: 所有初始化容器是否成功启动PodReady
: Pod 是否可以服务请求PodScheduled
: Pod 的调度状态DisruptionTarget
: Pod 是否即将被终止PodReadyToStartContainers
: Pod 沙箱是否配置成功PodResizePending
: Pod 是否正在调整大小PodResizeInProgress
: Pod 大小调整是否正在进行
这就是 Kubernetes 中 Pod 的基本定义和结构。Pod 是 Kubernetes 中最小的可部署单元,它可以包含一个或多个容器,这些容器共享网络命名空间和存储卷。
pod 结构体
pod的结构体定义在 staging/src/k8s.io/api/core/v1/pod.go
文件中:
type Pod struct {
metav1.TypeMeta `json:",inline"`
// Standard object's metadata.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
// +optional
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Specification of the desired behavior of the pod.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
// +optional
Spec PodSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
// Most recently observed status of the pod.
// This data may not be up to date.
// Populated by the system.
// Read-only.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
// +optional
Status PodStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}
备注:
staging/src/k8s.io/api
的代码会被同步到仓库 https://github.com/kubernetes/api 中
PodSpec 结构体
PodSpec 的定义在同一个文件中:
// PodSpec is a description of a pod.
type PodSpec struct {
// List of volumes that can be mounted by containers belonging to the pod.
// More info: https://kubernetes.io/docs/concepts/storage/volumes
// +optional
// +patchMergeKey=name
// +patchStrategy=merge,retainKeys
// +listType=map
// +listMapKey=name
Volumes []Volume `json:"volumes,omitempty" patchStrategy:"merge,retainKeys" patchMergeKey:"name" protobuf:"bytes,1,rep,name=volumes"`
// List of initialization containers belonging to the pod.
// Init containers are executed in order prior to containers being started. If any
// init container fails, the pod is considered to have failed and is handled according
// to its restartPolicy. The name for an init container or normal container must be
// unique among all containers.
// Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes.
// The resourceRequirements of an init container are taken into account during scheduling
// by finding the highest request/limit for each resource type, and then using the max of
// that value or the sum of the normal containers. Limits are applied to init containers
// in a similar fashion.
// Init containers cannot currently be added or removed.
// Cannot be updated.
// More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/
// +patchMergeKey=name
// +patchStrategy=merge
// +listType=map
// +listMapKey=name
InitContainers []Container `json:"initContainers,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,20,rep,name=initContainers"`
// List of containers belonging to the pod.
// Containers cannot currently be added or removed.
// There must be at least one container in a Pod.
// Cannot be updated.
// +patchMergeKey=name
// +patchStrategy=merge
// +listType=map
// +listMapKey=name
Containers []Container `json:"containers" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,2,rep,name=containers"`
// List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing
// pod to perform user-initiated actions such as debugging. This list cannot be specified when
// creating a pod, and it cannot be modified by updating the pod spec. In order to add an
// ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.
// +optional
// +patchMergeKey=name
// +patchStrategy=merge
// +listType=map
// +listMapKey=name
EphemeralContainers []EphemeralContainer `json:"ephemeralContainers,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,34,rep,name=ephemeralContainers"`
// Restart policy for all containers within the pod.
// One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted.
// Default to Always.
// More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy
// +optional
RestartPolicy RestartPolicy `json:"restartPolicy,omitempty" protobuf:"bytes,3,opt,name=restartPolicy,casttype=RestartPolicy"`
// Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request.
// Value must be non-negative integer. The value zero indicates stop immediately via
// the kill signal (no opportunity to shut down).
// If this value is nil, the default grace period will be used instead.
// The grace period is the duration in seconds after the processes running in the pod are sent
// a termination signal and the time when the processes are forcibly halted with a kill signal.
// Set this value longer than the expected cleanup time for your process.
// Defaults to 30 seconds.
// +optional
TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty" protobuf:"varint,4,opt,name=terminationGracePeriodSeconds"`
// Optional duration in seconds the pod may be active on the node relative to
// StartTime before the system will actively try to mark it failed and kill associated containers.
// Value must be a positive integer.
// +optional
ActiveDeadlineSeconds *int64 `json:"activeDeadlineSeconds,omitempty" protobuf:"varint,5,opt,name=activeDeadlineSeconds"`
// Set DNS policy for the pod.
// Defaults to "ClusterFirst".
// Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', 'Default' or 'None'.
// DNS parameters given in DNSConfig will be merged with the policy selected with DNSPolicy.
// To have DNS options set along with hostNetwork, you have to specify DNS policy
// explicitly to 'ClusterFirstWithHostNet'.
// +optional
DNSPolicy DNSPolicy `json:"dnsPolicy,omitempty" protobuf:"bytes,6,opt,name=dnsPolicy,casttype=DNSPolicy"`
// NodeSelector is a selector which must be true for the pod to fit on a node.
// Selector which must match a node's labels for the pod to be scheduled on that node.
// More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
// +optional
// +mapType=atomic
NodeSelector map[string]string `json:"nodeSelector,omitempty" protobuf:"bytes,7,rep,name=nodeSelector"`
// ServiceAccountName is the name of the ServiceAccount to use to run this pod.
// More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/
// +optional
ServiceAccountName string `json:"serviceAccountName,omitempty" protobuf:"bytes,8,opt,name=serviceAccountName"`
// DeprecatedServiceAccount is a deprecated alias for ServiceAccountName.
// Deprecated: Use serviceAccountName instead.
// +k8s:conversion-gen=false
// +optional
DeprecatedServiceAccount string `json:"serviceAccount,omitempty" protobuf:"bytes,9,opt,name=serviceAccount"`
// AutomountServiceAccountToken indicates whether a service account token should be automatically mounted.
// +optional
AutomountServiceAccountToken *bool `json:"automountServiceAccountToken,omitempty" protobuf:"varint,21,opt,name=automountServiceAccountToken"`
// NodeName indicates in which node this pod is scheduled.
// If empty, this pod is a candidate for scheduling by the scheduler defined in schedulerName.
// Once this field is set, the kubelet for this node becomes responsible for the lifecycle of this pod.
// This field should not be used to express a desire for the pod to be scheduled on a specific node.
// https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename
// +optional
NodeName string `json:"nodeName,omitempty" protobuf:"bytes,10,opt,name=nodeName"`
// Host networking requested for this pod. Use the host's network namespace.
// If this option is set, the ports that will be used must be specified.
// Default to false.
// +k8s:conversion-gen=false
// +optional
HostNetwork bool `json:"hostNetwork,omitempty" protobuf:"varint,11,opt,name=hostNetwork"`
// Use the host's pid namespace.
// Optional: Default to false.
// +k8s:conversion-gen=false
// +optional
HostPID bool `json:"hostPID,omitempty" protobuf:"varint,12,opt,name=hostPID"`
// Use the host's ipc namespace.
// Optional: Default to false.
// +k8s:conversion-gen=false
// +optional
HostIPC bool `json:"hostIPC,omitempty" protobuf:"varint,13,opt,name=hostIPC"`
// Share a single process namespace between all of the containers in a pod.
// When this is set containers will be able to view and signal processes from other containers
// in the same pod, and the first process in each container will not be assigned PID 1.
// HostPID and ShareProcessNamespace cannot both be set.
// Optional: Default to false.
// +k8s:conversion-gen=false
// +optional
ShareProcessNamespace *bool `json:"shareProcessNamespace,omitempty" protobuf:"varint,27,opt,name=shareProcessNamespace"`
// SecurityContext holds pod-level security attributes and common container settings.
// Optional: Defaults to empty. See type description for default values of each field.
// +optional
SecurityContext *PodSecurityContext `json:"securityContext,omitempty" protobuf:"bytes,14,opt,name=securityContext"`
// ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec.
// If specified, these secrets will be passed to individual puller implementations for them to use.
// More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod
// +optional
// +patchMergeKey=name
// +patchStrategy=merge
// +listType=map
// +listMapKey=name
ImagePullSecrets []LocalObjectReference `json:"imagePullSecrets,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,15,rep,name=imagePullSecrets"`
// Specifies the hostname of the Pod
// If not specified, the pod's hostname will be set to a system-defined value.
// +optional
Hostname string `json:"hostname,omitempty" protobuf:"bytes,16,opt,name=hostname"`
// If specified, the fully qualified Pod hostname will be "<hostname>.<subdomain>.<pod namespace>.svc.<cluster domain>".
// If not specified, the pod will not have a domainname at all.
// +optional
Subdomain string `json:"subdomain,omitempty" protobuf:"bytes,17,opt,name=subdomain"`
// If specified, the pod's scheduling constraints
// +optional
Affinity *Affinity `json:"affinity,omitempty" protobuf:"bytes,18,opt,name=affinity"`
// If specified, the pod will be dispatched by specified scheduler.
// If not specified, the pod will be dispatched by default scheduler.
// +optional
SchedulerName string `json:"schedulerName,omitempty" protobuf:"bytes,19,opt,name=schedulerName"`
// If specified, the pod's tolerations.
// +optional
// +listType=atomic
Tolerations []Toleration `json:"tolerations,omitempty" protobuf:"bytes,22,opt,name=tolerations"`
// HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts
// file if specified.
// +optional
// +patchMergeKey=ip
// +patchStrategy=merge
// +listType=map
// +listMapKey=ip
HostAliases []HostAlias `json:"hostAliases,omitempty" patchStrategy:"merge" patchMergeKey:"ip" protobuf:"bytes,23,rep,name=hostAliases"`
// If specified, indicates the pod's priority. "system-node-critical" and
// "system-cluster-critical" are two special keywords which indicate the
// highest priorities with the former being the highest priority. Any other
// name must be defined by creating a PriorityClass object with that name.
// If not specified, the pod priority will be default or zero if there is no
// default.
// +optional
PriorityClassName string `json:"priorityClassName,omitempty" protobuf:"bytes,24,opt,name=priorityClassName"`
// The priority value. Various system components use this field to find the
// priority of the pod. When Priority Admission Controller is enabled, it
// prevents users from setting this field. The admission controller populates
// this field from PriorityClassName.
// The higher the value, the higher the priority.
// +optional
Priority *int32 `json:"priority,omitempty" protobuf:"bytes,25,opt,name=priority"`
// Specifies the DNS parameters of a pod.
// Parameters specified here will be merged to the generated DNS
// configuration based on DNSPolicy.
// +optional
DNSConfig *PodDNSConfig `json:"dnsConfig,omitempty" protobuf:"bytes,26,opt,name=dnsConfig"`
// If specified, all readiness gates will be evaluated for pod readiness.
// A pod is ready when all its containers are ready AND
// all conditions specified in the readiness gates have status equal to "True"
// More info: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates
// +optional
// +listType=atomic
ReadinessGates []PodReadinessGate `json:"readinessGates,omitempty" protobuf:"bytes,28,opt,name=readinessGates"`
// RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used
// to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run.
// If unset or empty, the "legacy" RuntimeClass will be used, which is an implicit class with an
// empty definition that uses the default runtime handler.
// More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class
// +optional
RuntimeClassName *string `json:"runtimeClassName,omitempty" protobuf:"bytes,29,opt,name=runtimeClassName"`
// EnableServiceLinks indicates whether information about services should be injected into pod's
// environment variables, matching the syntax of Docker links.
// Optional: Defaults to true.
// +optional
EnableServiceLinks *bool `json:"enableServiceLinks,omitempty" protobuf:"varint,30,opt,name=enableServiceLinks"`
// PreemptionPolicy is the Policy for preempting pods with lower priority.
// One of Never, PreemptLowerPriority.
// Defaults to PreemptLowerPriority if unset.
// +optional
PreemptionPolicy *PreemptionPolicy `json:"preemptionPolicy,omitempty" protobuf:"bytes,31,opt,name=preemptionPolicy"`
// Overhead represents the resource overhead associated with running a pod for a given RuntimeClass.
// This field will be autopopulated at admission time by the RuntimeClass admission controller. If
// the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests.
// The RuntimeClass admission controller will reject Pod create requests which have the overhead already
// set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value
// defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero.
// More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md
// +optional
Overhead ResourceList `json:"overhead,omitempty" protobuf:"bytes,32,opt,name=overhead"`
// TopologySpreadConstraints describes how a group of pods ought to spread across topology
// domains. Scheduler will schedule pods in a way which abides by the constraints.
// All topologySpreadConstraints are ANDed.
// +optional
// +patchMergeKey=topologyKey
// +patchStrategy=merge
// +listType=map
// +listMapKey=topologyKey
// +listMapKey=whenUnsatisfiable
TopologySpreadConstraints []TopologySpreadConstraint `json:"topologySpreadConstraints,omitempty" patchStrategy:"merge" patchMergeKey:"topologyKey" protobuf:"bytes,33,opt,name=topologySpreadConstraints"`
// If true the pod's hostname will be configured as the pod's FQDN, rather than the leaf name (the default).
// In Linux containers, this means setting the FQDN in the hostname field of the kernel (the nodename field of struct utsname).
// In Windows containers, this means setting the registry value of hostname for the registry key HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters to FQDN.
// If a pod does not have FQDN, this has no effect.
// Default to false.
// +optional
SetHostnameAsFQDN *bool `json:"setHostnameAsFQDN,omitempty" protobuf:"varint,35,opt,name=setHostnameAsFQDN"`
// Specifies the OS of the containers in the pod.
// Some pod and container fields are restricted if this is set.
//
// If the OS field is set to linux, the following fields must be unset:
// -securityContext.windowsOptions
//
// If the OS field is set to windows, following fields must be unset:
// - spec.hostPID
// - spec.hostIPC
// - spec.hostUsers
// - spec.securityContext.appArmorProfile
// - spec.securityContext.seLinuxOptions
// - spec.securityContext.seccompProfile
// - spec.securityContext.fsGroup
// - spec.securityContext.fsGroupChangePolicy
// - spec.securityContext.sysctls
// - spec.shareProcessNamespace
// - spec.securityContext.runAsUser
// - spec.securityContext.runAsGroup
// - spec.securityContext.supplementalGroups
// - spec.securityContext.supplementalGroupsPolicy
// - spec.containers[*].securityContext.appArmorProfile
// - spec.containers[*].securityContext.seLinuxOptions
// - spec.containers[*].securityContext.seccompProfile
// - spec.containers[*].securityContext.capabilities
// - spec.containers[*].securityContext.readOnlyRootFilesystem
// - spec.containers[*].securityContext.privileged
// - spec.containers[*].securityContext.allowPrivilegeEscalation
// - spec.containers[*].securityContext.procMount
// - spec.containers[*].securityContext.runAsUser
// - spec.containers[*].securityContext.runAsGroup
// +optional
OS *PodOS `json:"os,omitempty" protobuf:"bytes,36,opt,name=os"`
// Use the host's user namespace.
// Optional: Default to true.
// If set to true or not present, the pod will be run in the host user namespace, useful
// for when the pod needs a feature only available to the host user namespace, such as
// loading a kernel module with CAP_SYS_MODULE.
// When set to false, a new userns is created for the pod. Setting false is useful for
// mitigating container breakout vulnerabilities even allowing users to run their
// containers as root without actually having root privileges on the host.
// This field is alpha-level and is only honored by servers that enable the UserNamespacesSupport feature.
// +k8s:conversion-gen=false
// +optional
HostUsers *bool `json:"hostUsers,omitempty" protobuf:"bytes,37,opt,name=hostUsers"`
// SchedulingGates is an opaque list of values that if specified will block scheduling the pod.
// If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the
// scheduler will not attempt to schedule the pod.
//
// SchedulingGates can only be set at pod creation time, and be removed only afterwards.
//
// +patchMergeKey=name
// +patchStrategy=merge
// +listType=map
// +listMapKey=name
// +optional
SchedulingGates []PodSchedulingGate `json:"schedulingGates,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,38,opt,name=schedulingGates"`
// ResourceClaims defines which ResourceClaims must be allocated
// and reserved before the Pod is allowed to start. The resources
// will be made available to those containers which consume them
// by name.
//
// This is an alpha field and requires enabling the
// DynamicResourceAllocation feature gate.
//
// This field is immutable.
//
// +patchMergeKey=name
// +patchStrategy=merge,retainKeys
// +listType=map
// +listMapKey=name
// +featureGate=DynamicResourceAllocation
// +optional
ResourceClaims []PodResourceClaim `json:"resourceClaims,omitempty" patchStrategy:"merge,retainKeys" patchMergeKey:"name" protobuf:"bytes,39,rep,name=resourceClaims"`
// Resources is the total amount of CPU and Memory resources required by all
// containers in the pod. It supports specifying Requests and Limits for
// "cpu" and "memory" resource names only. ResourceClaims are not supported.
//
// This field enables fine-grained control over resource allocation for the
// entire pod, allowing resource sharing among containers in a pod.
// TODO: For beta graduation, expand this comment with a detailed explanation.
//
// This is an alpha field and requires enabling the PodLevelResources feature
// gate.
//
// +featureGate=PodLevelResources
// +optional
Resources *ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,40,opt,name=resources"`
}
PodStatus 结构体
PodStatus 的定义在同一个文件中:
// PodStatus represents information about the status of a pod. Status may trail the actual
// state of a system, especially if the node that hosts the pod cannot contact the control
// plane.
type PodStatus struct {
// If set, this represents the .metadata.generation that the pod status was set based upon.
// This is an alpha field. Enable PodObservedGenerationTracking to be able to use this field.
// +featureGate=PodObservedGenerationTracking
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,17,opt,name=observedGeneration"`
// The phase of a Pod is a simple, high-level summary of where the Pod is in its lifecycle.
// The conditions array, the reason and message fields, and the individual container status
// arrays contain more detail about the pod's status.
// There are five possible phase values:
//
// Pending: The pod has been accepted by the Kubernetes system, but one or more of the
// container images has not been created. This includes time before being scheduled as
// well as time spent downloading images over the network, which could take a while.
// Running: The pod has been bound to a node, and all of the containers have been created.
// At least one container is still running, or is in the process of starting or restarting.
// Succeeded: All containers in the pod have terminated in success, and will not be restarted.
// Failed: All containers in the pod have terminated, and at least one container has
// terminated in failure. The container either exited with non-zero status or was terminated
// by the system.
// Unknown: For some reason the state of the pod could not be obtained, typically due to an
// error in communicating with the host of the pod.
//
// More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-phase
// +optional
Phase PodPhase `json:"phase,omitempty" protobuf:"bytes,1,opt,name=phase,casttype=PodPhase"`
// Current service state of pod.
// More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions
// +optional
// +patchMergeKey=type
// +patchStrategy=merge
// +listType=map
// +listMapKey=type
Conditions []PodCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,2,rep,name=conditions"`
// A human readable message indicating details about why the pod is in this condition.
// +optional
Message string `json:"message,omitempty" protobuf:"bytes,3,opt,name=message"`
// A brief CamelCase message indicating details about why the pod is in this state.
// e.g. 'Evicted'
// +optional
Reason string `json:"reason,omitempty" protobuf:"bytes,4,opt,name=reason"`
// nominatedNodeName is set only when this pod preempts other pods on the node, but it cannot be
// scheduled right away as preemption victims receive their graceful termination periods.
// This field does not guarantee that the pod will be scheduled on this node. Scheduler may decide
// to place the pod elsewhere if other nodes become available sooner. Scheduler may also decide to
// give the resources on this node to a higher priority pod that is created after preemption.
// As a result, this field may be different than PodSpec.nodeName when the pod is
// scheduled.
// +optional
NominatedNodeName string `json:"nominatedNodeName,omitempty" protobuf:"bytes,11,opt,name=nominatedNodeName"`
// hostIP holds the IP address of the host to which the pod is assigned. Empty if the pod has not started yet.
// A pod can be assigned to a node that has a problem in kubelet which in turns mean that HostIP will
// not be updated even if there is a node is assigned to pod
// +optional
HostIP string `json:"hostIP,omitempty" protobuf:"bytes,5,opt,name=hostIP"`
// hostIPs holds the IP addresses allocated to the host. If this field is specified, the first entry must
// match the hostIP field. This list is empty if the pod has not started yet.
// A pod can be assigned to a node that has a problem in kubelet which in turns means that HostIPs will
// not be updated even if there is a node is assigned to this pod.
// +optional
// +patchStrategy=merge
// +patchMergeKey=ip
// +listType=atomic
HostIPs []HostIP `json:"hostIPs,omitempty" protobuf:"bytes,16,rep,name=hostIPs" patchStrategy:"merge" patchMergeKey:"ip"`
// podIP address allocated to the pod. Routable at least within the cluster.
// Empty if not yet allocated.
// +optional
PodIP string `json:"podIP,omitempty" protobuf:"bytes,6,opt,name=podIP"`
// podIPs holds the IP addresses allocated to the pod. If this field is specified, the 0th entry must
// match the podIP field. Pods may be allocated at most 1 value for each of IPv4 and IPv6. This list
// is empty if no IPs have been allocated yet.
// +optional
// +patchStrategy=merge
// +patchMergeKey=ip
// +listType=map
// +listMapKey=ip
PodIPs []PodIP `json:"podIPs,omitempty" protobuf:"bytes,12,rep,name=podIPs" patchStrategy:"merge" patchMergeKey:"ip"`
// RFC 3339 date and time at which the object was acknowledged by the Kubelet.
// This is before the Kubelet pulled the container image(s) for the pod.
// +optional
StartTime *metav1.Time `json:"startTime,omitempty" protobuf:"bytes,7,opt,name=startTime"`
// Statuses of init containers in this pod. The most recent successful non-restartable
// init container will have ready = true, the most recently started container will have
// startTime set.
// Each init container in the pod should have at most one status in this list,
// and all statuses should be for containers in the pod.
// However this is not enforced.
// If a status for a non-existent container is present in the list, or the list has duplicate names,
// the behavior of various Kubernetes components is not defined and those statuses might be
// ignored.
// More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-and-container-status
// +listType=atomic
InitContainerStatuses []ContainerStatus `json:"initContainerStatuses,omitempty" protobuf:"bytes,10,rep,name=initContainerStatuses"`
// Statuses of containers in this pod.
// Each container in the pod should have at most one status in this list,
// and all statuses should be for containers in the pod.
// However this is not enforced.
// If a status for a non-existent container is present in the list, or the list has duplicate names,
// the behavior of various Kubernetes components is not defined and those statuses might be
// ignored.
// More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-and-container-status
// +optional
// +listType=atomic
ContainerStatuses []ContainerStatus `json:"containerStatuses,omitempty" protobuf:"bytes,8,rep,name=containerStatuses"`
// The Quality of Service (QOS) classification assigned to the pod based on resource requirements
// See PodQOSClass type for available QOS classes
// More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-qos/#quality-of-service-classes
// +optional
QOSClass PodQOSClass `json:"qosClass,omitempty" protobuf:"bytes,9,rep,name=qosClass"`
// Statuses for any ephemeral containers that have run in this pod.
// Each ephemeral container in the pod should have at most one status in this list,
// and all statuses should be for containers in the pod.
// However this is not enforced.
// If a status for a non-existent container is present in the list, or the list has duplicate names,
// the behavior of various Kubernetes components is not defined and those statuses might be
// ignored.
// More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-and-container-status
// +optional
// +listType=atomic
EphemeralContainerStatuses []ContainerStatus `json:"ephemeralContainerStatuses,omitempty" protobuf:"bytes,13,rep,name=ephemeralContainerStatuses"`
// Status of resources resize desired for pod's containers.
// It is empty if no resources resize is pending.
// Any changes to container resources will automatically set this to "Proposed"
// Deprecated: Resize status is moved to two pod conditions PodResizePending and PodResizeInProgress.
// PodResizePending will track states where the spec has been resized, but the Kubelet has not yet allocated the resources.
// PodResizeInProgress will track in-progress resizes, and should be present whenever allocated resources != acknowledged resources.
// +featureGate=InPlacePodVerticalScaling
// +optional
Resize PodResizeStatus `json:"resize,omitempty" protobuf:"bytes,14,opt,name=resize,casttype=PodResizeStatus"`
// Status of resource claims.
// +patchMergeKey=name
// +patchStrategy=merge,retainKeys
// +listType=map
// +listMapKey=name
// +featureGate=DynamicResourceAllocation
// +optional
ResourceClaimStatuses []PodResourceClaimStatus `json:"resourceClaimStatuses,omitempty" patchStrategy:"merge,retainKeys" patchMergeKey:"name" protobuf:"bytes,15,rep,name=resourceClaimStatuses"`
}
reference 文档
pod 的 reference 文档:
https://kubernetes.io/zh-cn/docs/reference/kubernetes-api/workload-resources/pod-v1/
1.3 - pkg\api\pod\util.go
文件列表具体为:
- pkg\api\pod\util.go
1.3.1 - ContainerType
ContainerType 定义
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() 方法
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 啊
1.3.2 - ContainerVisitor
ContainerType 定义
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() 方法
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
}
1.3.3 - Visitor
Visitor() 方法定义
Visitor() 方法会被每个对象名称调用,如果要继续访问,则返回 true
// Visitor is called with each object name, and returns true if visiting should continue
type Visitor func(name string) (shouldContinue bool)
skipEmptyNames() 方法
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() 方法
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() 方法
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
}
1.3.4 - PodCondition
IsPodReady() 方法
如果 pod ready 则返回 true
// IsPodReady returns true if a pod is ready; false otherwise.
func IsPodReady(pod *api.Pod) bool {
return IsPodReadyConditionTrue(pod.Status)
}
IsPodReadyConditionTrue() 方法
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() 方法
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() 方法
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() 方法
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
}
1.3.5 - ValidationOptions
预备方法
检查 Huge Pages 的 usesIndivisibleHugePagesValues() 方法
如果其中一个容器使用非整数倍的 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
}
检查 Topology Spread Constraints 的 hasInvalidTopologySpreadConstraintLabelSelector() 方法
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 = 拓扑扩展限制
检查 Volumes 的 hasNonLocalProjectedTokenPath() 方法
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() 方法
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() 方法
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)
}
1.3.6 - Drop
DropDisabledTemplateFields() 方法
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() 方法
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() 方法
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() 方法
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
}
}
}
1.4 - pkg\api\pod\warnings.go
文件列表具体为:
- pkg\api\pod\warnings.go