rebase: update kubernetes and libraries to v1.22.0 version

Kubernetes v1.22 version has been released and this update
ceph csi dependencies to use the same version.

Signed-off-by: Humble Chirammal <hchiramm@redhat.com>
This commit is contained in:
Humble Chirammal
2021-08-09 12:49:24 +05:30
committed by mergify[bot]
parent e077c1fdf5
commit aa698bc3e1
759 changed files with 61864 additions and 6514 deletions

View File

@ -1,19 +1,8 @@
# See the OWNERS docs at https://go.k8s.io/owners
# approval on api packages bubbles to api-approvers
reviewers:
- thockin
- lavalamp
- smarterclayton
- deads2k
- caesarxuchao
- pmorie
- sttts
- saad-ali
- ncdc
- dims
- errordeveloper
- mml
- m1093782566
- kevin-wangzefeng
- sig-apps-api-reviewers
- sig-apps-api-approvers
labels:
- sig/apps

View File

@ -157,6 +157,13 @@ type StatefulSetSpec struct {
// consists of all revisions not represented by a currently applied
// StatefulSetSpec version. The default value is 10.
RevisionHistoryLimit *int32
// Minimum number of seconds for which a newly created pod should be ready
// without any of its container crashing for it to be considered available.
// Defaults to 0 (pod will be considered available as soon as it is ready)
// This is an alpha field and requires enabling StatefulSetMinReadySeconds feature gate.
// +optional
MinReadySeconds int32
}
// StatefulSetStatus represents the current state of a StatefulSet.
@ -196,6 +203,12 @@ type StatefulSetStatus struct {
// Represents the latest available observations of a statefulset's current state.
Conditions []StatefulSetCondition
// Total number of available pods (ready for at least minReadySeconds) targeted by this statefulset.
// This is an alpha field and requires enabling StatefulSetMinReadySeconds feature gate.
// Remove omitempty when graduating to beta
// +optional
AvailableReplicas int32
}
// StatefulSetConditionType describes the condition types of StatefulSets.
@ -532,7 +545,7 @@ type RollingUpdateDaemonSet struct {
// The maximum number of DaemonSet pods that can be unavailable during the
// update. Value can be an absolute number (ex: 5) or a percentage of total
// number of DaemonSet pods at the start of the update (ex: 10%). Absolute
// number is calculated from percentage by rounding down to a minimum of one.
// number is calculated from percentage by rounding up.
// This cannot be 0 if MaxSurge is 0
// Default value is 1.
// Example: when this is set to 30%, at most 30% of the total number of nodes
@ -564,7 +577,7 @@ type RollingUpdateDaemonSet struct {
// daemonset on any given node can double if the readiness check fails, and
// so resource intensive daemonsets should take into account that they may
// cause evictions during disruption.
// This is an alpha field and requires enabling DaemonSetUpdateSurge feature gate.
// This is beta field and enabled/disabled by DaemonSetUpdateSurge feature gate.
// +optional
MaxSurge intstr.IntOrString
}

View File

@ -7,10 +7,8 @@ reviewers:
- wojtek-t
- deads2k
- caesarxuchao
- erictune
- sttts
- ncdc
- piosz
- dims
- errordeveloper
- mml

View File

@ -1,19 +1,8 @@
# See the OWNERS docs at https://go.k8s.io/owners
# approval on api packages bubbles to api-approvers
reviewers:
- thockin
- lavalamp
- smarterclayton
- wojtek-t
- deads2k
- caesarxuchao
- erictune
- sttts
- saad-ali
- ncdc
- soltysh
- dims
- errordeveloper
- mml
- sig-apps-api-reviewers
- sig-apps-api-approvers
labels:
- sig/apps

View File

@ -18,9 +18,18 @@ package batch
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
api "k8s.io/kubernetes/pkg/apis/core"
)
// JobTrackingFinalizer is a finalizer for Job's pods. It prevents them from
// being deleted before being accounted in the Job status.
// The apiserver and job controller use this string as a Job annotation, to
// mark Jobs that are being tracked using pod finalizers. Two releases after
// the JobTrackingWithFinalizers graduates to GA, JobTrackingFinalizer will
// no longer be used as a Job annotation.
const JobTrackingFinalizer = "batch.kubernetes.io/job-tracking"
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Job represents the configuration of a single job.
@ -183,9 +192,11 @@ type JobSpec struct {
// for each index.
// When value is `Indexed`, .spec.completions must be specified and
// `.spec.parallelism` must be less than or equal to 10^5.
// In addition, The Pod name takes the form
// `$(job-name)-$(index)-$(random-string)`,
// the Pod hostname takes the form `$(job-name)-$(index)`.
//
// This field is alpha-level and is only honored by servers that enable the
// IndexedJob feature gate. More completion modes can be added in the future.
// This field is beta-level. More completion modes can be added in the future.
// If the Job controller observes a mode that it doesn't recognize, the
// controller skips updates for the Job.
// +optional
@ -197,9 +208,11 @@ type JobSpec struct {
// false to true), the Job controller will delete all active Pods associated
// with this Job. Users must design their workload to gracefully handle this.
// Suspending a Job will reset the StartTime field of the Job, effectively
// resetting the ActiveDeadlineSeconds timer too. This is an alpha field and
// requires the SuspendJob feature gate to be enabled; otherwise this field
// may not be set to true. Defaults to false.
// resetting the ActiveDeadlineSeconds timer too. Defaults to false.
//
// This field is beta-level, gated by SuspendJob feature flag (enabled by
// default).
//
// +optional
Suspend *bool
}
@ -251,6 +264,38 @@ type JobStatus struct {
// represented as "1,3-5,7".
// +optional
CompletedIndexes string
// UncountedTerminatedPods holds the UIDs of Pods that have terminated but
// the job controller hasn't yet accounted for in the status counters.
//
// The job controller creates pods with a finalizer. When a pod terminates
// (succeeded or failed), the controller does three steps to account for it
// in the job status:
// (1) Add the pod UID to the corresponding array in this field.
// (2) Remove the pod finalizer.
// (3) Remove the pod UID from the array while increasing the corresponding
// counter.
//
// This field is alpha-level. The job controller only makes use of this field
// when the feature gate PodTrackingWithFinalizers is enabled.
// Old jobs might not be tracked using this field, in which case the field
// remains null.
// +optional
UncountedTerminatedPods *UncountedTerminatedPods
}
// UncountedTerminatedPods holds UIDs of Pods that have terminated but haven't
// been accounted in Job status counters.
type UncountedTerminatedPods struct {
// Succeeded holds UIDs of succeeded Pods.
// +listType=set
// +optional
Succeeded []types.UID
// Failed holds UIDs of failed Pods.
// +listType=set
// +optional
Failed []types.UID
}
// JobConditionType is a valid value for JobCondition.Type

View File

@ -23,6 +23,7 @@ package batch
import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
types "k8s.io/apimachinery/pkg/types"
core "k8s.io/kubernetes/pkg/apis/core"
)
@ -312,6 +313,11 @@ func (in *JobStatus) DeepCopyInto(out *JobStatus) {
in, out := &in.CompletionTime, &out.CompletionTime
*out = (*in).DeepCopy()
}
if in.UncountedTerminatedPods != nil {
in, out := &in.UncountedTerminatedPods, &out.UncountedTerminatedPods
*out = new(UncountedTerminatedPods)
(*in).DeepCopyInto(*out)
}
return
}
@ -369,3 +375,29 @@ func (in *JobTemplateSpec) DeepCopy() *JobTemplateSpec {
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UncountedTerminatedPods) DeepCopyInto(out *UncountedTerminatedPods) {
*out = *in
if in.Succeeded != nil {
in, out := &in.Succeeded, &out.Succeeded
*out = make([]types.UID, len(*in))
copy(*out, *in)
}
if in.Failed != nil {
in, out := &in.Failed, &out.Failed
*out = make([]types.UID, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UncountedTerminatedPods.
func (in *UncountedTerminatedPods) DeepCopy() *UncountedTerminatedPods {
if in == nil {
return nil
}
out := new(UncountedTerminatedPods)
in.DeepCopyInto(out)
return out
}

View File

@ -1,40 +1,4 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- erictune
- lavalamp
- smarterclayton
- thockin
- liggitt
reviewers:
- thockin
- lavalamp
- smarterclayton
- wojtek-t
- deads2k
- yujuhong
- brendandburns
- derekwaynecarr
- caesarxuchao
- vishh
- mikedanese
- liggitt
- erictune
- davidopp
- pmorie
- sttts
- dchen1107
- saad-ali
- luxas
- janetkuo
- justinsb
- pwittrock
- ncdc
- tallclair
- yifan-gu
- mwielgus
- soltysh
- piosz
- jsafrane
labels:
- sig/apps

View File

@ -23,9 +23,6 @@ const (
// webhook backend fails.
ImagePolicyFailedOpenKey string = "alpha.image-policy.k8s.io/failed-open"
// PodPresetOptOutAnnotationKey represents the annotation key for a pod to exempt itself from pod preset manipulation
PodPresetOptOutAnnotationKey string = "podpreset.admission.kubernetes.io/exclude"
// MirrorPodAnnotationKey represents the annotation key set by kubelets when creating mirror pods
MirrorPodAnnotationKey string = "kubernetes.io/config.mirror"
@ -122,7 +119,7 @@ const (
// pod deletion order.
// The implicit deletion cost for pods that don't set the annotation is 0, negative values are permitted.
//
// This annotation is alpha-level and is only honored when PodDeletionCost feature is enabled.
// This annotation is beta-level and is only honored when PodDeletionCost feature is enabled.
PodDeletionCost = "controller.kubernetes.io/pod-deletion-cost"
// AnnotationTopologyAwareHints can be used to enable or disable Topology

View File

@ -303,19 +303,22 @@ func IsStandardFinalizerName(str string) bool {
}
// GetAccessModesAsString returns a string representation of an array of access modes.
// modes, when present, are always in the same order: RWO,ROX,RWX.
// modes, when present, are always in the same order: RWO,ROX,RWX,RWOP.
func GetAccessModesAsString(modes []core.PersistentVolumeAccessMode) string {
modes = removeDuplicateAccessModes(modes)
modesStr := []string{}
if containsAccessMode(modes, core.ReadWriteOnce) {
if ContainsAccessMode(modes, core.ReadWriteOnce) {
modesStr = append(modesStr, "RWO")
}
if containsAccessMode(modes, core.ReadOnlyMany) {
if ContainsAccessMode(modes, core.ReadOnlyMany) {
modesStr = append(modesStr, "ROX")
}
if containsAccessMode(modes, core.ReadWriteMany) {
if ContainsAccessMode(modes, core.ReadWriteMany) {
modesStr = append(modesStr, "RWX")
}
if ContainsAccessMode(modes, core.ReadWriteOncePod) {
modesStr = append(modesStr, "RWOP")
}
return strings.Join(modesStr, ",")
}
@ -332,6 +335,8 @@ func GetAccessModesFromString(modes string) []core.PersistentVolumeAccessMode {
accessModes = append(accessModes, core.ReadOnlyMany)
case s == "RWX":
accessModes = append(accessModes, core.ReadWriteMany)
case s == "RWOP":
accessModes = append(accessModes, core.ReadWriteOncePod)
}
}
return accessModes
@ -341,14 +346,14 @@ func GetAccessModesFromString(modes string) []core.PersistentVolumeAccessMode {
func removeDuplicateAccessModes(modes []core.PersistentVolumeAccessMode) []core.PersistentVolumeAccessMode {
accessModes := []core.PersistentVolumeAccessMode{}
for _, m := range modes {
if !containsAccessMode(accessModes, m) {
if !ContainsAccessMode(accessModes, m) {
accessModes = append(accessModes, m)
}
}
return accessModes
}
func containsAccessMode(modes []core.PersistentVolumeAccessMode, mode core.PersistentVolumeAccessMode) bool {
func ContainsAccessMode(modes []core.PersistentVolumeAccessMode, mode core.PersistentVolumeAccessMode) bool {
for _, m := range modes {
if m == mode {
return true

View File

@ -96,7 +96,6 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&RangeAllocation{},
&ConfigMap{},
&ConfigMapList{},
&EphemeralContainers{},
)
return nil

View File

@ -450,13 +450,31 @@ type PersistentVolumeClaimSpec struct {
// This field can be used to specify either:
// * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot)
// * An existing PVC (PersistentVolumeClaim)
// * An existing custom resource that implements data population (Alpha)
// In order to use custom resource types that implement data population,
// the AnyVolumeDataSource feature gate must be enabled.
// If the provisioner or an external controller can support the specified data source,
// it will create a new volume based on the contents of the specified data source.
// If the AnyVolumeDataSource feature gate is enabled, this field will always have
// the same contents as the DataSourceRef field.
// +optional
DataSource *TypedLocalObjectReference
// Specifies the object from which to populate the volume with data, if a non-empty
// volume is desired. This may be any local object from a non-empty API group (non
// core object) or a PersistentVolumeClaim object.
// When this field is specified, volume binding will only succeed if the type of
// the specified object matches some installed volume populator or dynamic
// provisioner.
// This field will replace the functionality of the DataSource field and as such
// if both fields are non-empty, they must have the same value. For backwards
// compatibility, both fields (DataSource and DataSourceRef) will be set to the same
// value automatically if one of them is empty and the other is non-empty.
// There are two important differences between DataSource and DataSourceRef:
// * While DataSource only allows two specific types of objects, DataSourceRef
// allows any non-core object, as well as PersistentVolumeClaim objects.
// * While DataSource ignores disallowed values (dropping them), DataSourceRef
// preserves all values, and generates an error if a disallowed value is
// specified.
// (Alpha) Using this field requires the AnyVolumeDataSource feature gate to be enabled.
// +optional
DataSourceRef *TypedLocalObjectReference
}
// PersistentVolumeClaimConditionType defines the condition of PV claim.
@ -511,6 +529,9 @@ const (
ReadOnlyMany PersistentVolumeAccessMode = "ReadOnlyMany"
// can be mounted in read/write mode to many hosts
ReadWriteMany PersistentVolumeAccessMode = "ReadWriteMany"
// can be mounted read/write mode to exactly 1 pod
// cannot be used in combination with other access modes
ReadWriteOncePod PersistentVolumeAccessMode = "ReadWriteOncePod"
)
// PersistentVolumePhase defines the phase in which a PV is
@ -1826,12 +1847,13 @@ type EnvVar struct {
Name string
// Optional: no more than one of the following may be specified.
// Optional: Defaults to ""; variable references $(VAR_NAME) are expanded
// using the previous defined environment variables in the container and
// using the previously defined environment variables in the container and
// any service environment variables. If a variable cannot be resolved,
// the reference in the input string will be unchanged. The $(VAR_NAME)
// syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped
// references will never be expanded, regardless of whether the variable
// exists or not.
// the reference in the input string will be unchanged. Double $$ are
// reduced to a single $, which allows for escaping the $(VAR_NAME)
// syntax: i.e. "$$(VAR_NAME)" will produce the string literal
// "$(VAR_NAME)". Escaped references will never be expanded,
// regardless of whether the variable exists or not.
// +optional
Value string
// Optional: Specifies a source the value of this var should come from.
@ -2028,7 +2050,7 @@ type Probe struct {
// value overrides the value provided by the pod spec.
// Value must be non-negative integer. The value zero indicates stop immediately via
// the kill signal (no opportunity to shut down).
// This is an alpha field and requires enabling ProbeTerminationGracePeriod feature gate.
// This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate.
// +optional
TerminationGracePeriodSeconds *int64
}
@ -2102,16 +2124,18 @@ type Container struct {
Image string
// Optional: The docker image's entrypoint is used if this is not provided; cannot be updated.
// Variable references $(VAR_NAME) are expanded using the container's environment. If a variable
// cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax
// can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded,
// regardless of whether the variable exists or not.
// cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced
// to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will
// produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless
// of whether the variable exists or not.
// +optional
Command []string
// Optional: The docker image's cmd is used if this is not provided; cannot be updated.
// Variable references $(VAR_NAME) are expanded using the container's environment. If a variable
// cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax
// can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded,
// regardless of whether the variable exists or not.
// cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced
// to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will
// produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless
// of whether the variable exists or not.
// +optional
Args []string
// Optional: Defaults to Docker's default.
@ -2304,6 +2328,7 @@ const (
PodFailed PodPhase = "Failed"
// PodUnknown means that 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.
// Deprecated in v1.21: It isn't being set since 2015 (74da3b14b0c0f658b3bb8d2def5094686d0e9095)
PodUnknown PodPhase = "Unknown"
)
@ -2579,7 +2604,7 @@ type PodAffinityTerm struct {
// and the ones listed in the namespaces field.
// null selector and null or empty namespaces list means "this pod's namespace".
// An empty selector ({}) matches all namespaces.
// This field is alpha-level and is only honored when PodAffinityNamespaceSelector feature is enabled.
// This field is beta-level and is only honored when PodAffinityNamespaceSelector feature is enabled.
// +optional
NamespaceSelector *metav1.LabelSelector
}
@ -2832,14 +2857,14 @@ type PodSpec struct {
// 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/0007-pod-ready%2B%2B.md
// More info: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates
// +optional
ReadinessGates []PodReadinessGate
// 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/README.md
// More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class
// +optional
RuntimeClassName *string
// Overhead represents the resource overhead associated with running a pod for a given RuntimeClass.
@ -2848,8 +2873,8 @@ type PodSpec struct {
// 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/20190226-pod-overhead.md
// This field is alpha-level as of Kubernetes v1.16, and is only honored by servers that enable the PodOverhead feature.
// More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead
// This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature.
// +optional
Overhead ResourceList
// EnableServiceLinks indicates whether information about services should be injected into pod's
@ -3078,16 +3103,18 @@ type EphemeralContainerCommon struct {
Image string
// Optional: The docker image's entrypoint is used if this is not provided; cannot be updated.
// Variable references $(VAR_NAME) are expanded using the container's environment. If a variable
// cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax
// can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded,
// regardless of whether the variable exists or not.
// cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced
// to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will
// produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless
// of whether the variable exists or not.
// +optional
Command []string
// Optional: The docker image's cmd is used if this is not provided; cannot be updated.
// Variable references $(VAR_NAME) are expanded using the container's environment. If a variable
// cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax
// can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded,
// regardless of whether the variable exists or not.
// cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced
// to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will
// produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless
// of whether the variable exists or not.
// +optional
Args []string
// Optional: Defaults to Docker's default.
@ -3134,7 +3161,8 @@ type EphemeralContainerCommon struct {
TerminationMessagePolicy TerminationMessagePolicy
// Required: Policy for pulling images for this container
ImagePullPolicy PullPolicy
// SecurityContext is not allowed for ephemeral containers.
// Optional: SecurityContext defines the security options the ephemeral container should be run with.
// If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext.
// +optional
SecurityContext *SecurityContext
@ -3554,11 +3582,6 @@ type LoadBalancerIngress struct {
Ports []PortStatus
}
const (
// MaxServiceTopologyKeys is the largest number of topology keys allowed on a service
MaxServiceTopologyKeys = 16
)
// IPFamily represents the IP Family (IPv4 or IPv6). This type is used
// to express the family of an IP expressed by a type (e.g. service.spec.ipFamilies).
type IPFamily string
@ -3726,29 +3749,14 @@ type ServiceSpec struct {
// +optional
PublishNotReadyAddresses bool
// topologyKeys is a preference-order list of topology keys which
// implementations of services should use to preferentially sort endpoints
// when accessing this Service, it can not be used at the same time as
// externalTrafficPolicy=Local.
// Topology keys must be valid label keys and at most 16 keys may be specified.
// Endpoints are chosen based on the first topology key with available backends.
// If this field is specified and all entries have no backends that match
// the topology of the client, the service has no backends for that client
// and connections should fail.
// The special value "*" may be used to mean "any topology". This catch-all
// value, if used, only makes sense as the last value in the list.
// If this is not specified or empty, no topology constraints will be applied.
// This field is alpha-level and is only honored by servers that enable the ServiceTopology feature.
// This field is deprecated and will be removed in a future version.
// +optional
TopologyKeys []string
// allocateLoadBalancerNodePorts defines if NodePorts will be automatically
// allocated for services with type LoadBalancer. Default is "true". It may be
// set to "false" if the cluster load-balancer does not rely on NodePorts.
// allocateLoadBalancerNodePorts may only be set for services with type LoadBalancer
// and will be cleared if the type is changed to any other type.
// This field is alpha-level and is only honored by servers that enable the ServiceLBNodePortControl feature.
// allocated for services with type LoadBalancer. Default is "true". It
// may be set to "false" if the cluster load-balancer does not rely on
// NodePorts. If the caller requests specific NodePorts (by specifying a
// value), those requests will be respected, regardless of this field.
// This field may only be set for services with type LoadBalancer and will
// be cleared if the type is changed to any other type.
// This field is beta-level and is only honored by servers that enable the ServiceLBNodePortControl feature.
// +optional
AllocateLoadBalancerNodePorts *bool
@ -4200,6 +4208,7 @@ type PodSignature struct {
// ContainerImage describe a container image
type ContainerImage struct {
// Names by which this image is known.
// +optional
Names []string
// The size of the image in bytes.
// +optional
@ -4255,11 +4264,44 @@ type NodeAddressType string
// These are valid values of node address type
const (
NodeHostName NodeAddressType = "Hostname"
NodeExternalIP NodeAddressType = "ExternalIP"
NodeInternalIP NodeAddressType = "InternalIP"
NodeExternalDNS NodeAddressType = "ExternalDNS"
// NodeHostName identifies a name of the node. Although every node can be assumed
// to have a NodeAddress of this type, its exact syntax and semantics are not
// defined, and are not consistent between different clusters.
NodeHostName NodeAddressType = "Hostname"
// NodeInternalIP identifies an IP address which is assigned to one of the node's
// network interfaces. Every node should have at least one address of this type.
//
// An internal IP is normally expected to be reachable from every other node, but
// may not be visible to hosts outside the cluster. By default it is assumed that
// kube-apiserver can reach node internal IPs, though it is possible to configure
// clusters where this is not the case.
//
// NodeInternalIP is the default type of node IP, and does not necessarily imply
// that the IP is ONLY reachable internally. If a node has multiple internal IPs,
// no specific semantics are assigned to the additional IPs.
NodeInternalIP NodeAddressType = "InternalIP"
// NodeExternalIP identifies an IP address which is, in some way, intended to be
// more usable from outside the cluster then an internal IP, though no specific
// semantics are defined. It may be a globally routable IP, though it is not
// required to be.
//
// External IPs may be assigned directly to an interface on the node, like a
// NodeInternalIP, or alternatively, packets sent to the external IP may be NAT'ed
// to an internal node IP rather than being delivered directly (making the IP less
// efficient for node-to-node traffic than a NodeInternalIP).
NodeExternalIP NodeAddressType = "ExternalIP"
// NodeInternalDNS identifies a DNS name which resolves to an IP address which has
// the characteristics of a NodeInternalIP. The IP it resolves to may or may not
// be a listed NodeInternalIP address.
NodeInternalDNS NodeAddressType = "InternalDNS"
// NodeExternalDNS identifies a DNS name which resolves to an IP address which has
// the characteristics of a NodeExternalIP. The IP it resolves to may or may not
// be a listed NodeExternalIP address.
NodeExternalDNS NodeAddressType = "ExternalDNS"
)
// NodeAddress represents node's address
@ -4439,20 +4481,6 @@ type Binding struct {
Target ObjectReference
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// EphemeralContainers is a list of ephemeral containers used with the Pod ephemeralcontainers subresource.
type EphemeralContainers struct {
metav1.TypeMeta
// +optional
metav1.ObjectMeta
// A list of ephemeral containers associated with this pod. New ephemeral containers
// may be appended to this list, but existing ephemeral containers may not be removed
// or modified.
EphemeralContainers []EphemeralContainer
}
// Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.
type Preconditions struct {
// Specifies the target UID.
@ -4900,7 +4928,7 @@ const (
// Match all pod objects that have priority class mentioned
ResourceQuotaScopePriorityClass ResourceQuotaScope = "PriorityClass"
// Match all pod objects that have cross-namespace pod (anti)affinity mentioned
// This is an alpha feature enabled by the PodAffinityNamespaceSelector feature flag.
// This is a beta feature enabled by the PodAffinityNamespaceSelector feature flag.
ResourceQuotaScopeCrossNamespacePodAffinity ResourceQuotaScope = "CrossNamespacePodAffinity"
)
@ -5364,6 +5392,16 @@ type WindowsSecurityContextOptions struct {
// PodSecurityContext, the value specified in SecurityContext takes precedence.
// +optional
RunAsUserName *string
// HostProcess determines if a container should be run as a 'Host Process' container.
// This field is alpha-level and will only be honored by components that enable the
// WindowsHostProcessContainers feature flag. Setting this field without the feature
// flag will result in errors when validating the Pod. All of a Pod's containers must
// have the same effective HostProcess value (it is not allowed to have a mix of HostProcess
// containers and non-HostProcess containers). In addition, if HostProcess is true
// then HostNetwork must also be set to true.
// +optional
HostProcess *bool
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

View File

@ -13,7 +13,6 @@ reviewers:
- vishh
- mikedanese
- liggitt
- erictune
- davidopp
- pmorie
- sttts

View File

@ -170,19 +170,22 @@ func ingressEqual(lhs, rhs *v1.LoadBalancerIngress) bool {
}
// GetAccessModesAsString returns a string representation of an array of access modes.
// modes, when present, are always in the same order: RWO,ROX,RWX.
// modes, when present, are always in the same order: RWO,ROX,RWX,RWOP.
func GetAccessModesAsString(modes []v1.PersistentVolumeAccessMode) string {
modes = removeDuplicateAccessModes(modes)
modesStr := []string{}
if containsAccessMode(modes, v1.ReadWriteOnce) {
if ContainsAccessMode(modes, v1.ReadWriteOnce) {
modesStr = append(modesStr, "RWO")
}
if containsAccessMode(modes, v1.ReadOnlyMany) {
if ContainsAccessMode(modes, v1.ReadOnlyMany) {
modesStr = append(modesStr, "ROX")
}
if containsAccessMode(modes, v1.ReadWriteMany) {
if ContainsAccessMode(modes, v1.ReadWriteMany) {
modesStr = append(modesStr, "RWX")
}
if ContainsAccessMode(modes, v1.ReadWriteOncePod) {
modesStr = append(modesStr, "RWOP")
}
return strings.Join(modesStr, ",")
}
@ -199,6 +202,8 @@ func GetAccessModesFromString(modes string) []v1.PersistentVolumeAccessMode {
accessModes = append(accessModes, v1.ReadOnlyMany)
case s == "RWX":
accessModes = append(accessModes, v1.ReadWriteMany)
case s == "RWOP":
accessModes = append(accessModes, v1.ReadWriteOncePod)
}
}
return accessModes
@ -208,14 +213,14 @@ func GetAccessModesFromString(modes string) []v1.PersistentVolumeAccessMode {
func removeDuplicateAccessModes(modes []v1.PersistentVolumeAccessMode) []v1.PersistentVolumeAccessMode {
accessModes := []v1.PersistentVolumeAccessMode{}
for _, m := range modes {
if !containsAccessMode(accessModes, m) {
if !ContainsAccessMode(accessModes, m) {
accessModes = append(accessModes, m)
}
}
return accessModes
}
func containsAccessMode(modes []v1.PersistentVolumeAccessMode, mode v1.PersistentVolumeAccessMode) bool {
func ContainsAccessMode(modes []v1.PersistentVolumeAccessMode, mode v1.PersistentVolumeAccessMode) bool {
for _, m := range modes {
if m == mode {
return true

View File

@ -531,16 +531,6 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1.EphemeralContainers)(nil), (*core.EphemeralContainers)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_EphemeralContainers_To_core_EphemeralContainers(a.(*v1.EphemeralContainers), b.(*core.EphemeralContainers), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*core.EphemeralContainers)(nil), (*v1.EphemeralContainers)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_core_EphemeralContainers_To_v1_EphemeralContainers(a.(*core.EphemeralContainers), b.(*v1.EphemeralContainers), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1.EphemeralVolumeSource)(nil), (*core.EphemeralVolumeSource)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_EphemeralVolumeSource_To_core_EphemeralVolumeSource(a.(*v1.EphemeralVolumeSource), b.(*core.EphemeralVolumeSource), scope)
}); err != nil {
@ -3522,28 +3512,6 @@ func Convert_core_EphemeralContainerCommon_To_v1_EphemeralContainerCommon(in *co
return autoConvert_core_EphemeralContainerCommon_To_v1_EphemeralContainerCommon(in, out, s)
}
func autoConvert_v1_EphemeralContainers_To_core_EphemeralContainers(in *v1.EphemeralContainers, out *core.EphemeralContainers, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
out.EphemeralContainers = *(*[]core.EphemeralContainer)(unsafe.Pointer(&in.EphemeralContainers))
return nil
}
// Convert_v1_EphemeralContainers_To_core_EphemeralContainers is an autogenerated conversion function.
func Convert_v1_EphemeralContainers_To_core_EphemeralContainers(in *v1.EphemeralContainers, out *core.EphemeralContainers, s conversion.Scope) error {
return autoConvert_v1_EphemeralContainers_To_core_EphemeralContainers(in, out, s)
}
func autoConvert_core_EphemeralContainers_To_v1_EphemeralContainers(in *core.EphemeralContainers, out *v1.EphemeralContainers, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
out.EphemeralContainers = *(*[]v1.EphemeralContainer)(unsafe.Pointer(&in.EphemeralContainers))
return nil
}
// Convert_core_EphemeralContainers_To_v1_EphemeralContainers is an autogenerated conversion function.
func Convert_core_EphemeralContainers_To_v1_EphemeralContainers(in *core.EphemeralContainers, out *v1.EphemeralContainers, s conversion.Scope) error {
return autoConvert_core_EphemeralContainers_To_v1_EphemeralContainers(in, out, s)
}
func autoConvert_v1_EphemeralVolumeSource_To_core_EphemeralVolumeSource(in *v1.EphemeralVolumeSource, out *core.EphemeralVolumeSource, s conversion.Scope) error {
out.VolumeClaimTemplate = (*core.PersistentVolumeClaimTemplate)(unsafe.Pointer(in.VolumeClaimTemplate))
return nil
@ -5149,6 +5117,7 @@ func autoConvert_v1_PersistentVolumeClaimSpec_To_core_PersistentVolumeClaimSpec(
out.StorageClassName = (*string)(unsafe.Pointer(in.StorageClassName))
out.VolumeMode = (*core.PersistentVolumeMode)(unsafe.Pointer(in.VolumeMode))
out.DataSource = (*core.TypedLocalObjectReference)(unsafe.Pointer(in.DataSource))
out.DataSourceRef = (*core.TypedLocalObjectReference)(unsafe.Pointer(in.DataSourceRef))
return nil
}
@ -5167,6 +5136,7 @@ func autoConvert_core_PersistentVolumeClaimSpec_To_v1_PersistentVolumeClaimSpec(
out.StorageClassName = (*string)(unsafe.Pointer(in.StorageClassName))
out.VolumeMode = (*v1.PersistentVolumeMode)(unsafe.Pointer(in.VolumeMode))
out.DataSource = (*v1.TypedLocalObjectReference)(unsafe.Pointer(in.DataSource))
out.DataSourceRef = (*v1.TypedLocalObjectReference)(unsafe.Pointer(in.DataSourceRef))
return nil
}
@ -7631,7 +7601,6 @@ func autoConvert_v1_ServiceSpec_To_core_ServiceSpec(in *v1.ServiceSpec, out *cor
out.HealthCheckNodePort = in.HealthCheckNodePort
out.PublishNotReadyAddresses = in.PublishNotReadyAddresses
out.SessionAffinityConfig = (*core.SessionAffinityConfig)(unsafe.Pointer(in.SessionAffinityConfig))
out.TopologyKeys = *(*[]string)(unsafe.Pointer(&in.TopologyKeys))
out.IPFamilies = *(*[]core.IPFamily)(unsafe.Pointer(&in.IPFamilies))
out.IPFamilyPolicy = (*core.IPFamilyPolicyType)(unsafe.Pointer(in.IPFamilyPolicy))
out.AllocateLoadBalancerNodePorts = (*bool)(unsafe.Pointer(in.AllocateLoadBalancerNodePorts))
@ -7662,7 +7631,6 @@ func autoConvert_core_ServiceSpec_To_v1_ServiceSpec(in *core.ServiceSpec, out *v
out.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyType(in.ExternalTrafficPolicy)
out.HealthCheckNodePort = in.HealthCheckNodePort
out.PublishNotReadyAddresses = in.PublishNotReadyAddresses
out.TopologyKeys = *(*[]string)(unsafe.Pointer(&in.TopologyKeys))
out.AllocateLoadBalancerNodePorts = (*bool)(unsafe.Pointer(in.AllocateLoadBalancerNodePorts))
out.LoadBalancerClass = (*string)(unsafe.Pointer(in.LoadBalancerClass))
out.InternalTrafficPolicy = (*v1.ServiceInternalTrafficPolicyType)(unsafe.Pointer(in.InternalTrafficPolicy))
@ -8244,6 +8212,7 @@ func autoConvert_v1_WindowsSecurityContextOptions_To_core_WindowsSecurityContext
out.GMSACredentialSpecName = (*string)(unsafe.Pointer(in.GMSACredentialSpecName))
out.GMSACredentialSpec = (*string)(unsafe.Pointer(in.GMSACredentialSpec))
out.RunAsUserName = (*string)(unsafe.Pointer(in.RunAsUserName))
out.HostProcess = (*bool)(unsafe.Pointer(in.HostProcess))
return nil
}
@ -8256,6 +8225,7 @@ func autoConvert_core_WindowsSecurityContextOptions_To_v1_WindowsSecurityContext
out.GMSACredentialSpecName = (*string)(unsafe.Pointer(in.GMSACredentialSpecName))
out.GMSACredentialSpec = (*string)(unsafe.Pointer(in.GMSACredentialSpec))
out.RunAsUserName = (*string)(unsafe.Pointer(in.RunAsUserName))
out.HostProcess = (*bool)(unsafe.Pointer(in.HostProcess))
return nil
}

View File

@ -33,7 +33,6 @@ func RegisterDefaults(scheme *runtime.Scheme) error {
scheme.AddTypeDefaultingFunc(&v1.ConfigMapList{}, func(obj interface{}) { SetObjectDefaults_ConfigMapList(obj.(*v1.ConfigMapList)) })
scheme.AddTypeDefaultingFunc(&v1.Endpoints{}, func(obj interface{}) { SetObjectDefaults_Endpoints(obj.(*v1.Endpoints)) })
scheme.AddTypeDefaultingFunc(&v1.EndpointsList{}, func(obj interface{}) { SetObjectDefaults_EndpointsList(obj.(*v1.EndpointsList)) })
scheme.AddTypeDefaultingFunc(&v1.EphemeralContainers{}, func(obj interface{}) { SetObjectDefaults_EphemeralContainers(obj.(*v1.EphemeralContainers)) })
scheme.AddTypeDefaultingFunc(&v1.LimitRange{}, func(obj interface{}) { SetObjectDefaults_LimitRange(obj.(*v1.LimitRange)) })
scheme.AddTypeDefaultingFunc(&v1.LimitRangeList{}, func(obj interface{}) { SetObjectDefaults_LimitRangeList(obj.(*v1.LimitRangeList)) })
scheme.AddTypeDefaultingFunc(&v1.Namespace{}, func(obj interface{}) { SetObjectDefaults_Namespace(obj.(*v1.Namespace)) })
@ -85,59 +84,6 @@ func SetObjectDefaults_EndpointsList(in *v1.EndpointsList) {
}
}
func SetObjectDefaults_EphemeralContainers(in *v1.EphemeralContainers) {
for i := range in.EphemeralContainers {
a := &in.EphemeralContainers[i]
SetDefaults_EphemeralContainer(a)
for j := range a.EphemeralContainerCommon.Ports {
b := &a.EphemeralContainerCommon.Ports[j]
if b.Protocol == "" {
b.Protocol = "TCP"
}
}
for j := range a.EphemeralContainerCommon.Env {
b := &a.EphemeralContainerCommon.Env[j]
if b.ValueFrom != nil {
if b.ValueFrom.FieldRef != nil {
SetDefaults_ObjectFieldSelector(b.ValueFrom.FieldRef)
}
}
}
SetDefaults_ResourceList(&a.EphemeralContainerCommon.Resources.Limits)
SetDefaults_ResourceList(&a.EphemeralContainerCommon.Resources.Requests)
if a.EphemeralContainerCommon.LivenessProbe != nil {
SetDefaults_Probe(a.EphemeralContainerCommon.LivenessProbe)
if a.EphemeralContainerCommon.LivenessProbe.Handler.HTTPGet != nil {
SetDefaults_HTTPGetAction(a.EphemeralContainerCommon.LivenessProbe.Handler.HTTPGet)
}
}
if a.EphemeralContainerCommon.ReadinessProbe != nil {
SetDefaults_Probe(a.EphemeralContainerCommon.ReadinessProbe)
if a.EphemeralContainerCommon.ReadinessProbe.Handler.HTTPGet != nil {
SetDefaults_HTTPGetAction(a.EphemeralContainerCommon.ReadinessProbe.Handler.HTTPGet)
}
}
if a.EphemeralContainerCommon.StartupProbe != nil {
SetDefaults_Probe(a.EphemeralContainerCommon.StartupProbe)
if a.EphemeralContainerCommon.StartupProbe.Handler.HTTPGet != nil {
SetDefaults_HTTPGetAction(a.EphemeralContainerCommon.StartupProbe.Handler.HTTPGet)
}
}
if a.EphemeralContainerCommon.Lifecycle != nil {
if a.EphemeralContainerCommon.Lifecycle.PostStart != nil {
if a.EphemeralContainerCommon.Lifecycle.PostStart.HTTPGet != nil {
SetDefaults_HTTPGetAction(a.EphemeralContainerCommon.Lifecycle.PostStart.HTTPGet)
}
}
if a.EphemeralContainerCommon.Lifecycle.PreStop != nil {
if a.EphemeralContainerCommon.Lifecycle.PreStop.HTTPGet != nil {
SetDefaults_HTTPGetAction(a.EphemeralContainerCommon.Lifecycle.PreStop.HTTPGet)
}
}
}
}
}
func SetObjectDefaults_LimitRange(in *v1.LimitRange) {
for i := range in.Spec.Limits {
a := &in.Spec.Limits[i]

View File

@ -13,7 +13,6 @@ reviewers:
- vishh
- mikedanese
- liggitt
- erictune
- davidopp
- pmorie
- sttts

View File

@ -109,7 +109,7 @@ func validateV1EventSeries(event *core.Event) field.ErrorList {
zeroTime := time.Time{}
if event.Series != nil {
if event.Series.Count < 2 {
allErrs = append(allErrs, field.Invalid(field.NewPath("series.count"), "", fmt.Sprintf("should be at least 2")))
allErrs = append(allErrs, field.Invalid(field.NewPath("series.count"), "", "should be at least 2"))
}
if event.Series.LastObservedTime.Time == zeroTime {
allErrs = append(allErrs, field.Required(field.NewPath("series.lastObservedTime"), ""))

View File

@ -35,7 +35,6 @@ import (
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
v1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/util/intstr"
@ -49,7 +48,6 @@ import (
"k8s.io/kubernetes/pkg/apis/core/helper"
podshelper "k8s.io/kubernetes/pkg/apis/core/pods"
corev1 "k8s.io/kubernetes/pkg/apis/core/v1"
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
"k8s.io/kubernetes/pkg/capabilities"
"k8s.io/kubernetes/pkg/cluster/ports"
"k8s.io/kubernetes/pkg/features"
@ -86,6 +84,7 @@ var allowedEphemeralContainerFields = map[string]bool{
"TerminationMessagePath": true,
"TerminationMessagePolicy": true,
"ImagePullPolicy": true,
"SecurityContext": true,
"Stdin": true,
"StdinOnce": true,
"TTY": true,
@ -283,7 +282,7 @@ var ValidateClusterName = apimachineryvalidation.ValidateClusterName
// (where it should be) and this file.
var ValidateClassName = apimachineryvalidation.NameIsDNSSubdomain
// ValidatePiorityClassName can be used to check whether the given priority
// ValidatePriorityClassName can be used to check whether the given priority
// class name is valid.
var ValidatePriorityClassName = apimachineryvalidation.NameIsDNSSubdomain
@ -1217,8 +1216,8 @@ func validateMountPropagation(mountPropagation *core.MountPropagationMode, conta
}
if container == nil {
// The container is not available yet, e.g. during validation of
// PodPreset. Stop validation now, Pod validation will refuse final
// The container is not available yet.
// Stop validation now, Pod validation will refuse final
// Pods with Bidirectional propagation in non-privileged containers.
return allErrs
}
@ -1609,22 +1608,23 @@ func validateEphemeralVolumeSource(ephemeral *core.EphemeralVolumeSource, fldPat
if ephemeral.VolumeClaimTemplate == nil {
allErrs = append(allErrs, field.Required(fldPath.Child("volumeClaimTemplate"), ""))
} else {
allErrs = append(allErrs, ValidatePersistentVolumeClaimTemplate(ephemeral.VolumeClaimTemplate, fldPath.Child("volumeClaimTemplate"))...)
opts := ValidationOptionsForPersistentVolumeClaimTemplate(ephemeral.VolumeClaimTemplate, nil)
allErrs = append(allErrs, ValidatePersistentVolumeClaimTemplate(ephemeral.VolumeClaimTemplate, fldPath.Child("volumeClaimTemplate"), opts)...)
}
return allErrs
}
// ValidatePersistentVolumeClaimTemplate verifies that the embedded object meta and spec are valid.
// Checking of the object data is very minimal because only labels and annotations are used.
func ValidatePersistentVolumeClaimTemplate(claimTemplate *core.PersistentVolumeClaimTemplate, fldPath *field.Path) field.ErrorList {
func ValidatePersistentVolumeClaimTemplate(claimTemplate *core.PersistentVolumeClaimTemplate, fldPath *field.Path, opts PersistentVolumeClaimSpecValidationOptions) field.ErrorList {
allErrs := validatePersistentVolumeClaimTemplateObjectMeta(&claimTemplate.ObjectMeta, fldPath.Child("metadata"))
allErrs = append(allErrs, ValidatePersistentVolumeClaimSpec(&claimTemplate.Spec, fldPath.Child("spec"))...)
allErrs = append(allErrs, ValidatePersistentVolumeClaimSpec(&claimTemplate.Spec, fldPath.Child("spec"), opts)...)
return allErrs
}
func validatePersistentVolumeClaimTemplateObjectMeta(objMeta *metav1.ObjectMeta, fldPath *field.Path) field.ErrorList {
allErrs := apimachineryvalidation.ValidateAnnotations(objMeta.Annotations, fldPath.Child("annotations"))
allErrs = append(allErrs, v1validation.ValidateLabels(objMeta.Labels, fldPath.Child("labels"))...)
allErrs = append(allErrs, unversionedvalidation.ValidateLabels(objMeta.Labels, fldPath.Child("labels"))...)
// All other fields are not supported and thus must not be set
// to avoid confusion. We could reject individual fields,
// but then adding a new one to ObjectMeta wouldn't be checked
@ -1639,6 +1639,12 @@ var allowedPVCTemplateObjectMetaFields = map[string]bool{
"Labels": true,
}
// PersistentVolumeSpecValidationOptions contains the different settings for PeristentVolume validation
type PersistentVolumeSpecValidationOptions struct {
// Allow spec to contain the "ReadWiteOncePod" access mode
AllowReadWriteOncePod bool
}
// ValidatePersistentVolumeName checks that a name is appropriate for a
// PersistentVolumeName object.
var ValidatePersistentVolumeName = apimachineryvalidation.NameIsDNSSubdomain
@ -1649,7 +1655,22 @@ var supportedReclaimPolicy = sets.NewString(string(core.PersistentVolumeReclaimD
var supportedVolumeModes = sets.NewString(string(core.PersistentVolumeBlock), string(core.PersistentVolumeFilesystem))
func ValidatePersistentVolumeSpec(pvSpec *core.PersistentVolumeSpec, pvName string, validateInlinePersistentVolumeSpec bool, fldPath *field.Path) field.ErrorList {
func ValidationOptionsForPersistentVolume(pv, oldPv *core.PersistentVolume) PersistentVolumeSpecValidationOptions {
opts := PersistentVolumeSpecValidationOptions{
AllowReadWriteOncePod: utilfeature.DefaultFeatureGate.Enabled(features.ReadWriteOncePod),
}
if oldPv == nil {
// If there's no old PV, use the options based solely on feature enablement
return opts
}
if helper.ContainsAccessMode(oldPv.Spec.AccessModes, core.ReadWriteOncePod) {
// If the old object allowed "ReadWriteOncePod", continue to allow it in the new object
opts.AllowReadWriteOncePod = true
}
return opts
}
func ValidatePersistentVolumeSpec(pvSpec *core.PersistentVolumeSpec, pvName string, validateInlinePersistentVolumeSpec bool, fldPath *field.Path, opts PersistentVolumeSpecValidationOptions) field.ErrorList {
allErrs := field.ErrorList{}
if validateInlinePersistentVolumeSpec {
@ -1667,10 +1688,26 @@ func ValidatePersistentVolumeSpec(pvSpec *core.PersistentVolumeSpec, pvName stri
if len(pvSpec.AccessModes) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("accessModes"), ""))
}
expandedSupportedAccessModes := sets.StringKeySet(supportedAccessModes)
if opts.AllowReadWriteOncePod {
expandedSupportedAccessModes.Insert(string(core.ReadWriteOncePod))
}
foundReadWriteOncePod, foundNonReadWriteOncePod := false, false
for _, mode := range pvSpec.AccessModes {
if !supportedAccessModes.Has(string(mode)) {
allErrs = append(allErrs, field.NotSupported(fldPath.Child("accessModes"), mode, supportedAccessModes.List()))
if !expandedSupportedAccessModes.Has(string(mode)) {
allErrs = append(allErrs, field.NotSupported(fldPath.Child("accessModes"), mode, expandedSupportedAccessModes.List()))
}
if mode == core.ReadWriteOncePod {
foundReadWriteOncePod = true
} else if supportedAccessModes.Has(string(mode)) {
foundNonReadWriteOncePod = true
}
}
if foundReadWriteOncePod && foundNonReadWriteOncePod {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("accessModes"), "may not use ReadWriteOncePod with other access modes"))
}
if !validateInlinePersistentVolumeSpec {
@ -1923,17 +1960,17 @@ func ValidatePersistentVolumeSpec(pvSpec *core.PersistentVolumeSpec, pvName stri
return allErrs
}
func ValidatePersistentVolume(pv *core.PersistentVolume) field.ErrorList {
func ValidatePersistentVolume(pv *core.PersistentVolume, opts PersistentVolumeSpecValidationOptions) field.ErrorList {
metaPath := field.NewPath("metadata")
allErrs := ValidateObjectMeta(&pv.ObjectMeta, false, ValidatePersistentVolumeName, metaPath)
allErrs = append(allErrs, ValidatePersistentVolumeSpec(&pv.Spec, pv.ObjectMeta.Name, false, field.NewPath("spec"))...)
allErrs = append(allErrs, ValidatePersistentVolumeSpec(&pv.Spec, pv.ObjectMeta.Name, false, field.NewPath("spec"), opts)...)
return allErrs
}
// ValidatePersistentVolumeUpdate tests to see if the update is legal for an end user to make.
// newPv is updated with fields that cannot be changed.
func ValidatePersistentVolumeUpdate(newPv, oldPv *core.PersistentVolume) field.ErrorList {
allErrs := ValidatePersistentVolume(newPv)
func ValidatePersistentVolumeUpdate(newPv, oldPv *core.PersistentVolume, opts PersistentVolumeSpecValidationOptions) field.ErrorList {
allErrs := ValidatePersistentVolume(newPv, opts)
// if oldPV does not have ControllerExpandSecretRef then allow it to be set
if (oldPv.Spec.CSI != nil && oldPv.Spec.CSI.ControllerExpandSecretRef == nil) &&
@ -1966,15 +2003,72 @@ func ValidatePersistentVolumeStatusUpdate(newPv, oldPv *core.PersistentVolume) f
return allErrs
}
// PersistentVolumeClaimSpecValidationOptions contains the different settings for PersistentVolumeClaim validation
type PersistentVolumeClaimSpecValidationOptions struct {
// Allow spec to contain the "ReadWiteOncePod" access mode
AllowReadWriteOncePod bool
}
func ValidationOptionsForPersistentVolumeClaim(pvc, oldPvc *core.PersistentVolumeClaim) PersistentVolumeClaimSpecValidationOptions {
opts := PersistentVolumeClaimSpecValidationOptions{
AllowReadWriteOncePod: utilfeature.DefaultFeatureGate.Enabled(features.ReadWriteOncePod),
}
if oldPvc == nil {
// If there's no old PVC, use the options based solely on feature enablement
return opts
}
if helper.ContainsAccessMode(oldPvc.Spec.AccessModes, core.ReadWriteOncePod) {
// If the old object allowed "ReadWriteOncePod", continue to allow it in the new object
opts.AllowReadWriteOncePod = true
}
return opts
}
func ValidationOptionsForPersistentVolumeClaimTemplate(claimTemplate, oldClaimTemplate *core.PersistentVolumeClaimTemplate) PersistentVolumeClaimSpecValidationOptions {
opts := PersistentVolumeClaimSpecValidationOptions{
AllowReadWriteOncePod: utilfeature.DefaultFeatureGate.Enabled(features.ReadWriteOncePod),
}
if oldClaimTemplate == nil {
// If there's no old PVC template, use the options based solely on feature enablement
return opts
}
if helper.ContainsAccessMode(oldClaimTemplate.Spec.AccessModes, core.ReadWriteOncePod) {
// If the old object allowed "ReadWriteOncePod", continue to allow it in the new object
opts.AllowReadWriteOncePod = true
}
return opts
}
// ValidatePersistentVolumeClaim validates a PersistentVolumeClaim
func ValidatePersistentVolumeClaim(pvc *core.PersistentVolumeClaim) field.ErrorList {
func ValidatePersistentVolumeClaim(pvc *core.PersistentVolumeClaim, opts PersistentVolumeClaimSpecValidationOptions) field.ErrorList {
allErrs := ValidateObjectMeta(&pvc.ObjectMeta, true, ValidatePersistentVolumeName, field.NewPath("metadata"))
allErrs = append(allErrs, ValidatePersistentVolumeClaimSpec(&pvc.Spec, field.NewPath("spec"))...)
allErrs = append(allErrs, ValidatePersistentVolumeClaimSpec(&pvc.Spec, field.NewPath("spec"), opts)...)
return allErrs
}
// validateDataSource validates a DataSource/DataSourceRef in a PersistentVolumeClaimSpec
func validateDataSource(dataSource *core.TypedLocalObjectReference, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(dataSource.Name) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
}
if len(dataSource.Kind) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("kind"), ""))
}
apiGroup := ""
if dataSource.APIGroup != nil {
apiGroup = *dataSource.APIGroup
}
if len(apiGroup) == 0 && dataSource.Kind != "PersistentVolumeClaim" {
allErrs = append(allErrs, field.Invalid(fldPath, dataSource.Kind, ""))
}
return allErrs
}
// ValidatePersistentVolumeClaimSpec validates a PersistentVolumeClaimSpec
func ValidatePersistentVolumeClaimSpec(spec *core.PersistentVolumeClaimSpec, fldPath *field.Path) field.ErrorList {
func ValidatePersistentVolumeClaimSpec(spec *core.PersistentVolumeClaimSpec, fldPath *field.Path, opts PersistentVolumeClaimSpecValidationOptions) field.ErrorList {
allErrs := field.ErrorList{}
if len(spec.AccessModes) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("accessModes"), "at least 1 access mode is required"))
@ -1982,11 +2076,28 @@ func ValidatePersistentVolumeClaimSpec(spec *core.PersistentVolumeClaimSpec, fld
if spec.Selector != nil {
allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, fldPath.Child("selector"))...)
}
expandedSupportedAccessModes := sets.StringKeySet(supportedAccessModes)
if opts.AllowReadWriteOncePod {
expandedSupportedAccessModes.Insert(string(core.ReadWriteOncePod))
}
foundReadWriteOncePod, foundNonReadWriteOncePod := false, false
for _, mode := range spec.AccessModes {
if mode != core.ReadWriteOnce && mode != core.ReadOnlyMany && mode != core.ReadWriteMany {
allErrs = append(allErrs, field.NotSupported(fldPath.Child("accessModes"), mode, supportedAccessModes.List()))
if !expandedSupportedAccessModes.Has(string(mode)) {
allErrs = append(allErrs, field.NotSupported(fldPath.Child("accessModes"), mode, expandedSupportedAccessModes.List()))
}
if mode == core.ReadWriteOncePod {
foundReadWriteOncePod = true
} else if supportedAccessModes.Has(string(mode)) {
foundNonReadWriteOncePod = true
}
}
if foundReadWriteOncePod && foundNonReadWriteOncePod {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("accessModes"), "may not use ReadWriteOncePod with other access modes"))
}
storageValue, ok := spec.Resources.Requests[core.ResourceStorage]
if !ok {
allErrs = append(allErrs, field.Required(fldPath.Child("resources").Key(string(core.ResourceStorage)), ""))
@ -2006,18 +2117,15 @@ func ValidatePersistentVolumeClaimSpec(spec *core.PersistentVolumeClaimSpec, fld
}
if spec.DataSource != nil {
if len(spec.DataSource.Name) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("dataSource", "name"), ""))
}
if len(spec.DataSource.Kind) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("dataSource", "kind"), ""))
}
apiGroup := ""
if spec.DataSource.APIGroup != nil {
apiGroup = *spec.DataSource.APIGroup
}
if len(apiGroup) == 0 && spec.DataSource.Kind != "PersistentVolumeClaim" {
allErrs = append(allErrs, field.Invalid(fldPath.Child("dataSource"), spec.DataSource.Kind, ""))
allErrs = append(allErrs, validateDataSource(spec.DataSource, fldPath.Child("dataSource"))...)
}
if spec.DataSourceRef != nil {
allErrs = append(allErrs, validateDataSource(spec.DataSourceRef, fldPath.Child("dataSourceRef"))...)
}
if spec.DataSource != nil && spec.DataSourceRef != nil {
if !apiequality.Semantic.DeepEqual(spec.DataSource, spec.DataSourceRef) {
allErrs = append(allErrs, field.Invalid(fldPath, fldPath.Child("dataSource"),
"must match dataSourceRef"))
}
}
@ -2025,9 +2133,9 @@ func ValidatePersistentVolumeClaimSpec(spec *core.PersistentVolumeClaimSpec, fld
}
// ValidatePersistentVolumeClaimUpdate validates an update to a PersistentVolumeClaim
func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *core.PersistentVolumeClaim) field.ErrorList {
func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *core.PersistentVolumeClaim, opts PersistentVolumeClaimSpecValidationOptions) field.ErrorList {
allErrs := ValidateObjectMetaUpdate(&newPvc.ObjectMeta, &oldPvc.ObjectMeta, field.NewPath("metadata"))
allErrs = append(allErrs, ValidatePersistentVolumeClaim(newPvc)...)
allErrs = append(allErrs, ValidatePersistentVolumeClaim(newPvc, opts)...)
newPvcClone := newPvc.DeepCopy()
oldPvcClone := oldPvc.DeepCopy()
@ -2547,6 +2655,9 @@ func validateProbe(probe *core.Probe, fldPath *field.Path) field.ErrorList {
allErrs = append(allErrs, ValidateNonnegativeField(int64(probe.PeriodSeconds), fldPath.Child("periodSeconds"))...)
allErrs = append(allErrs, ValidateNonnegativeField(int64(probe.SuccessThreshold), fldPath.Child("successThreshold"))...)
allErrs = append(allErrs, ValidateNonnegativeField(int64(probe.FailureThreshold), fldPath.Child("failureThreshold"))...)
if probe.TerminationGracePeriodSeconds != nil && *probe.TerminationGracePeriodSeconds <= 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("terminationGracePeriodSeconds"), *probe.TerminationGracePeriodSeconds, "must be greater than 0"))
}
return allErrs
}
@ -2962,10 +3073,14 @@ const (
// restrictions in Linux libc name resolution handling.
// Max number of DNS name servers.
MaxDNSNameservers = 3
// Max number of domains in search path.
MaxDNSSearchPaths = 6
// Max number of characters in search path.
MaxDNSSearchListChars = 256
// Expanded max number of domains in the search path list.
MaxDNSSearchPathsExpanded = 32
// Expanded max number of characters in the search path.
MaxDNSSearchListCharsExpanded = 2048
// Max number of domains in the search path list.
MaxDNSSearchPathsLegacy = 6
// Max number of characters in the search path list.
MaxDNSSearchListCharsLegacy = 256
)
func validateReadinessGates(readinessGates []core.PodReadinessGate, fldPath *field.Path) field.ErrorList {
@ -2978,7 +3093,7 @@ func validateReadinessGates(readinessGates []core.PodReadinessGate, fldPath *fie
return allErrs
}
func validatePodDNSConfig(dnsConfig *core.PodDNSConfig, dnsPolicy *core.DNSPolicy, fldPath *field.Path) field.ErrorList {
func validatePodDNSConfig(dnsConfig *core.PodDNSConfig, dnsPolicy *core.DNSPolicy, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
allErrs := field.ErrorList{}
// Validate DNSNone case. Must provide at least one DNS name server.
@ -3002,12 +3117,16 @@ func validatePodDNSConfig(dnsConfig *core.PodDNSConfig, dnsPolicy *core.DNSPolic
}
}
// Validate searches.
if len(dnsConfig.Searches) > MaxDNSSearchPaths {
allErrs = append(allErrs, field.Invalid(fldPath.Child("searches"), dnsConfig.Searches, fmt.Sprintf("must not have more than %v search paths", MaxDNSSearchPaths)))
maxDNSSearchPaths, maxDNSSearchListChars := MaxDNSSearchPathsLegacy, MaxDNSSearchListCharsLegacy
if opts.AllowExpandedDNSConfig {
maxDNSSearchPaths, maxDNSSearchListChars = MaxDNSSearchPathsExpanded, MaxDNSSearchListCharsExpanded
}
if len(dnsConfig.Searches) > maxDNSSearchPaths {
allErrs = append(allErrs, field.Invalid(fldPath.Child("searches"), dnsConfig.Searches, fmt.Sprintf("must not have more than %v search paths", maxDNSSearchPaths)))
}
// Include the space between search paths.
if len(strings.Join(dnsConfig.Searches, " ")) > MaxDNSSearchListChars {
allErrs = append(allErrs, field.Invalid(fldPath.Child("searches"), dnsConfig.Searches, "must not have more than 256 characters (including spaces) in the search list"))
if len(strings.Join(dnsConfig.Searches, " ")) > maxDNSSearchListChars {
allErrs = append(allErrs, field.Invalid(fldPath.Child("searches"), dnsConfig.Searches, fmt.Sprintf("must not have more than %v characters (including spaces) in the search list", maxDNSSearchListChars)))
}
for i, search := range dnsConfig.Searches {
// it is fine to have a trailing dot
@ -3197,31 +3316,16 @@ func validateContainersOnlyForPod(containers []core.Container, fldPath *field.Pa
// PodValidationOptions contains the different settings for pod validation
type PodValidationOptions struct {
// Allow pod spec to have more than one huge page resource (with different sizes)
AllowMultipleHugePageResources bool
// Allow pod spec to use hugepages in downward API
AllowDownwardAPIHugePages bool
// Allow invalid pod-deletion-cost annotation value for backward compatibility.
AllowInvalidPodDeletionCost bool
// Allow pod spec to use non-integer multiple of huge page unit size
AllowIndivisibleHugePagesValues bool
}
// ValidatePodSingleHugePageResources checks if there are multiple huge
// pages resources in the pod object.
func ValidatePodSingleHugePageResources(pod *core.Pod, specPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
resourceSet := helper.ToPodResourcesSet(&pod.Spec)
hugePageResources := sets.NewString()
for resourceStr := range resourceSet {
if v1helper.IsHugePageResourceName(v1.ResourceName(resourceStr)) {
hugePageResources.Insert(resourceStr)
}
}
if len(hugePageResources) > 1 {
allErrs = append(allErrs, field.Invalid(specPath, hugePageResources.List(), "must use a single hugepage size in a pod spec"))
}
return allErrs
// Allow hostProcess field to be set in windows security context
AllowWindowsHostProcessField bool
// Allow more DNSSearchPaths and longer DNSSearchListChars
AllowExpandedDNSConfig bool
}
// validatePodMetadataAndSpec tests if required fields in the pod.metadata and pod.spec are set,
@ -3253,10 +3357,6 @@ func validatePodMetadataAndSpec(pod *core.Pod, opts PodValidationOptions) field.
allErrs = append(allErrs, validateContainersOnlyForPod(pod.Spec.Containers, specPath.Child("containers"))...)
allErrs = append(allErrs, validateContainersOnlyForPod(pod.Spec.InitContainers, specPath.Child("initContainers"))...)
if !opts.AllowMultipleHugePageResources {
allErrs = append(allErrs, ValidatePodSingleHugePageResources(pod, specPath)...)
}
return allErrs
}
@ -3325,9 +3425,10 @@ func ValidatePodSpec(spec *core.PodSpec, podMeta *metav1.ObjectMeta, fldPath *fi
allErrs = append(allErrs, ValidatePodSecurityContext(spec.SecurityContext, spec, fldPath, fldPath.Child("securityContext"))...)
allErrs = append(allErrs, validateImagePullSecrets(spec.ImagePullSecrets, fldPath.Child("imagePullSecrets"))...)
allErrs = append(allErrs, validateAffinity(spec.Affinity, fldPath.Child("affinity"))...)
allErrs = append(allErrs, validatePodDNSConfig(spec.DNSConfig, &spec.DNSPolicy, fldPath.Child("dnsConfig"))...)
allErrs = append(allErrs, validatePodDNSConfig(spec.DNSConfig, &spec.DNSPolicy, fldPath.Child("dnsConfig"), opts)...)
allErrs = append(allErrs, validateReadinessGates(spec.ReadinessGates, fldPath.Child("readinessGates"))...)
allErrs = append(allErrs, validateTopologySpreadConstraints(spec.TopologySpreadConstraints, fldPath.Child("topologySpreadConstraints"))...)
allErrs = append(allErrs, validateWindowsHostProcessPod(spec, fldPath, opts)...)
if len(spec.ServiceAccountName) > 0 {
for _, msg := range ValidateServiceAccountName(spec.ServiceAccountName, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("serviceAccountName"), spec.ServiceAccountName, msg))
@ -3953,14 +4054,11 @@ func ValidatePodUpdate(newPod, oldPod *core.Pod, opts PodValidationOptions) fiel
allErrs = append(allErrs, ValidatePodSpecificAnnotationUpdates(newPod, oldPod, fldPath.Child("annotations"), opts)...)
specPath := field.NewPath("spec")
if !opts.AllowMultipleHugePageResources {
allErrs = append(allErrs, ValidatePodSingleHugePageResources(newPod, specPath)...)
}
// validate updateable fields:
// 1. spec.containers[*].image
// 2. spec.initContainers[*].image
// 3. spec.activeDeadlineSeconds
// 4. spec.terminationGracePeriodSeconds
containerErrs, stop := ValidateContainerUpdates(newPod.Spec.Containers, oldPod.Spec.Containers, specPath.Child("containers"))
allErrs = append(allErrs, containerErrs...)
@ -4027,11 +4125,17 @@ func ValidatePodUpdate(newPod, oldPod *core.Pod, opts PodValidationOptions) fiel
// tolerations are checked before the deep copy, so munge those too
mungedPodSpec.Tolerations = oldPod.Spec.Tolerations // +k8s:verify-mutation:reason=clone
// Relax validation of immutable fields to allow it to be set to 1 if it was previously negative.
if oldPod.Spec.TerminationGracePeriodSeconds != nil && *oldPod.Spec.TerminationGracePeriodSeconds < 0 &&
mungedPodSpec.TerminationGracePeriodSeconds != nil && *mungedPodSpec.TerminationGracePeriodSeconds == 1 {
mungedPodSpec.TerminationGracePeriodSeconds = oldPod.Spec.TerminationGracePeriodSeconds // +k8s:verify-mutation:reason=clone
}
if !apiequality.Semantic.DeepEqual(mungedPodSpec, oldPod.Spec) {
// This diff isn't perfect, but it's a helluva lot better an "I'm not going to tell you what the difference is".
//TODO: Pinpoint the specific field that causes the invalid error after we have strategic merge diff
specDiff := diff.ObjectDiff(mungedPodSpec, oldPod.Spec)
allErrs = append(allErrs, field.Forbidden(specPath, fmt.Sprintf("pod updates may not change fields other than `spec.containers[*].image`, `spec.initContainers[*].image`, `spec.activeDeadlineSeconds` or `spec.tolerations` (only additions to existing tolerations)\n%v", specDiff)))
allErrs = append(allErrs, field.Forbidden(specPath, fmt.Sprintf("pod updates may not change fields other than `spec.containers[*].image`, `spec.initContainers[*].image`, `spec.activeDeadlineSeconds`, `spec.tolerations` (only additions to existing tolerations) or `spec.terminationGracePeriodSeconds` (allow it to be set to 1 if it was previously negative)\n%v", specDiff)))
}
return allErrs
@ -4086,11 +4190,7 @@ func ValidatePodStatusUpdate(newPod, oldPod *core.Pod, opts PodValidationOptions
allErrs = append(allErrs, ValidateContainerStateTransition(newPod.Status.InitContainerStatuses, oldPod.Status.InitContainerStatuses, fldPath.Child("initContainerStatuses"), oldPod.Spec.RestartPolicy)...)
if newIPErrs := validatePodIPs(newPod); len(newIPErrs) > 0 {
// Tolerate IP errors if IP errors already existed in the old pod. See http://issue.k8s.io/90625
// TODO(liggitt): Drop the check of oldPod in 1.20
if oldIPErrs := validatePodIPs(oldPod); len(oldIPErrs) == 0 {
allErrs = append(allErrs, newIPErrs...)
}
allErrs = append(allErrs, newIPErrs...)
}
return allErrs
@ -4319,35 +4419,6 @@ func ValidateService(service *core.Service) field.ErrorList {
ports[key] = true
}
// Validate TopologyKeys
if len(service.Spec.TopologyKeys) > 0 {
topoPath := specPath.Child("topologyKeys")
// topologyKeys is mutually exclusive with 'externalTrafficPolicy=Local'
if service.Spec.ExternalTrafficPolicy == core.ServiceExternalTrafficPolicyTypeLocal {
allErrs = append(allErrs, field.Forbidden(topoPath, "may not be specified when `externalTrafficPolicy=Local`"))
}
if len(service.Spec.TopologyKeys) > core.MaxServiceTopologyKeys {
allErrs = append(allErrs, field.TooMany(topoPath, len(service.Spec.TopologyKeys), core.MaxServiceTopologyKeys))
}
topoKeys := sets.NewString()
for i, key := range service.Spec.TopologyKeys {
keyPath := topoPath.Index(i)
if topoKeys.Has(key) {
allErrs = append(allErrs, field.Duplicate(keyPath, key))
}
topoKeys.Insert(key)
// "Any" must be the last value specified
if key == v1.TopologyKeyAny && i != len(service.Spec.TopologyKeys)-1 {
allErrs = append(allErrs, field.Invalid(keyPath, key, `"*" must be the last value specified`))
}
if key != v1.TopologyKeyAny {
for _, msg := range validation.IsQualifiedName(key) {
allErrs = append(allErrs, field.Invalid(keyPath, service.Spec.TopologyKeys, msg))
}
}
}
}
// Validate SourceRange field and annotation
_, ok := service.Annotations[core.AnnotationLoadBalancerSourceRangesKey]
if len(service.Spec.LoadBalancerSourceRanges) > 0 || ok {
@ -5408,7 +5479,7 @@ func ValidateResourceRequirements(requirements *core.ResourceRequirements, fldPa
}
if !limContainsCPUOrMemory && !reqContainsCPUOrMemory && (reqContainsHugePages || limContainsHugePages) {
allErrs = append(allErrs, field.Forbidden(fldPath, fmt.Sprintf("HugePages require cpu or memory")))
allErrs = append(allErrs, field.Forbidden(fldPath, "HugePages require cpu or memory"))
}
return allErrs
@ -5775,16 +5846,16 @@ func ValidateNonSpecialIP(ipAddress string, fldPath *field.Path) field.ErrorList
return allErrs
}
if ip.IsUnspecified() {
allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "may not be unspecified (0.0.0.0)"))
allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, fmt.Sprintf("may not be unspecified (%v)", ipAddress)))
}
if ip.IsLoopback() {
allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "may not be in the loopback range (127.0.0.0/8)"))
allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "may not be in the loopback range (127.0.0.0/8, ::1/128)"))
}
if ip.IsLinkLocalUnicast() {
allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "may not be in the link-local range (169.254.0.0/16)"))
allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "may not be in the link-local range (169.254.0.0/16, fe80::/10)"))
}
if ip.IsLinkLocalMulticast() {
allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "may not be in the link-local multicast range (224.0.0.0/24)"))
allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "may not be in the link-local multicast range (224.0.0.0/24, ff02::/10)"))
}
return allErrs
}
@ -5927,10 +5998,10 @@ func validateWindowsSecurityContextOptions(windowsOptions *core.WindowsSecurityC
if l := len(*windowsOptions.RunAsUserName); l == 0 {
allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, "runAsUserName cannot be an empty string"))
} else if ctrlRegex.MatchString(*windowsOptions.RunAsUserName) {
errMsg := fmt.Sprintf("runAsUserName cannot contain control characters")
errMsg := "runAsUserName cannot contain control characters"
allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
} else if parts := strings.Split(*windowsOptions.RunAsUserName, "\\"); len(parts) > 2 {
errMsg := fmt.Sprintf("runAsUserName cannot contain more than one backslash")
errMsg := "runAsUserName cannot contain more than one backslash"
allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
} else {
var (
@ -5957,7 +6028,7 @@ func validateWindowsSecurityContextOptions(windowsOptions *core.WindowsSecurityC
}
if l := len(user); l == 0 {
errMsg := fmt.Sprintf("runAsUserName's User cannot be empty")
errMsg := "runAsUserName's User cannot be empty"
allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
} else if l > maxRunAsUserNameUserLength {
errMsg := fmt.Sprintf("runAsUserName's User length must not be longer than %d characters", maxRunAsUserNameUserLength)
@ -5979,6 +6050,91 @@ func validateWindowsSecurityContextOptions(windowsOptions *core.WindowsSecurityC
return allErrs
}
func validateWindowsHostProcessPod(podSpec *core.PodSpec, fieldPath *field.Path, opts PodValidationOptions) field.ErrorList {
allErrs := field.ErrorList{}
// Keep track of container and hostProcess container count for validate
containerCount := 0
hostProcessContainerCount := 0
var podHostProcess *bool
if podSpec.SecurityContext != nil && podSpec.SecurityContext.WindowsOptions != nil {
podHostProcess = podSpec.SecurityContext.WindowsOptions.HostProcess
}
if !opts.AllowWindowsHostProcessField && podHostProcess != nil {
// Do not allow pods to persist data that sets hostProcess (true or false)
errMsg := "not allowed when feature gate 'WindowsHostProcessContainers' is not enabled"
allErrs = append(allErrs, field.Forbidden(fieldPath.Child("securityContext", "windowsOptions", "hostProcess"), errMsg))
return allErrs
}
hostNetwork := false
if podSpec.SecurityContext != nil {
hostNetwork = podSpec.SecurityContext.HostNetwork
}
podshelper.VisitContainersWithPath(podSpec, fieldPath, func(c *core.Container, cFieldPath *field.Path) bool {
containerCount++
var containerHostProcess *bool = nil
if c.SecurityContext != nil && c.SecurityContext.WindowsOptions != nil {
containerHostProcess = c.SecurityContext.WindowsOptions.HostProcess
}
if !opts.AllowWindowsHostProcessField && containerHostProcess != nil {
// Do not allow pods to persist data that sets hostProcess (true or false)
errMsg := "not allowed when feature gate 'WindowsHostProcessContainers' is not enabled"
allErrs = append(allErrs, field.Forbidden(cFieldPath.Child("securityContext", "windowsOptions", "hostProcess"), errMsg))
}
if podHostProcess != nil && containerHostProcess != nil && *podHostProcess != *containerHostProcess {
errMsg := fmt.Sprintf("pod hostProcess value must be identical if both are specified, was %v", *podHostProcess)
allErrs = append(allErrs, field.Invalid(cFieldPath.Child("securityContext", "windowsOptions", "hostProcess"), *containerHostProcess, errMsg))
}
switch {
case containerHostProcess != nil && *containerHostProcess:
// Container explitly sets hostProcess=true
hostProcessContainerCount++
case containerHostProcess == nil && podHostProcess != nil && *podHostProcess:
// Container inherits hostProcess=true from pod settings
hostProcessContainerCount++
}
return true
})
if hostProcessContainerCount > 0 {
// Fail Pod validation if feature is not enabled (unless podspec already exists and contains HostProcess fields) instead of dropping fields based on PRR reivew.
if !opts.AllowWindowsHostProcessField {
errMsg := "pod must not contain Windows hostProcess containers when feature gate 'WindowsHostProcessContainers' is not enabled"
allErrs = append(allErrs, field.Forbidden(fieldPath, errMsg))
return allErrs
}
// At present, if a Windows Pods contains any HostProcess containers than all containers must be
// HostProcess containers (explicitly set or inherited).
if hostProcessContainerCount != containerCount {
errMsg := "If pod contains any hostProcess containers then all containers must be HostProcess containers"
allErrs = append(allErrs, field.Invalid(fieldPath, "", errMsg))
}
// At present Windows Pods which contain HostProcess containers must also set HostNetwork.
if hostNetwork != true {
errMsg := "hostNetwork must be true if pod contains any hostProcess containers"
allErrs = append(allErrs, field.Invalid(fieldPath.Child("hostNetwork"), hostNetwork, errMsg))
}
if !capabilities.Get().AllowPrivileged {
errMsg := "hostProcess containers are disallowed by cluster policy"
allErrs = append(allErrs, field.Forbidden(fieldPath, errMsg))
}
}
return allErrs
}
func ValidatePodLogOptions(opts *core.PodLogOptions) field.ErrorList {
allErrs := field.ErrorList{}
if opts.TailLines != nil && *opts.TailLines < 0 {

View File

@ -1400,39 +1400,6 @@ func (in *EphemeralContainerCommon) DeepCopy() *EphemeralContainerCommon {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EphemeralContainers) DeepCopyInto(out *EphemeralContainers) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
if in.EphemeralContainers != nil {
in, out := &in.EphemeralContainers, &out.EphemeralContainers
*out = make([]EphemeralContainer, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EphemeralContainers.
func (in *EphemeralContainers) DeepCopy() *EphemeralContainers {
if in == nil {
return nil
}
out := new(EphemeralContainers)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *EphemeralContainers) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EphemeralVolumeSource) DeepCopyInto(out *EphemeralVolumeSource) {
*out = *in
@ -2969,6 +2936,11 @@ func (in *PersistentVolumeClaimSpec) DeepCopyInto(out *PersistentVolumeClaimSpec
*out = new(TypedLocalObjectReference)
(*in).DeepCopyInto(*out)
}
if in.DataSourceRef != nil {
in, out := &in.DataSourceRef, &out.DataSourceRef
*out = new(TypedLocalObjectReference)
(*in).DeepCopyInto(*out)
}
return
}
@ -5325,11 +5297,6 @@ func (in *ServiceSpec) DeepCopyInto(out *ServiceSpec) {
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.TopologyKeys != nil {
in, out := &in.TopologyKeys, &out.TopologyKeys
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.AllocateLoadBalancerNodePorts != nil {
in, out := &in.AllocateLoadBalancerNodePorts, &out.AllocateLoadBalancerNodePorts
*out = new(bool)
@ -5928,6 +5895,11 @@ func (in *WindowsSecurityContextOptions) DeepCopyInto(out *WindowsSecurityContex
*out = new(string)
**out = **in
}
if in.HostProcess != nil {
in, out := &in.HostProcess, &out.HostProcess
*out = new(bool)
**out = **in
}
return
}

View File

@ -11,7 +11,6 @@ reviewers:
- caesarxuchao
- mikedanese
- liggitt
- erictune
- pmorie
- sttts
- saad-ali
@ -25,7 +24,6 @@ reviewers:
- dims
- errordeveloper
- rootfs
- mml
- resouer
- therc
- pweil-

View File

@ -149,8 +149,8 @@ type NetworkPolicyPort struct {
// should be allowed by the policy. This field cannot be defined if the port field
// is not defined or if the port field is defined as a named (string) port.
// The endPort must be equal or greater than port.
// This feature is in Alpha state and should be enabled using the Feature Gate
// "NetworkPolicyEndPort".
// This feature is in Beta state and is enabled by default.
// It can be disabled using the Feature Gate "NetworkPolicyEndPort".
// +optional
EndPort *int32
}
@ -492,8 +492,8 @@ const (
type HTTPIngressPath struct {
// Path is matched against the path of an incoming request. Currently it can
// contain characters disallowed from the conventional "path" part of a URL
// as defined by RFC 3986. Paths must begin with a '/'. When unspecified,
// all paths from incoming requests are matched.
// as defined by RFC 3986. Paths must begin with a '/' and must be present
// when using PathType with value "Exact" or "Prefix".
// +optional
Path string

View File

@ -2,6 +2,7 @@
# approval on api packages bubbles to api-approvers
reviewers:
- sig-apps-api-reviewers
- sig-apps-api-approvers
- sig-auth-policy-approvers
- sig-auth-policy-reviewers