Update to kube v1.17

Signed-off-by: Humble Chirammal <hchiramm@redhat.com>
This commit is contained in:
Humble Chirammal
2020-01-14 16:08:55 +05:30
committed by mergify[bot]
parent 327fcd1b1b
commit 3af1e26d7c
1710 changed files with 289562 additions and 168638 deletions

View File

@ -23,8 +23,10 @@ import (
)
var (
// SchemeBuilder stores functions to add things to a scheme.
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
// AddToScheme applies all stored functions t oa scheme.
AddToScheme = SchemeBuilder.AddToScheme
)
// GroupName is the group name use in this package

View File

@ -81,13 +81,13 @@ const (
// ordering constraints. When a scale operation is performed with this
// strategy, new Pods will be created from the specification version indicated
// by the StatefulSet's updateRevision.
RollingUpdateStatefulSetStrategyType = "RollingUpdate"
RollingUpdateStatefulSetStrategyType StatefulSetUpdateStrategyType = "RollingUpdate"
// OnDeleteStatefulSetStrategyType triggers the legacy behavior. Version
// tracking and ordered rolling restarts are disabled. Pods are recreated
// from the StatefulSetSpec when they are manually deleted. When a scale
// operation is performed with this strategy,specification version indicated
// by the StatefulSet's currentRevision.
OnDeleteStatefulSetStrategyType = "OnDelete"
OnDeleteStatefulSetStrategyType StatefulSetUpdateStrategyType = "OnDelete"
)
// RollingUpdateStatefulSetStrategy is used to communicate parameter for RollingUpdateStatefulSetStrategyType.
@ -198,6 +198,7 @@ type StatefulSetStatus struct {
Conditions []StatefulSetCondition
}
// StatefulSetConditionType describes the condition types of StatefulSets.
type StatefulSetConditionType string
// TODO: Add valid condition types for Statefulsets.
@ -237,7 +238,7 @@ type StatefulSetList struct {
type ControllerRevision struct {
metav1.TypeMeta
// Standard object's metadata.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
// +optional
metav1.ObjectMeta
@ -262,6 +263,7 @@ type ControllerRevisionList struct {
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Deployment provides declarative updates for Pods and ReplicaSets.
type Deployment struct {
metav1.TypeMeta
// +optional
@ -276,6 +278,7 @@ type Deployment struct {
Status DeploymentStatus
}
// DeploymentSpec specifies the state of a Deployment.
type DeploymentSpec struct {
// Number of desired pods. This is a pointer to distinguish between explicit
// zero and not specified. Defaults to 1.
@ -329,8 +332,8 @@ type DeploymentSpec struct {
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// DEPRECATED.
// DeploymentRollback stores the information required to rollback a deployment.
// DEPRECATED.
type DeploymentRollback struct {
metav1.TypeMeta
// Required: This must match the Name of a deployment.
@ -342,6 +345,7 @@ type DeploymentRollback struct {
RollbackTo RollbackConfig
}
// RollbackConfig specifies the state of a revision to roll back to.
// DEPRECATED.
type RollbackConfig struct {
// The revision to rollback to. If set to 0, rollback to the last revision.
@ -356,6 +360,8 @@ const (
DefaultDeploymentUniqueLabelKey string = "pod-template-hash"
)
// DeploymentStrategy stores information about the strategy and rolling-update
// behavior of a deployment.
type DeploymentStrategy struct {
// Type of deployment. Can be "Recreate" or "RollingUpdate". Default is RollingUpdate.
// +optional
@ -370,6 +376,7 @@ type DeploymentStrategy struct {
RollingUpdate *RollingUpdateDeployment
}
// DeploymentStrategyType defines strategies with a deployment.
type DeploymentStrategyType string
const (
@ -409,6 +416,7 @@ type RollingUpdateDeployment struct {
MaxSurge intstr.IntOrString
}
// DeploymentStatus holds information about the observed status of a deployment.
type DeploymentStatus struct {
// The generation observed by the deployment controller.
// +optional
@ -446,6 +454,7 @@ type DeploymentStatus struct {
CollisionCount *int32
}
// DeploymentConditionType defines conditions of a deployment.
type DeploymentConditionType string
// These are valid conditions of a deployment.
@ -481,6 +490,7 @@ type DeploymentCondition struct {
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// DeploymentList defines multiple deployments.
type DeploymentList struct {
metav1.TypeMeta
// +optional
@ -490,9 +500,9 @@ type DeploymentList struct {
Items []Deployment
}
// DaemonSetUpdateStrategy defines a strategy to update a daemon set.
type DaemonSetUpdateStrategy struct {
// Type of daemon set update. Can be "RollingUpdate" or "OnDelete".
// Default is OnDelete.
// +optional
Type DaemonSetUpdateStrategyType
@ -505,6 +515,8 @@ type DaemonSetUpdateStrategy struct {
RollingUpdate *RollingUpdateDaemonSet
}
// DaemonSetUpdateStrategyType is a strategy according to which a daemon set
// gets updated.
type DaemonSetUpdateStrategyType string
const (
@ -623,6 +635,7 @@ type DaemonSetStatus struct {
Conditions []DaemonSetCondition
}
// DaemonSetConditionType defines a daemon set condition.
type DaemonSetConditionType string
// TODO: Add valid condition types of a DaemonSet.
@ -647,12 +660,12 @@ type DaemonSetCondition struct {
type DaemonSet struct {
metav1.TypeMeta
// Standard object's metadata.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
// +optional
metav1.ObjectMeta
// The desired behavior of this daemon set.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
// +optional
Spec DaemonSetSpec
@ -660,16 +673,16 @@ type DaemonSet struct {
// out of date by some window of time.
// Populated by the system.
// Read-only.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
// +optional
Status DaemonSetStatus
}
const (
// DEPRECATED: DefaultDaemonSetUniqueLabelKey is used instead.
// DaemonSetTemplateGenerationKey is the key of the labels that is added
// to daemon set pods to distinguish between old and new pod templates
// during DaemonSet template update.
// DEPRECATED: DefaultDaemonSetUniqueLabelKey is used instead.
DaemonSetTemplateGenerationKey string = "pod-template-generation"
)
@ -679,7 +692,7 @@ const (
type DaemonSetList struct {
metav1.TypeMeta
// Standard list metadata.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
// +optional
metav1.ListMeta
@ -768,6 +781,7 @@ type ReplicaSetStatus struct {
Conditions []ReplicaSetCondition
}
// ReplicaSetConditionType is a condition of a replica set.
type ReplicaSetConditionType string
// These are valid conditions of a replica set.

View File

@ -255,6 +255,7 @@ func ValidateDaemonSetUpdate(ds, oldDS *apps.DaemonSet) field.ErrorList {
return allErrs
}
// ValidateDaemonSetSpecUpdate tests if an update to a DaemonSetSpec is valid.
func ValidateDaemonSetSpecUpdate(newSpec, oldSpec *apps.DaemonSetSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
@ -291,7 +292,7 @@ func validateDaemonSetStatus(status *apps.DaemonSetStatus, fldPath *field.Path)
return allErrs
}
// ValidateDaemonSetStatusUpdate validates tests if required fields in the DaemonSet Status section
// ValidateDaemonSetStatusUpdate tests if required fields in the DaemonSet Status section
func ValidateDaemonSetStatusUpdate(ds, oldDS *apps.DaemonSet) field.ErrorList {
allErrs := apivalidation.ValidateObjectMetaUpdate(&ds.ObjectMeta, &oldDS.ObjectMeta, field.NewPath("metadata"))
allErrs = append(allErrs, validateDaemonSetStatus(&ds.Status, field.NewPath("status"))...)
@ -340,6 +341,7 @@ func ValidateDaemonSetSpec(spec *apps.DaemonSetSpec, fldPath *field.Path) field.
return allErrs
}
// ValidateRollingUpdateDaemonSet validates a given RollingUpdateDaemonSet.
func ValidateRollingUpdateDaemonSet(rollingUpdate *apps.RollingUpdateDaemonSet, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
@ -352,6 +354,7 @@ func ValidateRollingUpdateDaemonSet(rollingUpdate *apps.RollingUpdateDaemonSet,
return allErrs
}
// ValidateDaemonSetUpdateStrategy validates a given DaemonSetUpdateStrategy.
func ValidateDaemonSetUpdateStrategy(strategy *apps.DaemonSetUpdateStrategy, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
switch strategy.Type {
@ -378,6 +381,8 @@ var ValidateDaemonSetName = apimachineryvalidation.NameIsDNSSubdomain
// ValidateDeploymentName validates that the given name can be used as a deployment name.
var ValidateDeploymentName = apimachineryvalidation.NameIsDNSSubdomain
// ValidatePositiveIntOrPercent tests if a given value is a valid int or
// percentage.
func ValidatePositiveIntOrPercent(intOrPercent intstr.IntOrString, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
switch intOrPercent.Type {
@ -412,6 +417,8 @@ func getIntOrPercentValue(intOrStringValue intstr.IntOrString) int {
return intOrStringValue.IntValue()
}
// IsNotMoreThan100Percent tests is a value can be represented as a percentage
// and if this value is not more than 100%.
func IsNotMoreThan100Percent(intOrStringValue intstr.IntOrString, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
value, isPercent := getPercentValue(intOrStringValue)
@ -422,6 +429,7 @@ func IsNotMoreThan100Percent(intOrStringValue intstr.IntOrString, fldPath *field
return allErrs
}
// ValidateRollingUpdateDeployment validates a given RollingUpdateDeployment.
func ValidateRollingUpdateDeployment(rollingUpdate *apps.RollingUpdateDeployment, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
@ -435,6 +443,7 @@ func ValidateRollingUpdateDeployment(rollingUpdate *apps.RollingUpdateDeployment
return allErrs
}
// ValidateDeploymentStrategy validates given DeploymentStrategy.
func ValidateDeploymentStrategy(strategy *apps.DeploymentStrategy, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
switch strategy.Type {
@ -456,6 +465,7 @@ func ValidateDeploymentStrategy(strategy *apps.DeploymentStrategy, fldPath *fiel
return allErrs
}
// ValidateRollback validates given RollbackConfig.
func ValidateRollback(rollback *apps.RollbackConfig, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
v := rollback.Revision
@ -530,12 +540,15 @@ func ValidateDeploymentStatus(status *apps.DeploymentStatus, fldPath *field.Path
return allErrs
}
// ValidateDeploymentUpdate tests if an update to a Deployment is valid.
func ValidateDeploymentUpdate(update, old *apps.Deployment) field.ErrorList {
allErrs := apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))
allErrs = append(allErrs, ValidateDeploymentSpec(&update.Spec, field.NewPath("spec"))...)
return allErrs
}
// ValidateDeploymentStatusUpdate tests if a an update to a Deployment status
// is valid.
func ValidateDeploymentStatusUpdate(update, old *apps.Deployment) field.ErrorList {
allErrs := apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))
fldPath := field.NewPath("status")
@ -550,12 +563,14 @@ func ValidateDeploymentStatusUpdate(update, old *apps.Deployment) field.ErrorLis
return allErrs
}
// ValidateDeployment validates a given Deployment.
func ValidateDeployment(obj *apps.Deployment) field.ErrorList {
allErrs := apivalidation.ValidateObjectMeta(&obj.ObjectMeta, true, ValidateDeploymentName, field.NewPath("metadata"))
allErrs = append(allErrs, ValidateDeploymentSpec(&obj.Spec, field.NewPath("spec"))...)
return allErrs
}
// ValidateDeploymentRollback validates a given DeploymentRollback.
func ValidateDeploymentRollback(obj *apps.DeploymentRollback) field.ErrorList {
allErrs := apivalidation.ValidateAnnotations(obj.UpdatedAnnotations, field.NewPath("updatedAnnotations"))
if len(obj.Name) == 0 {
@ -594,6 +609,7 @@ func ValidateReplicaSetStatusUpdate(rs, oldRs *apps.ReplicaSet) field.ErrorList
return allErrs
}
// ValidateReplicaSetStatus validates a given ReplicaSetStatus.
func ValidateReplicaSetStatus(status apps.ReplicaSetStatus, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.Replicas), fldPath.Child("replicas"))...)

View File

@ -27,15 +27,15 @@ import (
// Scale represents a scaling request for a resource.
type Scale struct {
metav1.TypeMeta
// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata.
// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata.
// +optional
metav1.ObjectMeta
// defines the behavior of the scale. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status.
// defines the behavior of the scale. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status.
// +optional
Spec ScaleSpec
// current status of the scale. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status. Read-only.
// current status of the scale. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status. Read-only.
// +optional
Status ScaleStatus
}
@ -62,7 +62,7 @@ type ScaleStatus struct {
// CrossVersionObjectReference contains enough information to let you identify the referred resource.
type CrossVersionObjectReference struct {
// Kind of the referent; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds"
// Kind of the referent; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds"
Kind string
// Name of the referent; More info: http://kubernetes.io/docs/user-guide/identifiers#names
Name string
@ -76,8 +76,11 @@ type HorizontalPodAutoscalerSpec struct {
// ScaleTargetRef points to the target resource to scale, and is used to the pods for which metrics
// should be collected, as well as to actually change the replica count.
ScaleTargetRef CrossVersionObjectReference
// MinReplicas is the lower limit for the number of replicas to which the autoscaler can scale down.
// It defaults to 1 pod.
// minReplicas is the lower limit for the number of replicas to which the autoscaler
// can scale down. It defaults to 1 pod. minReplicas is allowed to be 0 if the
// alpha feature gate HPAScaleToZero is enabled and at least one Object or External
// metric is configured. Scaling is active as long as at least one metric value is
// available.
// +optional
MinReplicas *int32
// MaxReplicas is the upper limit for the number of replicas to which the autoscaler can scale up.
@ -97,7 +100,7 @@ type HorizontalPodAutoscalerSpec struct {
// MetricSourceType indicates the type of metric.
type MetricSourceType string
var (
const (
// ObjectMetricSourceType is a metric describing a kubernetes object
// (for example, hits-per-second on an Ingress object).
ObjectMetricSourceType MetricSourceType = "Object"
@ -225,7 +228,7 @@ type MetricTarget struct {
// "Value", "AverageValue", or "Utilization"
type MetricTargetType string
var (
const (
// UtilizationMetricType is a possible value for MetricTarget.Type.
UtilizationMetricType MetricTargetType = "Utilization"
// ValueMetricType is a possible value for MetricTarget.Type.
@ -279,7 +282,7 @@ const (
// a HorizontalPodAutoscaler.
type HorizontalPodAutoscalerConditionType string
var (
const (
// ScalingActive indicates that the HPA controller is able to scale if necessary:
// it's correctly configured, can fetch the desired metrics, and isn't disabled.
ScalingActive HorizontalPodAutoscalerConditionType = "ScalingActive"
@ -391,12 +394,12 @@ type MetricValueStatus struct {
type HorizontalPodAutoscaler struct {
metav1.TypeMeta
// Metadata is the standard object metadata.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
// +optional
metav1.ObjectMeta
// Spec is the specification for the behaviour of the autoscaler.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status.
// +optional
Spec HorizontalPodAutoscalerSpec

View File

@ -27,17 +27,17 @@ import (
type Job struct {
metav1.TypeMeta
// Standard object's metadata.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
// +optional
metav1.ObjectMeta
// Specification of the desired behavior of a job.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
// +optional
Spec JobSpec
// Current status of a job.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
// +optional
Status JobStatus
}
@ -48,7 +48,7 @@ type Job struct {
type JobList struct {
metav1.TypeMeta
// Standard list metadata.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
// +optional
metav1.ListMeta
@ -62,12 +62,12 @@ type JobList struct {
type JobTemplate struct {
metav1.TypeMeta
// Standard object's metadata.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
// +optional
metav1.ObjectMeta
// Defines jobs that will be created from this template.
// https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
// +optional
Template JobTemplateSpec
}
@ -75,12 +75,12 @@ type JobTemplate struct {
// JobTemplateSpec describes the data a Job should have when created from a template
type JobTemplateSpec struct {
// Standard object's metadata of the jobs created from this template.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
// +optional
metav1.ObjectMeta
// Specification of the desired behavior of the job.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
// +optional
Spec JobSpec
}
@ -220,17 +220,17 @@ type JobCondition struct {
type CronJob struct {
metav1.TypeMeta
// Standard object's metadata.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
// +optional
metav1.ObjectMeta
// Specification of the desired behavior of a cron job, including the schedule.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
// +optional
Spec CronJobSpec
// Current status of a cron job.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
// +optional
Status CronJobStatus
}
@ -241,7 +241,7 @@ type CronJob struct {
type CronJobList struct {
metav1.TypeMeta
// Standard list metadata.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
// +optional
metav1.ListMeta

View File

@ -26,7 +26,7 @@ const (
// PodPresetOptOutAnnotationKey represents the annotation key for a pod to exempt itself from pod preset manipulation
PodPresetOptOutAnnotationKey string = "podpreset.admission.kubernetes.io/exclude"
// MirrorAnnotationKey represents the annotation key set by kubelets when creating mirror pods
// MirrorPodAnnotationKey represents the annotation key set by kubelets when creating mirror pods
MirrorPodAnnotationKey string = "kubernetes.io/config.mirror"
// TolerationsAnnotationKey represents the key of tolerations data (json serialized)
@ -56,7 +56,7 @@ const (
// in the Annotations of a Node.
PreferAvoidPodsAnnotationKey string = "scheduler.alpha.kubernetes.io/preferAvoidPods"
// ObjectTTLAnnotations represents a suggestion for kubelet for how long it can cache
// ObjectTTLAnnotationKey represents a suggestion for kubelet for how long it can cache
// an object (e.g. secret, config map) before fetching it again from apiserver.
// This annotation can be attached to node.
ObjectTTLAnnotationKey string = "node.alpha.kubernetes.io/ttl"
@ -65,7 +65,7 @@ const (
// the kubelet prior to running
BootstrapCheckpointAnnotationKey string = "node.kubernetes.io/bootstrap-checkpoint"
// annotation key prefix used to identify non-convertible json paths.
// NonConvertibleAnnotationPrefix annotation key prefix used to identify non-convertible json paths.
NonConvertibleAnnotationPrefix = "non-convertible.kubernetes.io"
kubectlPrefix = "kubectl.kubernetes.io/"

View File

@ -16,7 +16,7 @@ limitations under the License.
// +k8s:deepcopy-gen=package
// Package api contains the latest (or "internal") version of the
// Package core contains the latest (or "internal") version of the
// Kubernetes API objects. This is the API objects as represented in memory.
// The contract presented to clients is located in the versioned packages,
// which are sub-directories. The first one is "v1". Those packages

View File

@ -281,34 +281,6 @@ func IsStandardFinalizerName(str string) bool {
return standardFinalizers.Has(str)
}
// LoadBalancerStatusEqual checks if the status of the load balancer is equal to the target status
// TODO: make method on LoadBalancerStatus?
func LoadBalancerStatusEqual(l, r *core.LoadBalancerStatus) bool {
return ingressSliceEqual(l.Ingress, r.Ingress)
}
func ingressSliceEqual(lhs, rhs []core.LoadBalancerIngress) bool {
if len(lhs) != len(rhs) {
return false
}
for i := range lhs {
if !ingressEqual(&lhs[i], &rhs[i]) {
return false
}
}
return true
}
func ingressEqual(lhs, rhs *core.LoadBalancerIngress) bool {
if lhs.IP != rhs.IP {
return false
}
if lhs.Hostname != rhs.Hostname {
return false
}
return true
}
// GetAccessModesAsString returns a string representation of an array of access modes.
// modes, when present, are always in the same order: RWO,ROX,RWX.
func GetAccessModesAsString(modes []core.PersistentVolumeAccessMode) string {

View File

@ -24,5 +24,8 @@ import "encoding/json"
var _ = json.Marshaler(&AvoidPods{})
var _ = json.Unmarshaler(&AvoidPods{})
// MarshalJSON panics to prevent marshalling of internal structs
func (AvoidPods) MarshalJSON() ([]byte, error) { panic("do not marshal internal struct") }
func (*AvoidPods) UnmarshalJSON([]byte) error { panic("do not unmarshal to internal struct") }
// UnmarshalJSON panics to prevent unmarshalling of internal structs
func (*AvoidPods) UnmarshalJSON([]byte) error { panic("do not unmarshal to internal struct") }

View File

@ -23,12 +23,15 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
)
// SetGroupVersionKind sets the API version and kind of the object reference
func (obj *ObjectReference) SetGroupVersionKind(gvk schema.GroupVersionKind) {
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
}
// GroupVersionKind returns the API version and kind of the object reference
func (obj *ObjectReference) GroupVersionKind() schema.GroupVersionKind {
return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
}
// GetObjectKind returns the kind of object reference
func (obj *ObjectReference) GetObjectKind() schema.ObjectKind { return obj }

View File

@ -19,9 +19,45 @@ package pods
import (
"fmt"
"k8s.io/apimachinery/pkg/util/validation/field"
utilfeature "k8s.io/apiserver/pkg/util/feature"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/fieldpath"
)
// ContainerVisitorWithPath is called with each container and the field.Path to that container,
// and returns true if visiting should continue.
type ContainerVisitorWithPath func(container *api.Container, path *field.Path) bool
// VisitContainersWithPath invokes the visitor function with a pointer to the spec
// of every container in the given pod spec and the field.Path to that container.
// If visitor returns false, visiting is short-circuited. VisitContainersWithPath returns true if visiting completes,
// false if visiting was short-circuited.
func VisitContainersWithPath(podSpec *api.PodSpec, visitor ContainerVisitorWithPath) bool {
path := field.NewPath("spec", "initContainers")
for i := range podSpec.InitContainers {
if !visitor(&podSpec.InitContainers[i], path.Index(i)) {
return false
}
}
path = field.NewPath("spec", "containers")
for i := range podSpec.Containers {
if !visitor(&podSpec.Containers[i], path.Index(i)) {
return false
}
}
if utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
path = field.NewPath("spec", "ephemeralContainers")
for i := range podSpec.EphemeralContainers {
if !visitor((*api.Container)(&podSpec.EphemeralContainers[i].EphemeralContainerCommon), path.Index(i)) {
return false
}
}
}
return true
}
// ConvertDownwardAPIFieldLabel converts the specified downward API field label
// and its value in the pod of the specified version to the internal version,
// and returns the converted label and value. This function returns an error if
@ -52,7 +88,8 @@ func ConvertDownwardAPIFieldLabel(version, label, value string) (string, string,
"spec.schedulerName",
"status.phase",
"status.hostIP",
"status.podIP":
"status.podIP",
"status.podIPs":
return label, value, nil
// This is for backwards compatibility with old v1 clients which send spec.host
case "spec.host":

View File

@ -39,8 +39,12 @@ func Resource(resource string) schema.GroupResource {
}
var (
// SchemeBuilder object to register various known types
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
// AddToScheme represents a func that can be used to apply all the registered
// funcs in a scheme
AddToScheme = SchemeBuilder.AddToScheme
)
func addKnownTypes(scheme *runtime.Scheme) error {
@ -92,6 +96,7 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&RangeAllocation{},
&ConfigMap{},
&ConfigMapList{},
&EphemeralContainers{},
)
return nil

View File

@ -20,35 +20,37 @@ import (
"k8s.io/apimachinery/pkg/api/resource"
)
func (self ResourceName) String() string {
return string(self)
func (rn ResourceName) String() string {
return string(rn)
}
// Returns the CPU limit if specified.
func (self *ResourceList) Cpu() *resource.Quantity {
if val, ok := (*self)[ResourceCPU]; ok {
// CPU returns the CPU limit if specified.
func (rl *ResourceList) CPU() *resource.Quantity {
if val, ok := (*rl)[ResourceCPU]; ok {
return &val
}
return &resource.Quantity{Format: resource.DecimalSI}
}
// Returns the Memory limit if specified.
func (self *ResourceList) Memory() *resource.Quantity {
if val, ok := (*self)[ResourceMemory]; ok {
// Memory returns the Memory limit if specified.
func (rl *ResourceList) Memory() *resource.Quantity {
if val, ok := (*rl)[ResourceMemory]; ok {
return &val
}
return &resource.Quantity{Format: resource.BinarySI}
}
func (self *ResourceList) Pods() *resource.Quantity {
if val, ok := (*self)[ResourcePods]; ok {
// Pods returns the list of pods
func (rl *ResourceList) Pods() *resource.Quantity {
if val, ok := (*rl)[ResourcePods]; ok {
return &val
}
return &resource.Quantity{}
}
func (self *ResourceList) StorageEphemeral() *resource.Quantity {
if val, ok := (*self)[ResourceEphemeralStorage]; ok {
// StorageEphemeral returns the list of ephemeral storage volumes, if any
func (rl *ResourceList) StorageEphemeral() *resource.Quantity {
if val, ok := (*rl)[ResourceEphemeralStorage]; ok {
return &val
}
return &resource.Quantity{}

View File

@ -27,8 +27,14 @@ func (t *Taint) MatchTaint(taintToMatch Taint) bool {
return t.Key == taintToMatch.Key && t.Effect == taintToMatch.Effect
}
// taint.ToString() converts taint struct to string in format key=value:effect or key:effect.
// ToString converts taint struct to string in format '<key>=<value>:<effect>', '<key>=<value>:', '<key>:<effect>', or '<key>'.
func (t *Taint) ToString() string {
if len(t.Effect) == 0 {
if len(t.Value) == 0 {
return fmt.Sprintf("%v", t.Key)
}
return fmt.Sprintf("%v=%v:", t.Key, t.Value)
}
if len(t.Value) == 0 {
return fmt.Sprintf("%v:%v", t.Key, t.Effect)
}

File diff suppressed because it is too large Load Diff

View File

@ -31,31 +31,8 @@ import (
)
func addConversionFuncs(scheme *runtime.Scheme) error {
// Add non-generated conversion functions
err := scheme.AddConversionFuncs(
Convert_core_Pod_To_v1_Pod,
Convert_core_PodSpec_To_v1_PodSpec,
Convert_core_ReplicationControllerSpec_To_v1_ReplicationControllerSpec,
Convert_core_ServiceSpec_To_v1_ServiceSpec,
Convert_v1_Pod_To_core_Pod,
Convert_v1_PodSpec_To_core_PodSpec,
Convert_v1_ReplicationControllerSpec_To_core_ReplicationControllerSpec,
Convert_v1_Secret_To_core_Secret,
Convert_v1_ServiceSpec_To_core_ServiceSpec,
Convert_v1_ResourceList_To_core_ResourceList,
Convert_v1_ReplicationController_To_apps_ReplicaSet,
Convert_v1_ReplicationControllerSpec_To_apps_ReplicaSetSpec,
Convert_v1_ReplicationControllerStatus_To_apps_ReplicaSetStatus,
Convert_apps_ReplicaSet_To_v1_ReplicationController,
Convert_apps_ReplicaSetSpec_To_v1_ReplicationControllerSpec,
Convert_apps_ReplicaSetStatus_To_v1_ReplicationControllerStatus,
)
if err != nil {
return err
}
// Add field conversion funcs.
err = scheme.AddFieldLabelConversionFunc(SchemeGroupVersion.WithKind("Pod"),
err := scheme.AddFieldLabelConversionFunc(SchemeGroupVersion.WithKind("Pod"),
func(label, value string) (string, string, error) {
switch label {
case "metadata.name",
@ -66,6 +43,7 @@ func addConversionFuncs(scheme *runtime.Scheme) error {
"spec.serviceAccountName",
"status.phase",
"status.podIP",
"status.podIPs",
"status.nominatedNodeName":
return label, value, nil
// This is for backwards compatibility with old v1 clients which send spec.host
@ -270,6 +248,40 @@ func Convert_v1_PodTemplateSpec_To_core_PodTemplateSpec(in *v1.PodTemplateSpec,
return nil
}
func Convert_v1_PodStatus_To_core_PodStatus(in *v1.PodStatus, out *core.PodStatus, s conversion.Scope) error {
if err := autoConvert_v1_PodStatus_To_core_PodStatus(in, out, s); err != nil {
return err
}
// If both fields (v1.PodIPs and v1.PodIP) are provided, then test v1.PodIP == v1.PodIPs[0]
if (len(in.PodIP) > 0 && len(in.PodIPs) > 0) && (in.PodIP != in.PodIPs[0].IP) {
return fmt.Errorf("conversion Error: v1.PodIP(%v) != v1.PodIPs[0](%v)", in.PodIP, in.PodIPs[0].IP)
}
// at the this point, autoConvert copied v1.PodIPs -> core.PodIPs
// if v1.PodIPs was empty but v1.PodIP is not, then set core.PodIPs[0] with v1.PodIP
if len(in.PodIP) > 0 && len(in.PodIPs) == 0 {
out.PodIPs = []core.PodIP{
{
IP: in.PodIP,
},
}
}
return nil
}
func Convert_core_PodStatus_To_v1_PodStatus(in *core.PodStatus, out *v1.PodStatus, s conversion.Scope) error {
if err := autoConvert_core_PodStatus_To_v1_PodStatus(in, out, s); err != nil {
return err
}
// at the this point autoConvert copied core.PodIPs -> v1.PodIPs
// v1.PodIP (singular value field, which does not exist in core) needs to
// be set with core.PodIPs[0]
if len(in.PodIPs) > 0 {
out.PodIP = in.PodIPs[0].IP
}
return nil
}
// The following two v1.PodSpec conversions are done here to support v1.ServiceAccount
// as an alias for ServiceAccountName.
func Convert_core_PodSpec_To_v1_PodSpec(in *core.PodSpec, out *v1.PodSpec, s conversion.Scope) error {
@ -292,6 +304,36 @@ func Convert_core_PodSpec_To_v1_PodSpec(in *core.PodSpec, out *v1.PodSpec, s con
return nil
}
func Convert_core_NodeSpec_To_v1_NodeSpec(in *core.NodeSpec, out *v1.NodeSpec, s conversion.Scope) error {
if err := autoConvert_core_NodeSpec_To_v1_NodeSpec(in, out, s); err != nil {
return err
}
// at the this point autoConvert copied core.PodCIDRs -> v1.PodCIDRs
// v1.PodCIDR (singular value field, which does not exist in core) needs to
// be set with core.PodCIDRs[0]
if len(in.PodCIDRs) > 0 {
out.PodCIDR = in.PodCIDRs[0]
}
return nil
}
func Convert_v1_NodeSpec_To_core_NodeSpec(in *v1.NodeSpec, out *core.NodeSpec, s conversion.Scope) error {
if err := autoConvert_v1_NodeSpec_To_core_NodeSpec(in, out, s); err != nil {
return err
}
// If both fields (v1.PodCIDRs and v1.PodCIDR) are provided, then test v1.PodCIDR == v1.PodCIDRs[0]
if (len(in.PodCIDR) > 0 && len(in.PodCIDRs) > 0) && (in.PodCIDR != in.PodCIDRs[0]) {
return fmt.Errorf("conversion Error: v1.PodCIDR(%v) != v1.CIDRs[0](%v)", in.PodCIDR, in.PodCIDRs[0])
}
// at the this point, autoConvert copied v1.PodCIDRs -> core.PodCIDRs
// if v1.PodCIDRs was empty but v1.PodCIDR is not, then set core.PodCIDRs[0] with v1.PodCIDR
if len(in.PodCIDR) > 0 && len(in.PodCIDRs) == 0 {
out.PodCIDRs = []string{in.PodCIDR}
}
return nil
}
func Convert_v1_PodSpec_To_core_PodSpec(in *v1.PodSpec, out *core.PodSpec, s conversion.Scope) error {
if err := autoConvert_v1_PodSpec_To_core_PodSpec(in, out, s); err != nil {
return err

View File

@ -24,6 +24,10 @@ import (
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/kubernetes/pkg/util/parsers"
utilpointer "k8s.io/utils/pointer"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/features"
utilnet "k8s.io/utils/net"
)
func addDefaultingFuncs(scheme *runtime.Scheme) error {
@ -128,6 +132,33 @@ func SetDefaults_Service(obj *v1.Service) {
obj.Spec.ExternalTrafficPolicy == "" {
obj.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyTypeCluster
}
// if dualstack feature gate is on then we need to default
// Spec.IPFamily correctly. This is to cover the case
// when an existing cluster have been converted to dualstack
// i.e. it already contain services with Spec.IPFamily==nil
if utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) &&
obj.Spec.Type != v1.ServiceTypeExternalName &&
obj.Spec.ClusterIP != "" && /*has an ip already set*/
obj.Spec.ClusterIP != "None" && /* not converting from ExternalName to other */
obj.Spec.IPFamily == nil /* family was not previously set */ {
// there is a change that the ClusterIP (set by user) is unparsable.
// in this case, the family will be set mistakenly to ipv4 (because
// the util function does not parse errors *sigh*). The error
// will be caught in validation which asserts the validity of the
// IP and the service object will not be persisted with the wrong IP
// family
ipv6 := v1.IPv6Protocol
ipv4 := v1.IPv4Protocol
if utilnet.IsIPv6String(obj.Spec.ClusterIP) {
obj.Spec.IPFamily = &ipv6
} else {
obj.Spec.IPFamily = &ipv4
}
}
}
func SetDefaults_Pod(obj *v1.Pod) {
// If limits are specified, but requests are not, default requests to limits
@ -141,7 +172,7 @@ func SetDefaults_Pod(obj *v1.Pod) {
}
for key, value := range obj.Spec.Containers[i].Resources.Limits {
if _, exists := obj.Spec.Containers[i].Resources.Requests[key]; !exists {
obj.Spec.Containers[i].Resources.Requests[key] = *(value.Copy())
obj.Spec.Containers[i].Resources.Requests[key] = value.DeepCopy()
}
}
}
@ -153,7 +184,7 @@ func SetDefaults_Pod(obj *v1.Pod) {
}
for key, value := range obj.Spec.InitContainers[i].Resources.Limits {
if _, exists := obj.Spec.InitContainers[i].Resources.Requests[key]; !exists {
obj.Spec.InitContainers[i].Resources.Requests[key] = *(value.Copy())
obj.Spec.InitContainers[i].Resources.Requests[key] = value.DeepCopy()
}
}
}
@ -315,7 +346,7 @@ func SetDefaults_NodeStatus(obj *v1.NodeStatus) {
if obj.Allocatable == nil && obj.Capacity != nil {
obj.Allocatable = make(v1.ResourceList, len(obj.Capacity))
for key, value := range obj.Capacity {
obj.Allocatable[key] = *(value.Copy())
obj.Allocatable[key] = value.DeepCopy()
}
obj.Allocatable = obj.Capacity
}
@ -339,19 +370,19 @@ func SetDefaults_LimitRangeItem(obj *v1.LimitRangeItem) {
// If a default limit is unspecified, but the max is specified, default the limit to the max
for key, value := range obj.Max {
if _, exists := obj.Default[key]; !exists {
obj.Default[key] = *(value.Copy())
obj.Default[key] = value.DeepCopy()
}
}
// If a default limit is specified, but the default request is not, default request to limit
for key, value := range obj.Default {
if _, exists := obj.DefaultRequest[key]; !exists {
obj.DefaultRequest[key] = *(value.Copy())
obj.DefaultRequest[key] = value.DeepCopy()
}
}
// If a default request is not specified, but the min is provided, default request to the min
for key, value := range obj.Min {
if _, exists := obj.DefaultRequest[key]; !exists {
obj.DefaultRequest[key] = *(value.Copy())
obj.DefaultRequest[key] = value.DeepCopy()
}
}
}

View File

@ -21,7 +21,7 @@ import (
"fmt"
"strings"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
@ -135,16 +135,6 @@ func ingressEqual(lhs, rhs *v1.LoadBalancerIngress) bool {
return true
}
// TODO: make method on LoadBalancerStatus?
func LoadBalancerStatusDeepCopy(lb *v1.LoadBalancerStatus) *v1.LoadBalancerStatus {
c := &v1.LoadBalancerStatus{}
c.Ingress = make([]v1.LoadBalancerIngress, len(lb.Ingress))
for i := range lb.Ingress {
c.Ingress[i] = lb.Ingress[i]
}
return c
}
// GetAccessModesAsString returns a string representation of an array of access modes.
// modes, when present, are always in the same order: RWO,ROX,RWX.
func GetAccessModesAsString(modes []v1.PersistentVolumeAccessMode) string {

View File

@ -1,99 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package qos
import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/kubernetes/pkg/apis/core"
)
var supportedQoSComputeResources = sets.NewString(string(core.ResourceCPU), string(core.ResourceMemory))
// QOSList is a set of (resource name, QoS class) pairs.
type QOSList map[v1.ResourceName]v1.PodQOSClass
func isSupportedQoSComputeResource(name v1.ResourceName) bool {
return supportedQoSComputeResources.Has(string(name))
}
// GetPodQOS returns the QoS class of a pod.
// A pod is besteffort if none of its containers have specified any requests or limits.
// A pod is guaranteed only when requests and limits are specified for all the containers and they are equal.
// A pod is burstable if limits and requests do not match across all containers.
func GetPodQOS(pod *v1.Pod) v1.PodQOSClass {
requests := v1.ResourceList{}
limits := v1.ResourceList{}
zeroQuantity := resource.MustParse("0")
isGuaranteed := true
for _, container := range pod.Spec.Containers {
// process requests
for name, quantity := range container.Resources.Requests {
if !isSupportedQoSComputeResource(name) {
continue
}
if quantity.Cmp(zeroQuantity) == 1 {
delta := quantity.Copy()
if _, exists := requests[name]; !exists {
requests[name] = *delta
} else {
delta.Add(requests[name])
requests[name] = *delta
}
}
}
// process limits
qosLimitsFound := sets.NewString()
for name, quantity := range container.Resources.Limits {
if !isSupportedQoSComputeResource(name) {
continue
}
if quantity.Cmp(zeroQuantity) == 1 {
qosLimitsFound.Insert(string(name))
delta := quantity.Copy()
if _, exists := limits[name]; !exists {
limits[name] = *delta
} else {
delta.Add(limits[name])
limits[name] = *delta
}
}
}
if !qosLimitsFound.HasAll(string(v1.ResourceMemory), string(v1.ResourceCPU)) {
isGuaranteed = false
}
}
if len(requests) == 0 && len(limits) == 0 {
return v1.PodQOSBestEffort
}
// Check is requests match limits for all resources.
if isGuaranteed {
for name, req := range requests {
if lim, exists := limits[name]; !exists || lim.Cmp(req) != 0 {
isGuaranteed = false
break
}
}
}
if isGuaranteed &&
len(requests) == len(limits) {
return v1.PodQOSGuaranteed
}
return v1.PodQOSBurstable
}

File diff suppressed because it is too large Load Diff

View File

@ -33,6 +33,7 @@ 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)) })
@ -84,6 +85,56 @@ func SetObjectDefaults_EndpointsList(in *v1.EndpointsList) {
}
}
func SetObjectDefaults_EphemeralContainers(in *v1.EphemeralContainers) {
for i := range in.EphemeralContainers {
a := &in.EphemeralContainers[i]
for j := range a.EphemeralContainerCommon.Ports {
b := &a.EphemeralContainerCommon.Ports[j]
SetDefaults_ContainerPort(b)
}
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]
@ -251,6 +302,12 @@ func SetObjectDefaults_Pod(in *v1.Pod) {
SetDefaults_HTTPGetAction(a.ReadinessProbe.Handler.HTTPGet)
}
}
if a.StartupProbe != nil {
SetDefaults_Probe(a.StartupProbe)
if a.StartupProbe.Handler.HTTPGet != nil {
SetDefaults_HTTPGetAction(a.StartupProbe.Handler.HTTPGet)
}
}
if a.Lifecycle != nil {
if a.Lifecycle.PostStart != nil {
if a.Lifecycle.PostStart.HTTPGet != nil {
@ -293,6 +350,12 @@ func SetObjectDefaults_Pod(in *v1.Pod) {
SetDefaults_HTTPGetAction(a.ReadinessProbe.Handler.HTTPGet)
}
}
if a.StartupProbe != nil {
SetDefaults_Probe(a.StartupProbe)
if a.StartupProbe.Handler.HTTPGet != nil {
SetDefaults_HTTPGetAction(a.StartupProbe.Handler.HTTPGet)
}
}
if a.Lifecycle != nil {
if a.Lifecycle.PostStart != nil {
if a.Lifecycle.PostStart.HTTPGet != nil {
@ -306,6 +369,54 @@ func SetObjectDefaults_Pod(in *v1.Pod) {
}
}
}
for i := range in.Spec.EphemeralContainers {
a := &in.Spec.EphemeralContainers[i]
for j := range a.EphemeralContainerCommon.Ports {
b := &a.EphemeralContainerCommon.Ports[j]
SetDefaults_ContainerPort(b)
}
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)
}
}
}
}
SetDefaults_ResourceList(&in.Spec.Overhead)
}
func SetObjectDefaults_PodList(in *v1.PodList) {
@ -397,6 +508,12 @@ func SetObjectDefaults_PodTemplate(in *v1.PodTemplate) {
SetDefaults_HTTPGetAction(a.ReadinessProbe.Handler.HTTPGet)
}
}
if a.StartupProbe != nil {
SetDefaults_Probe(a.StartupProbe)
if a.StartupProbe.Handler.HTTPGet != nil {
SetDefaults_HTTPGetAction(a.StartupProbe.Handler.HTTPGet)
}
}
if a.Lifecycle != nil {
if a.Lifecycle.PostStart != nil {
if a.Lifecycle.PostStart.HTTPGet != nil {
@ -439,6 +556,12 @@ func SetObjectDefaults_PodTemplate(in *v1.PodTemplate) {
SetDefaults_HTTPGetAction(a.ReadinessProbe.Handler.HTTPGet)
}
}
if a.StartupProbe != nil {
SetDefaults_Probe(a.StartupProbe)
if a.StartupProbe.Handler.HTTPGet != nil {
SetDefaults_HTTPGetAction(a.StartupProbe.Handler.HTTPGet)
}
}
if a.Lifecycle != nil {
if a.Lifecycle.PostStart != nil {
if a.Lifecycle.PostStart.HTTPGet != nil {
@ -452,6 +575,54 @@ func SetObjectDefaults_PodTemplate(in *v1.PodTemplate) {
}
}
}
for i := range in.Template.Spec.EphemeralContainers {
a := &in.Template.Spec.EphemeralContainers[i]
for j := range a.EphemeralContainerCommon.Ports {
b := &a.EphemeralContainerCommon.Ports[j]
SetDefaults_ContainerPort(b)
}
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)
}
}
}
}
SetDefaults_ResourceList(&in.Template.Spec.Overhead)
}
func SetObjectDefaults_PodTemplateList(in *v1.PodTemplateList) {
@ -545,6 +716,12 @@ func SetObjectDefaults_ReplicationController(in *v1.ReplicationController) {
SetDefaults_HTTPGetAction(a.ReadinessProbe.Handler.HTTPGet)
}
}
if a.StartupProbe != nil {
SetDefaults_Probe(a.StartupProbe)
if a.StartupProbe.Handler.HTTPGet != nil {
SetDefaults_HTTPGetAction(a.StartupProbe.Handler.HTTPGet)
}
}
if a.Lifecycle != nil {
if a.Lifecycle.PostStart != nil {
if a.Lifecycle.PostStart.HTTPGet != nil {
@ -587,6 +764,12 @@ func SetObjectDefaults_ReplicationController(in *v1.ReplicationController) {
SetDefaults_HTTPGetAction(a.ReadinessProbe.Handler.HTTPGet)
}
}
if a.StartupProbe != nil {
SetDefaults_Probe(a.StartupProbe)
if a.StartupProbe.Handler.HTTPGet != nil {
SetDefaults_HTTPGetAction(a.StartupProbe.Handler.HTTPGet)
}
}
if a.Lifecycle != nil {
if a.Lifecycle.PostStart != nil {
if a.Lifecycle.PostStart.HTTPGet != nil {
@ -600,6 +783,54 @@ func SetObjectDefaults_ReplicationController(in *v1.ReplicationController) {
}
}
}
for i := range in.Spec.Template.Spec.EphemeralContainers {
a := &in.Spec.Template.Spec.EphemeralContainers[i]
for j := range a.EphemeralContainerCommon.Ports {
b := &a.EphemeralContainerCommon.Ports[j]
SetDefaults_ContainerPort(b)
}
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)
}
}
}
}
SetDefaults_ResourceList(&in.Spec.Template.Spec.Overhead)
}
}

View File

@ -68,7 +68,7 @@ func ValidateEvent(event *core.Event) field.ErrorList {
allErrs = append(allErrs, field.Required(field.NewPath("reportingInstance"), ""))
}
if len(event.ReportingInstance) > ReportingInstanceLengthLimit {
allErrs = append(allErrs, field.Invalid(field.NewPath("repotingIntance"), "", fmt.Sprintf("can have at most %v characters", ReportingInstanceLengthLimit)))
allErrs = append(allErrs, field.Invalid(field.NewPath("reportingInstance"), "", fmt.Sprintf("can have at most %v characters", ReportingInstanceLengthLimit)))
}
if len(event.Action) == 0 {
allErrs = append(allErrs, field.Required(field.NewPath("action"), ""))

View File

@ -26,6 +26,8 @@ import (
"reflect"
"regexp"
"strings"
"unicode"
"unicode/utf8"
"k8s.io/klog"
@ -54,6 +56,7 @@ import (
"k8s.io/kubernetes/pkg/fieldpath"
"k8s.io/kubernetes/pkg/master/ports"
"k8s.io/kubernetes/pkg/security/apparmor"
netutils "k8s.io/utils/net"
)
const isNegativeErrorMsg string = apimachineryvalidation.IsNegativeErrorMsg
@ -72,6 +75,23 @@ var iscsiInitiatorIqnRegex = regexp.MustCompile(`iqn\.\d{4}-\d{2}\.([[:alnum:]-.
var iscsiInitiatorEuiRegex = regexp.MustCompile(`^eui.[[:alnum:]]{16}$`)
var iscsiInitiatorNaaRegex = regexp.MustCompile(`^naa.[[:alnum:]]{32}$`)
var allowedEphemeralContainerFields = map[string]bool{
"Name": true,
"Image": true,
"Command": true,
"Args": true,
"WorkingDir": true,
"EnvFrom": true,
"Env": true,
"VolumeMounts": true,
"TerminationMessagePath": true,
"TerminationMessagePolicy": true,
"ImagePullPolicy": true,
"Stdin": true,
"StdinOnce": true,
"TTY": true,
}
// ValidateHasLabel requires that metav1.ObjectMeta has a Label with key and expectedValue
func ValidateHasLabel(meta metav1.ObjectMeta, fldPath *field.Path, key, expectedValue string) field.ErrorList {
allErrs := field.ErrorList{}
@ -264,6 +284,12 @@ func ValidateRuntimeClassName(name string, fldPath *field.Path) field.ErrorList
return allErrs
}
// validateOverhead can be used to check whether the given Overhead is valid.
func validateOverhead(overhead core.ResourceList, fldPath *field.Path) field.ErrorList {
// reuse the ResourceRequirements validation logic
return ValidateResourceRequirements(&core.ResourceRequirements{Limits: overhead}, fldPath)
}
// Validates that given value is not negative.
func ValidateNonnegativeField(value int64, fldPath *field.Path) field.ErrorList {
return apimachineryvalidation.ValidateNonnegativeField(value, fldPath)
@ -878,8 +904,6 @@ func validateQuobyteVolumeSource(quobyte *core.QuobyteVolumeSource, fldPath *fie
allErrs := field.ErrorList{}
if len(quobyte.Registry) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("registry"), "must be a host:port pair or multiple pairs separated by commas"))
} else if len(quobyte.Tenant) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("tenant"), "must be a UUID provided by the configuration and may not be omitted "))
} else if len(quobyte.Tenant) >= 65 {
allErrs = append(allErrs, field.Required(fldPath.Child("tenant"), "must be a UUID and may not exceed a length of 64 characters"))
} else {
@ -1886,9 +1910,10 @@ func ValidatePersistentVolumeClaimSpec(spec *core.PersistentVolumeClaimSpec, fld
storageValue, ok := spec.Resources.Requests[core.ResourceStorage]
if !ok {
allErrs = append(allErrs, field.Required(fldPath.Child("resources").Key(string(core.ResourceStorage)), ""))
} else if errs := ValidatePositiveQuantityValue(storageValue, fldPath.Child("resources").Key(string(core.ResourceStorage))); len(errs) > 0 {
allErrs = append(allErrs, errs...)
} else {
allErrs = append(allErrs, ValidateResourceQuantityValue(string(core.ResourceStorage), storageValue, fldPath.Child("resources").Key(string(core.ResourceStorage)))...)
allErrs = append(allErrs, ValidatePositiveQuantityValue(storageValue, fldPath.Child("resources").Key(string(core.ResourceStorage)))...)
}
if spec.StorageClassName != nil && len(*spec.StorageClassName) > 0 {
@ -2072,7 +2097,10 @@ var validEnvDownwardAPIFieldPathExpressions = sets.NewString(
"spec.nodeName",
"spec.serviceAccountName",
"status.hostIP",
"status.podIP")
"status.podIP",
// status.podIPs is populated even if IPv6DualStack feature gate
// is not enabled. This will work for single stack and dual stack.
"status.podIPs")
var validContainerResourceFieldPathExpressions = sets.NewString("limits.cpu", "limits.memory", "limits.ephemeral-storage", "requests.cpu", "requests.memory", "requests.ephemeral-storage")
func validateEnvVarValueFrom(ev core.EnvVar, fldPath *field.Path) field.ErrorList {
@ -2581,6 +2609,70 @@ func validatePullPolicy(policy core.PullPolicy, fldPath *field.Path) field.Error
return allErrors
}
func validateEphemeralContainers(ephemeralContainers []core.EphemeralContainer, containers, initContainers []core.Container, volumes map[string]core.VolumeSource, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(ephemeralContainers) == 0 {
return allErrs
}
allNames := sets.String{}
for _, c := range containers {
allNames.Insert(c.Name)
}
for _, c := range initContainers {
allNames.Insert(c.Name)
}
for i, ec := range ephemeralContainers {
idxPath := fldPath.Index(i)
if ec.TargetContainerName != "" && !allNames.Has(ec.TargetContainerName) {
allErrs = append(allErrs, field.NotFound(idxPath.Child("targetContainerName"), ec.TargetContainerName))
}
if ec.Name == "" {
allErrs = append(allErrs, field.Required(idxPath, "ephemeralContainer requires a name"))
continue
}
// Using validateContainers() here isn't ideal because it adds an index to the error message that
// doesn't really exist for EphemeralContainers (i.e. ephemeralContainers[0].spec[0].name instead
// of ephemeralContainers[0].spec.name)
// TODO(verb): factor a validateContainer() out of validateContainers() to be used here
c := core.Container(ec.EphemeralContainerCommon)
allErrs = append(allErrs, validateContainers([]core.Container{c}, false, volumes, idxPath)...)
// EphemeralContainers don't require the backwards-compatibility distinction between pod/podTemplate validation
allErrs = append(allErrs, validateContainersOnlyForPod([]core.Container{c}, idxPath)...)
if allNames.Has(ec.Name) {
allErrs = append(allErrs, field.Duplicate(idxPath.Child("name"), ec.Name))
} else {
allNames.Insert(ec.Name)
}
// Ephemeral Containers should not be relied upon for fundamental pod services, so fields such as
// Lifecycle, probes, resources and ports should be disallowed. This is implemented as a whitelist
// so that new fields will be given consideration prior to inclusion in Ephemeral Containers.
specType, specValue := reflect.TypeOf(ec.EphemeralContainerCommon), reflect.ValueOf(ec.EphemeralContainerCommon)
for i := 0; i < specType.NumField(); i++ {
f := specType.Field(i)
if allowedEphemeralContainerFields[f.Name] {
continue
}
// Compare the value of this field to its zero value to determine if it has been set
if !reflect.DeepEqual(specValue.Field(i).Interface(), reflect.Zero(f.Type).Interface()) {
r, n := utf8.DecodeRuneInString(f.Name)
lcName := string(unicode.ToLower(r)) + f.Name[n:]
allErrs = append(allErrs, field.Forbidden(idxPath.Child(lcName), "cannot be set for an Ephemeral Container"))
}
}
}
return allErrs
}
func validateInitContainers(containers, otherContainers []core.Container, deviceVolumes map[string]core.VolumeSource, fldPath *field.Path) field.ErrorList {
var allErrs field.ErrorList
if len(containers) > 0 {
@ -2608,6 +2700,9 @@ func validateInitContainers(containers, otherContainers []core.Container, device
if ctr.ReadinessProbe != nil {
allErrs = append(allErrs, field.Invalid(idxPath.Child("readinessProbe"), ctr.ReadinessProbe, "must not be set for init containers"))
}
if ctr.StartupProbe != nil {
allErrs = append(allErrs, field.Invalid(idxPath.Child("startupProbe"), ctr.StartupProbe, "must not be set for init containers"))
}
}
return allErrs
}
@ -2650,6 +2745,11 @@ func validateContainers(containers []core.Container, isInitContainers bool, volu
if ctr.LivenessProbe != nil && ctr.LivenessProbe.SuccessThreshold != 1 {
allErrs = append(allErrs, field.Invalid(idxPath.Child("livenessProbe", "successThreshold"), ctr.LivenessProbe.SuccessThreshold, "must be 1"))
}
allErrs = append(allErrs, validateProbe(ctr.StartupProbe, idxPath.Child("startupProbe"))...)
// Startup-specific validation
if ctr.StartupProbe != nil && ctr.StartupProbe.SuccessThreshold != 1 {
allErrs = append(allErrs, field.Invalid(idxPath.Child("startupProbe", "successThreshold"), ctr.StartupProbe.SuccessThreshold, "must be 1"))
}
switch ctr.TerminationMessagePolicy {
case core.TerminationMessageReadFile, core.TerminationMessageFallbackToLogsOnError:
@ -3024,6 +3124,44 @@ func ValidatePod(pod *core.Pod) field.ErrorList {
allErrs = append(allErrs, field.Invalid(specPath, hugePageResources, "must use a single hugepage size in a pod spec"))
}
podIPsField := field.NewPath("status", "podIPs")
// all PodIPs must be valid IPs
for i, podIP := range pod.Status.PodIPs {
for _, msg := range validation.IsValidIP(podIP.IP) {
allErrs = append(allErrs, field.Invalid(podIPsField.Index(i), podIP.IP, msg))
}
}
// if we have more than one Pod.PodIP then
// - validate for dual stack
// - validate for duplication
if len(pod.Status.PodIPs) > 1 {
podIPs := make([]string, 0, len(pod.Status.PodIPs))
for _, podIP := range pod.Status.PodIPs {
podIPs = append(podIPs, podIP.IP)
}
dualStack, err := netutils.IsDualStackIPStrings(podIPs)
if err != nil {
allErrs = append(allErrs, field.InternalError(podIPsField, fmt.Errorf("failed to check for dual stack with error:%v", err)))
}
// We only support one from each IP family (i.e. max two IPs in this list).
if !dualStack || len(podIPs) > 2 {
allErrs = append(allErrs, field.Invalid(podIPsField, pod.Status.PodIPs, "may specify no more than one IP for each IP family"))
}
// There should be no duplicates in list of Pod.PodIPs
seen := sets.String{} //:= make(map[string]int)
for i, podIP := range pod.Status.PodIPs {
if seen.Has(podIP.IP) {
allErrs = append(allErrs, field.Duplicate(podIPsField.Index(i), podIP))
}
seen.Insert(podIP.IP)
}
}
return allErrs
}
@ -3038,6 +3176,7 @@ func ValidatePodSpec(spec *core.PodSpec, fldPath *field.Path) field.ErrorList {
allErrs = append(allErrs, vErrs...)
allErrs = append(allErrs, validateContainers(spec.Containers, false, vols, fldPath.Child("containers"))...)
allErrs = append(allErrs, validateInitContainers(spec.InitContainers, spec.Containers, vols, fldPath.Child("initContainers"))...)
allErrs = append(allErrs, validateEphemeralContainers(spec.EphemeralContainers, spec.Containers, spec.InitContainers, vols, fldPath.Child("ephemeralContainers"))...)
allErrs = append(allErrs, validateRestartPolicy(&spec.RestartPolicy, fldPath.Child("restartPolicy"))...)
allErrs = append(allErrs, validateDNSPolicy(&spec.DNSPolicy, fldPath.Child("dnsPolicy"))...)
allErrs = append(allErrs, unversionedvalidation.ValidateLabels(spec.NodeSelector, fldPath.Child("nodeSelector"))...)
@ -3046,6 +3185,7 @@ func ValidatePodSpec(spec *core.PodSpec, fldPath *field.Path) field.ErrorList {
allErrs = append(allErrs, validateAffinity(spec.Affinity, fldPath.Child("affinity"))...)
allErrs = append(allErrs, validatePodDNSConfig(spec.DNSConfig, &spec.DNSPolicy, fldPath.Child("dnsConfig"))...)
allErrs = append(allErrs, validateReadinessGates(spec.ReadinessGates, fldPath.Child("readinessGates"))...)
allErrs = append(allErrs, validateTopologySpreadConstraints(spec.TopologySpreadConstraints, fldPath.Child("topologySpreadConstraints"))...)
if len(spec.ServiceAccountName) > 0 {
for _, msg := range ValidateServiceAccountName(spec.ServiceAccountName, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("serviceAccountName"), spec.ServiceAccountName, msg))
@ -3095,6 +3235,10 @@ func ValidatePodSpec(spec *core.PodSpec, fldPath *field.Path) field.ErrorList {
allErrs = append(allErrs, ValidatePreemptionPolicy(spec.PreemptionPolicy, fldPath.Child("preemptionPolicy"))...)
}
if spec.Overhead != nil {
allErrs = append(allErrs, validateOverhead(spec.Overhead, fldPath.Child("overhead"))...)
}
return allErrs
}
@ -3424,17 +3568,15 @@ func ValidateAppArmorPodAnnotations(annotations map[string]string, spec *core.Po
}
func podSpecHasContainer(spec *core.PodSpec, containerName string) bool {
for _, c := range spec.InitContainers {
var hasContainer bool
podshelper.VisitContainersWithPath(spec, func(c *core.Container, _ *field.Path) bool {
if c.Name == containerName {
return true
hasContainer = true
return false
}
}
for _, c := range spec.Containers {
if c.Name == containerName {
return true
}
}
return false
return true
})
return hasContainer
}
const (
@ -3536,6 +3678,19 @@ func ValidateContainerUpdates(newContainers, oldContainers []core.Container, fld
return allErrs, false
}
// ValidatePodCreate validates a pod in the context of its initial create
func ValidatePodCreate(pod *core.Pod) field.ErrorList {
allErrs := ValidatePod(pod)
fldPath := field.NewPath("spec")
// EphemeralContainers can only be set on update using the ephemeralcontainers subresource
if len(pod.Spec.EphemeralContainers) > 0 {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("ephemeralContainers"), "cannot be set on create"))
}
return allErrs
}
// ValidatePodUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields
// that cannot be changed.
func ValidatePodUpdate(newPod, oldPod *core.Pod) field.ErrorList {
@ -3687,6 +3842,35 @@ func validatePodConditions(conditions []core.PodCondition, fldPath *field.Path)
return allErrs
}
// ValidatePodEphemeralContainersUpdate tests that a user update to EphemeralContainers is valid.
// newPod and oldPod must only differ in their EphemeralContainers.
func ValidatePodEphemeralContainersUpdate(newPod, oldPod *core.Pod) field.ErrorList {
spec := newPod.Spec
specPath := field.NewPath("spec").Child("ephemeralContainers")
vols := make(map[string]core.VolumeSource)
for _, vol := range spec.Volumes {
vols[vol.Name] = vol.VolumeSource
}
allErrs := validateEphemeralContainers(spec.EphemeralContainers, spec.Containers, spec.InitContainers, vols, specPath)
// Existing EphemeralContainers may not be changed. Order isn't preserved by patch, so check each individually.
newContainerIndex := make(map[string]*core.EphemeralContainer)
for i := range newPod.Spec.EphemeralContainers {
newContainerIndex[newPod.Spec.EphemeralContainers[i].Name] = &newPod.Spec.EphemeralContainers[i]
}
for _, old := range oldPod.Spec.EphemeralContainers {
if new, ok := newContainerIndex[old.Name]; !ok {
allErrs = append(allErrs, field.Forbidden(specPath, fmt.Sprintf("existing ephemeral containers %q may not be removed\n", old.Name)))
} else if !apiequality.Semantic.DeepEqual(old, *new) {
specDiff := diff.ObjectDiff(old, *new)
allErrs = append(allErrs, field.Forbidden(specPath, fmt.Sprintf("existing ephemeral containers %q may not be changed\n%v", old.Name, specDiff)))
}
}
return allErrs
}
// ValidatePodBinding tests if required fields in the pod binding are legal.
func ValidatePodBinding(binding *core.Binding) field.ErrorList {
allErrs := field.ErrorList{}
@ -3721,6 +3905,7 @@ func ValidatePodTemplateUpdate(newPod, oldPod *core.PodTemplate) field.ErrorList
var supportedSessionAffinityType = sets.NewString(string(core.ServiceAffinityClientIP), string(core.ServiceAffinityNone))
var supportedServiceType = sets.NewString(string(core.ServiceTypeClusterIP), string(core.ServiceTypeNodePort),
string(core.ServiceTypeLoadBalancer), string(core.ServiceTypeExternalName))
var supportedServiceIPFamily = sets.NewString(string(core.IPv4Protocol), string(core.IPv6Protocol))
// ValidateService tests if required fields/annotations of a Service are valid.
func ValidateService(service *core.Service) field.ErrorList {
@ -3871,6 +4056,35 @@ 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 {
@ -3892,8 +4106,22 @@ func ValidateService(service *core.Service) field.ErrorList {
}
}
allErrs = append(allErrs, validateServiceExternalTrafficFieldsValue(service)...)
//if an ipfamily provided then it has to be one of the supported values
// note:
// - we don't validate service.Spec.IPFamily is supported by the cluster
// - we don't validate service.Spec.ClusterIP is within a range supported by the cluster
// both of these validations are done by the ipallocator
// if the gate is on this field is required (and defaulted by REST if not provided by user)
if utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) && service.Spec.IPFamily == nil {
allErrs = append(allErrs, field.Required(specPath.Child("ipFamily"), ""))
}
if service.Spec.IPFamily != nil && !supportedServiceIPFamily.Has(string(*service.Spec.IPFamily)) {
allErrs = append(allErrs, field.NotSupported(specPath.Child("ipFamily"), service.Spec.IPFamily, supportedServiceIPFamily.List()))
}
allErrs = append(allErrs, validateServiceExternalTrafficFieldsValue(service)...)
return allErrs
}
@ -3947,6 +4175,7 @@ func validateServiceExternalTrafficFieldsValue(service *core.Service) field.Erro
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("externalTrafficPolicy"), service.Spec.ExternalTrafficPolicy,
fmt.Sprintf("ExternalTrafficPolicy must be empty, %v or %v", core.ServiceExternalTrafficPolicyTypeCluster, core.ServiceExternalTrafficPolicyTypeLocal)))
}
if service.Spec.HealthCheckNodePort < 0 {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("healthCheckNodePort"), service.Spec.HealthCheckNodePort,
"HealthCheckNodePort must be not less than 0"))
@ -3982,12 +4211,19 @@ func ValidateServiceExternalTrafficFieldsCombination(service *core.Service) fiel
func ValidateServiceUpdate(service, oldService *core.Service) field.ErrorList {
allErrs := ValidateObjectMetaUpdate(&service.ObjectMeta, &oldService.ObjectMeta, field.NewPath("metadata"))
// ClusterIP should be immutable for services using it (every type other than ExternalName)
// ClusterIP and IPFamily should be immutable for services using it (every type other than ExternalName)
// which do not have ClusterIP assigned yet (empty string value)
if service.Spec.Type != core.ServiceTypeExternalName {
if oldService.Spec.Type != core.ServiceTypeExternalName && oldService.Spec.ClusterIP != "" {
allErrs = append(allErrs, ValidateImmutableField(service.Spec.ClusterIP, oldService.Spec.ClusterIP, field.NewPath("spec", "clusterIP"))...)
}
// notes:
// we drop the IPFamily field when the Dualstack gate is off.
// once the gate is on, we start assigning default ipfamily according to cluster settings. in other words
// though the field is immutable, we allow (onetime) change from nil==> to value
if oldService.Spec.IPFamily != nil {
allErrs = append(allErrs, ValidateImmutableField(service.Spec.IPFamily, oldService.Spec.IPFamily, field.NewPath("spec", "ipFamily"))...)
}
}
allErrs = append(allErrs, ValidateService(service)...)
@ -4101,6 +4337,11 @@ func ValidatePodTemplateSpec(spec *core.PodTemplateSpec, fldPath *field.Path) fi
allErrs = append(allErrs, ValidateAnnotations(spec.Annotations, fldPath.Child("annotations"))...)
allErrs = append(allErrs, ValidatePodSpecificAnnotations(spec.Annotations, &spec.Spec, fldPath.Child("annotations"))...)
allErrs = append(allErrs, ValidatePodSpec(&spec.Spec, fldPath.Child("spec"))...)
if len(spec.Spec.EphemeralContainers) > 0 {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("spec", "ephemeralContainers"), "ephemeral containers not allowed in pod template"))
}
return allErrs
}
@ -4197,12 +4438,40 @@ func ValidateNode(node *core.Node) field.ErrorList {
// That said, if specified, we need to ensure they are valid.
allErrs = append(allErrs, ValidateNodeResources(node)...)
if len(node.Spec.PodCIDR) != 0 {
_, err := ValidateCIDR(node.Spec.PodCIDR)
if err != nil {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "podCIDR"), node.Spec.PodCIDR, "not a valid CIDR"))
// validate PodCIDRS only if we need to
if len(node.Spec.PodCIDRs) > 0 {
podCIDRsField := field.NewPath("spec", "podCIDRs")
// all PodCIDRs should be valid ones
for idx, value := range node.Spec.PodCIDRs {
if _, err := ValidateCIDR(value); err != nil {
allErrs = append(allErrs, field.Invalid(podCIDRsField.Index(idx), node.Spec.PodCIDRs, "must be valid CIDR"))
}
}
// if more than PodCIDR then
// - validate for dual stack
// - validate for duplication
if len(node.Spec.PodCIDRs) > 1 {
dualStack, err := netutils.IsDualStackCIDRStrings(node.Spec.PodCIDRs)
if err != nil {
allErrs = append(allErrs, field.InternalError(podCIDRsField, fmt.Errorf("invalid PodCIDRs. failed to check with dual stack with error:%v", err)))
}
if !dualStack || len(node.Spec.PodCIDRs) > 2 {
allErrs = append(allErrs, field.Invalid(podCIDRsField, node.Spec.PodCIDRs, "may specify no more than one CIDR for each IP family"))
}
// PodCIDRs must not contain duplicates
seen := sets.String{}
for i, value := range node.Spec.PodCIDRs {
if seen.Has(value) {
allErrs = append(allErrs, field.Duplicate(podCIDRsField.Index(i), value))
}
seen.Insert(value)
}
}
}
return allErrs
}
@ -4261,12 +4530,20 @@ func ValidateNodeUpdate(node, oldNode *core.Node) field.ErrorList {
addresses[address] = true
}
if len(oldNode.Spec.PodCIDR) == 0 {
if len(oldNode.Spec.PodCIDRs) == 0 {
// Allow the controller manager to assign a CIDR to a node if it doesn't have one.
oldNode.Spec.PodCIDR = node.Spec.PodCIDR
//this is a no op for a string slice.
oldNode.Spec.PodCIDRs = node.Spec.PodCIDRs
} else {
if oldNode.Spec.PodCIDR != node.Spec.PodCIDR {
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "podCIDR"), "node updates may not change podCIDR except from \"\" to valid"))
// compare the entire slice
if len(oldNode.Spec.PodCIDRs) != len(node.Spec.PodCIDRs) {
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "podCIDRs"), "node updates may not change podCIDR except from \"\" to valid"))
} else {
for idx, value := range oldNode.Spec.PodCIDRs {
if value != node.Spec.PodCIDRs[idx] {
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "podCIDRs"), "node updates may not change podCIDR except from \"\" to valid"))
}
}
}
}
@ -4677,16 +4954,16 @@ func ValidateSecret(secret *core.Secret) field.ErrorList {
if err := json.Unmarshal(dockercfgBytes, &map[string]interface{}{}); err != nil {
allErrs = append(allErrs, field.Invalid(dataPath.Key(core.DockerConfigKey), "<secret contents redacted>", err.Error()))
}
case core.SecretTypeDockerConfigJson:
dockerConfigJsonBytes, exists := secret.Data[core.DockerConfigJsonKey]
case core.SecretTypeDockerConfigJSON:
dockerConfigJSONBytes, exists := secret.Data[core.DockerConfigJSONKey]
if !exists {
allErrs = append(allErrs, field.Required(dataPath.Key(core.DockerConfigJsonKey), ""))
allErrs = append(allErrs, field.Required(dataPath.Key(core.DockerConfigJSONKey), ""))
break
}
// make sure that the content is well-formed json.
if err := json.Unmarshal(dockerConfigJsonBytes, &map[string]interface{}{}); err != nil {
allErrs = append(allErrs, field.Invalid(dataPath.Key(core.DockerConfigJsonKey), "<secret contents redacted>", err.Error()))
if err := json.Unmarshal(dockerConfigJSONBytes, &map[string]interface{}{}); err != nil {
allErrs = append(allErrs, field.Invalid(dataPath.Key(core.DockerConfigJSONKey), "<secret contents redacted>", err.Error()))
}
case core.SecretTypeBasicAuth:
_, usernameFieldExists := secret.Data[core.BasicAuthUsernameKey]
@ -5277,9 +5554,37 @@ func ValidateSecurityContext(sc *core.SecurityContext, fldPath *field.Path) fiel
// maxGMSACredentialSpecLength is the max length, in bytes, for the actual contents
// of a GMSA cred spec. In general, those shouldn't be more than a few hundred bytes,
// so we want to give plenty of room here while still providing an upper bound.
// The runAsUserName field will be used to execute the given container's entrypoint, and
// it can be formatted as "DOMAIN/USER", where the DOMAIN is optional, maxRunAsUserNameDomainLength
// is the max character length for the user's DOMAIN, and maxRunAsUserNameUserLength
// is the max character length for the USER itself. Both the DOMAIN and USER have their
// own restrictions, and more information about them can be found here:
// https://support.microsoft.com/en-us/help/909264/naming-conventions-in-active-directory-for-computers-domains-sites-and
// https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/bb726984(v=technet.10)
const (
maxGMSACredentialSpecLengthInKiB = 64
maxGMSACredentialSpecLength = maxGMSACredentialSpecLengthInKiB * 1024
maxRunAsUserNameDomainLength = 256
maxRunAsUserNameUserLength = 104
)
var (
// control characters are not permitted in the runAsUserName field.
ctrlRegex = regexp.MustCompile(`[[:cntrl:]]+`)
// a valid NetBios Domain name cannot start with a dot, has at least 1 character,
// at most 15 characters, and it cannot the characters: \ / : * ? " < > |
validNetBiosRegex = regexp.MustCompile(`^[^\\/:\*\?"<>|\.][^\\/:\*\?"<>|]{0,14}$`)
// a valid DNS name contains only alphanumeric characters, dots, and dashes.
dnsLabelFormat = `[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?`
dnsSubdomainFormat = fmt.Sprintf(`^%s(?:\.%s)*$`, dnsLabelFormat, dnsLabelFormat)
validWindowsUserDomainDNSRegex = regexp.MustCompile(dnsSubdomainFormat)
// a username is invalid if it contains the characters: " / \ [ ] : ; | = , + * ? < > @
// or it contains only dots or spaces.
invalidUserNameCharsRegex = regexp.MustCompile(`["/\\:;|=,\+\*\?<>@\[\]]`)
invalidUserNameDotsSpacesRegex = regexp.MustCompile(`^[\. ]+$`)
)
func validateWindowsSecurityContextOptions(windowsOptions *core.WindowsSecurityContextOptions, fieldPath *field.Path) field.ErrorList {
@ -5305,6 +5610,59 @@ func validateWindowsSecurityContextOptions(windowsOptions *core.WindowsSecurityC
}
}
if windowsOptions.RunAsUserName != nil {
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")
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")
allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
} else {
var (
hasDomain = false
domain = ""
user string
)
if len(parts) == 1 {
user = parts[0]
} else {
hasDomain = true
domain = parts[0]
user = parts[1]
}
if len(domain) >= maxRunAsUserNameDomainLength {
errMsg := fmt.Sprintf("runAsUserName's Domain length must be under %d characters", maxRunAsUserNameDomainLength)
allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
}
if hasDomain && !(validNetBiosRegex.MatchString(domain) || validWindowsUserDomainDNSRegex.MatchString(domain)) {
errMsg := "runAsUserName's Domain doesn't match the NetBios nor the DNS format"
allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
}
if l := len(user); l == 0 {
errMsg := fmt.Sprintf("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)
allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
}
if invalidUserNameDotsSpacesRegex.MatchString(user) {
errMsg := `runAsUserName's User cannot contain only periods or spaces`
allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
}
if invalidUserNameCharsRegex.MatchString(user) {
errMsg := `runAsUserName's User cannot contain the following characters: "/\:;|=,+*?<>@[]`
allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
}
}
}
return allErrs
}
@ -5397,3 +5755,67 @@ func ValidateProcMountType(fldPath *field.Path, procMountType core.ProcMountType
return field.NotSupported(fldPath, procMountType, []string{string(core.DefaultProcMount), string(core.UnmaskedProcMount)})
}
}
var (
supportedScheduleActions = sets.NewString(string(core.DoNotSchedule), string(core.ScheduleAnyway))
)
// validateTopologySpreadConstraints validates given TopologySpreadConstraints.
func validateTopologySpreadConstraints(constraints []core.TopologySpreadConstraint, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
for i, constraint := range constraints {
subFldPath := fldPath.Index(i)
if err := ValidateMaxSkew(subFldPath.Child("maxSkew"), constraint.MaxSkew); err != nil {
allErrs = append(allErrs, err)
}
if err := ValidateTopologyKey(subFldPath.Child("topologyKey"), constraint.TopologyKey); err != nil {
allErrs = append(allErrs, err)
}
if err := ValidateWhenUnsatisfiable(subFldPath.Child("whenUnsatisfiable"), constraint.WhenUnsatisfiable); err != nil {
allErrs = append(allErrs, err)
}
// tuple {topologyKey, whenUnsatisfiable} denotes one kind of spread constraint
if err := ValidateSpreadConstraintNotRepeat(subFldPath.Child("{topologyKey, whenUnsatisfiable}"), constraint, constraints[i+1:]); err != nil {
allErrs = append(allErrs, err)
}
}
return allErrs
}
// ValidateMaxSkew tests that the argument is a valid MaxSkew.
func ValidateMaxSkew(fldPath *field.Path, maxSkew int32) *field.Error {
if maxSkew <= 0 {
return field.Invalid(fldPath, maxSkew, isNotPositiveErrorMsg)
}
return nil
}
// ValidateTopologyKey tests that the argument is a valid TopologyKey.
func ValidateTopologyKey(fldPath *field.Path, topologyKey string) *field.Error {
if len(topologyKey) == 0 {
return field.Required(fldPath, "can not be empty")
}
return nil
}
// ValidateWhenUnsatisfiable tests that the argument is a valid UnsatisfiableConstraintAction.
func ValidateWhenUnsatisfiable(fldPath *field.Path, action core.UnsatisfiableConstraintAction) *field.Error {
if !supportedScheduleActions.Has(string(action)) {
return field.NotSupported(fldPath, action, supportedScheduleActions.List())
}
return nil
}
// ValidateSpreadConstraintNotRepeat tests that if `constraint` duplicates with `existingConstraintPairs`
// on TopologyKey and WhenUnsatisfiable fields.
func ValidateSpreadConstraintNotRepeat(fldPath *field.Path, constraint core.TopologySpreadConstraint, restingConstraints []core.TopologySpreadConstraint) *field.Error {
for _, restingConstraint := range restingConstraints {
if constraint.TopologyKey == restingConstraint.TopologyKey &&
constraint.WhenUnsatisfiable == restingConstraint.WhenUnsatisfiable {
return field.Duplicate(fldPath, fmt.Sprintf("{%v, %v}", constraint.TopologyKey, constraint.WhenUnsatisfiable))
}
}
return nil
}

View File

@ -773,6 +773,11 @@ func (in *Container) DeepCopyInto(out *Container) {
*out = new(Probe)
(*in).DeepCopyInto(*out)
}
if in.StartupProbe != nil {
in, out := &in.StartupProbe, &out.StartupProbe
*out = new(Probe)
(*in).DeepCopyInto(*out)
}
if in.Lifecycle != nil {
in, out := &in.Lifecycle, &out.Lifecycle
*out = new(Lifecycle)
@ -920,6 +925,11 @@ func (in *ContainerStatus) DeepCopyInto(out *ContainerStatus) {
*out = *in
in.State.DeepCopyInto(&out.State)
in.LastTerminationState.DeepCopyInto(&out.LastTerminationState)
if in.Started != nil {
in, out := &in.Started, &out.Started
*out = new(bool)
**out = **in
}
return
}
@ -1278,6 +1288,139 @@ func (in *EnvVarSource) DeepCopy() *EnvVarSource {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EphemeralContainer) DeepCopyInto(out *EphemeralContainer) {
*out = *in
in.EphemeralContainerCommon.DeepCopyInto(&out.EphemeralContainerCommon)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EphemeralContainer.
func (in *EphemeralContainer) DeepCopy() *EphemeralContainer {
if in == nil {
return nil
}
out := new(EphemeralContainer)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EphemeralContainerCommon) DeepCopyInto(out *EphemeralContainerCommon) {
*out = *in
if in.Command != nil {
in, out := &in.Command, &out.Command
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Args != nil {
in, out := &in.Args, &out.Args
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Ports != nil {
in, out := &in.Ports, &out.Ports
*out = make([]ContainerPort, len(*in))
copy(*out, *in)
}
if in.EnvFrom != nil {
in, out := &in.EnvFrom, &out.EnvFrom
*out = make([]EnvFromSource, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Env != nil {
in, out := &in.Env, &out.Env
*out = make([]EnvVar, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
in.Resources.DeepCopyInto(&out.Resources)
if in.VolumeMounts != nil {
in, out := &in.VolumeMounts, &out.VolumeMounts
*out = make([]VolumeMount, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.VolumeDevices != nil {
in, out := &in.VolumeDevices, &out.VolumeDevices
*out = make([]VolumeDevice, len(*in))
copy(*out, *in)
}
if in.LivenessProbe != nil {
in, out := &in.LivenessProbe, &out.LivenessProbe
*out = new(Probe)
(*in).DeepCopyInto(*out)
}
if in.ReadinessProbe != nil {
in, out := &in.ReadinessProbe, &out.ReadinessProbe
*out = new(Probe)
(*in).DeepCopyInto(*out)
}
if in.StartupProbe != nil {
in, out := &in.StartupProbe, &out.StartupProbe
*out = new(Probe)
(*in).DeepCopyInto(*out)
}
if in.Lifecycle != nil {
in, out := &in.Lifecycle, &out.Lifecycle
*out = new(Lifecycle)
(*in).DeepCopyInto(*out)
}
if in.SecurityContext != nil {
in, out := &in.SecurityContext, &out.SecurityContext
*out = new(SecurityContext)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EphemeralContainerCommon.
func (in *EphemeralContainerCommon) DeepCopy() *EphemeralContainerCommon {
if in == nil {
return nil
}
out := new(EphemeralContainerCommon)
in.DeepCopyInto(out)
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 *Event) DeepCopyInto(out *Event) {
*out = *in
@ -2063,7 +2206,7 @@ func (in *Namespace) DeepCopyInto(out *Namespace) {
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
in.Status.DeepCopyInto(&out.Status)
return
}
@ -2085,6 +2228,23 @@ func (in *Namespace) DeepCopyObject() runtime.Object {
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NamespaceCondition) DeepCopyInto(out *NamespaceCondition) {
*out = *in
in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceCondition.
func (in *NamespaceCondition) DeepCopy() *NamespaceCondition {
if in == nil {
return nil
}
out := new(NamespaceCondition)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NamespaceList) DeepCopyInto(out *NamespaceList) {
*out = *in
@ -2142,6 +2302,13 @@ func (in *NamespaceSpec) DeepCopy() *NamespaceSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NamespaceStatus) DeepCopyInto(out *NamespaceStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]NamespaceCondition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
@ -2472,6 +2639,11 @@ func (in *NodeSelectorTerm) DeepCopy() *NodeSelectorTerm {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NodeSpec) DeepCopyInto(out *NodeSpec) {
*out = *in
if in.PodCIDRs != nil {
in, out := &in.PodCIDRs, &out.PodCIDRs
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Taints != nil {
in, out := &in.Taints, &out.Taints
*out = make([]Taint, len(*in))
@ -3300,6 +3472,22 @@ func (in *PodExecOptions) DeepCopyObject() runtime.Object {
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PodIP) DeepCopyInto(out *PodIP) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodIP.
func (in *PodIP) DeepCopy() *PodIP {
if in == nil {
return nil
}
out := new(PodIP)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PodList) DeepCopyInto(out *PodList) {
*out = *in
@ -3554,6 +3742,13 @@ func (in *PodSpec) DeepCopyInto(out *PodSpec) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.EphemeralContainers != nil {
in, out := &in.EphemeralContainers, &out.EphemeralContainers
*out = make([]EphemeralContainer, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.TerminationGracePeriodSeconds != nil {
in, out := &in.TerminationGracePeriodSeconds, &out.TerminationGracePeriodSeconds
*out = new(int64)
@ -3630,11 +3825,25 @@ func (in *PodSpec) DeepCopyInto(out *PodSpec) {
*out = new(string)
**out = **in
}
if in.Overhead != nil {
in, out := &in.Overhead, &out.Overhead
*out = make(ResourceList, len(*in))
for key, val := range *in {
(*out)[key] = val.DeepCopy()
}
}
if in.EnableServiceLinks != nil {
in, out := &in.EnableServiceLinks, &out.EnableServiceLinks
*out = new(bool)
**out = **in
}
if in.TopologySpreadConstraints != nil {
in, out := &in.TopologySpreadConstraints, &out.TopologySpreadConstraints
*out = make([]TopologySpreadConstraint, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
@ -3658,6 +3867,11 @@ func (in *PodStatus) DeepCopyInto(out *PodStatus) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.PodIPs != nil {
in, out := &in.PodIPs, &out.PodIPs
*out = make([]PodIP, len(*in))
copy(*out, *in)
}
if in.StartTime != nil {
in, out := &in.StartTime, &out.StartTime
*out = (*in).DeepCopy()
@ -3676,6 +3890,13 @@ func (in *PodStatus) DeepCopyInto(out *PodStatus) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.EphemeralContainerStatuses != nil {
in, out := &in.EphemeralContainerStatuses, &out.EphemeralContainerStatuses
*out = make([]ContainerStatus, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
@ -4945,6 +5166,16 @@ func (in *ServiceSpec) DeepCopyInto(out *ServiceSpec) {
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.IPFamily != nil {
in, out := &in.IPFamily, &out.IPFamily
*out = new(IPFamily)
**out = **in
}
if in.TopologyKeys != nil {
in, out := &in.TopologyKeys, &out.TopologyKeys
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
@ -5156,6 +5387,27 @@ func (in *TopologySelectorTerm) DeepCopy() *TopologySelectorTerm {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TopologySpreadConstraint) DeepCopyInto(out *TopologySpreadConstraint) {
*out = *in
if in.LabelSelector != nil {
in, out := &in.LabelSelector, &out.LabelSelector
*out = new(v1.LabelSelector)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TopologySpreadConstraint.
func (in *TopologySpreadConstraint) DeepCopy() *TopologySpreadConstraint {
if in == nil {
return nil
}
out := new(TopologySpreadConstraint)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TypedLocalObjectReference) DeepCopyInto(out *TypedLocalObjectReference) {
*out = *in
@ -5490,6 +5742,11 @@ func (in *WindowsSecurityContextOptions) DeepCopyInto(out *WindowsSecurityContex
*out = new(string)
**out = **in
}
if in.RunAsUserName != nil {
in, out := &in.RunAsUserName, &out.RunAsUserName
*out = new(string)
**out = **in
}
return
}

View File

@ -41,6 +41,7 @@ func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
// Builds new Scheme of known types
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme

View File

@ -34,7 +34,7 @@ import (
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Dummy definition
// ReplicationControllerDummy : Dummy definition
type ReplicationControllerDummy struct {
metav1.TypeMeta
}

View File

@ -104,7 +104,7 @@ type NetworkPolicyIngressRule struct {
// List of sources which should be able to access the pods selected for this rule.
// Items in this list are combined using a logical OR operation. If this field is
// empty or missing, this rule matches all sources (traffic not restricted by
// source). If this field is present and contains at least on item, this rule
// source). If this field is present and contains at least one item, this rule
// allows traffic only if the traffic matches at least one item in the from list.
// +optional
From []NetworkPolicyPeer
@ -204,17 +204,17 @@ type NetworkPolicyList struct {
type Ingress struct {
metav1.TypeMeta
// Standard object's metadata.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
// +optional
metav1.ObjectMeta
// Spec is the desired state of the Ingress.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
// +optional
Spec IngressSpec
// Status is the current state of the Ingress.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
// +optional
Status IngressStatus
}
@ -225,7 +225,7 @@ type Ingress struct {
type IngressList struct {
metav1.TypeMeta
// Standard object's metadata.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
// +optional
metav1.ListMeta

View File

@ -38,8 +38,10 @@ func Resource(resource string) schema.GroupResource {
}
var (
// SchemeBuilder is the scheme builder with scheme init functions to run for this API package
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
// AddToScheme is a global function that registers this API group & version to a scheme
AddToScheme = SchemeBuilder.AddToScheme
)
// Adds the list of known types to the given scheme.

View File

@ -48,7 +48,7 @@ type PodDisruptionBudgetSpec struct {
// PodDisruptionBudget. Status may trail the actual state of a system.
type PodDisruptionBudgetStatus struct {
// Most recent generation observed when updating this PDB status. PodDisruptionsAllowed and other
// status informatio is valid only if observedGeneration equals to PDB's object generation.
// status information is valid only if observedGeneration equals to PDB's object generation.
// +optional
ObservedGeneration int64
@ -275,7 +275,8 @@ var AllowAllCapabilities api.Capability = "*"
// FSType gives strong typing to different file systems that are used by volumes.
type FSType string
var (
// Exported FSTypes.
const (
AzureFile FSType = "azureFile"
Flocker FSType = "flocker"
FlexVolume FSType = "flexVolume"

View File

@ -35,12 +35,16 @@ import (
psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
)
// ValidatePodDisruptionBudget validates a PodDisruptionBudget and returns an ErrorList
// with any errors.
func ValidatePodDisruptionBudget(pdb *policy.PodDisruptionBudget) field.ErrorList {
allErrs := ValidatePodDisruptionBudgetSpec(pdb.Spec, field.NewPath("spec"))
allErrs = append(allErrs, ValidatePodDisruptionBudgetStatus(pdb.Status, field.NewPath("status"))...)
return allErrs
}
// ValidatePodDisruptionBudgetSpec validates a PodDisruptionBudgetSpec and returns an ErrorList
// with any errors.
func ValidatePodDisruptionBudgetSpec(spec policy.PodDisruptionBudgetSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
@ -63,6 +67,8 @@ func ValidatePodDisruptionBudgetSpec(spec policy.PodDisruptionBudgetSpec, fldPat
return allErrs
}
// ValidatePodDisruptionBudgetStatus validates a PodDisruptionBudgetStatus and returns an ErrorList
// with any errors.
func ValidatePodDisruptionBudgetStatus(status policy.PodDisruptionBudgetStatus, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.PodDisruptionsAllowed), fldPath.Child("podDisruptionsAllowed"))...)
@ -78,6 +84,8 @@ func ValidatePodDisruptionBudgetStatus(status policy.PodDisruptionBudgetStatus,
// trailing dashes are allowed.
var ValidatePodSecurityPolicyName = apimachineryvalidation.NameIsDNSSubdomain
// ValidatePodSecurityPolicy validates a PodSecurityPolicy and returns an ErrorList
// with any errors.
func ValidatePodSecurityPolicy(psp *policy.PodSecurityPolicy) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&psp.ObjectMeta, false, ValidatePodSecurityPolicyName, field.NewPath("metadata"))...)
@ -86,6 +94,8 @@ func ValidatePodSecurityPolicy(psp *policy.PodSecurityPolicy) field.ErrorList {
return allErrs
}
// ValidatePodSecurityPolicySpec validates a PodSecurityPolicySpec and returns an ErrorList
// with any errors.
func ValidatePodSecurityPolicySpec(spec *policy.PodSecurityPolicySpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
@ -114,6 +124,8 @@ func ValidatePodSecurityPolicySpec(spec *policy.PodSecurityPolicySpec, fldPath *
return allErrs
}
// ValidatePodSecurityPolicySpecificAnnotations validates annotations and returns an ErrorList
// with any errors.
func ValidatePodSecurityPolicySpecificAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
@ -335,10 +347,13 @@ func validatePSPAllowedProcMountTypes(fldPath *field.Path, allowedProcMountTypes
}
const sysctlPatternSegmentFmt string = "([a-z0-9][-_a-z0-9]*)?[a-z0-9*]"
// SysctlPatternFmt is a regex used for matching valid sysctl patterns.
const SysctlPatternFmt string = "(" + apivalidation.SysctlSegmentFmt + "\\.)*" + sysctlPatternSegmentFmt
var sysctlPatternRegexp = regexp.MustCompile("^" + SysctlPatternFmt + "$")
// IsValidSysctlPattern checks if name is a valid sysctl pattern.
func IsValidSysctlPattern(name string) bool {
if len(name) > apivalidation.SysctlMaxLength {
return false

View File

@ -1,65 +0,0 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package scheduling
import (
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// SystemPriorityClasses define system priority classes that are auto-created at cluster bootstrapping.
// Our API validation logic ensures that any priority class that has a system prefix or its value
// is higher than HighestUserDefinablePriority is equal to one of these SystemPriorityClasses.
var systemPriorityClasses = []*PriorityClass{
{
ObjectMeta: metav1.ObjectMeta{
Name: SystemNodeCritical,
},
Value: SystemCriticalPriority + 1000,
Description: "Used for system critical pods that must not be moved from their current node.",
},
{
ObjectMeta: metav1.ObjectMeta{
Name: SystemClusterCritical,
},
Value: SystemCriticalPriority,
Description: "Used for system critical pods that must run in the cluster, but can be moved to another node if necessary.",
},
}
// SystemPriorityClasses returns the list of system priority classes.
// NOTE: be careful not to modify any of elements of the returned array directly.
func SystemPriorityClasses() []*PriorityClass {
return systemPriorityClasses
}
// IsKnownSystemPriorityClass checks that "pc" is equal to one of the system PriorityClasses.
// It ignores "description", labels, annotations, etc. of the PriorityClass.
func IsKnownSystemPriorityClass(pc *PriorityClass) (bool, error) {
for _, spc := range systemPriorityClasses {
if spc.Name == pc.Name {
if spc.Value != pc.Value {
return false, fmt.Errorf("value of %v PriorityClass must be %v", spc.Name, spc.Value)
}
if spc.GlobalDefault != pc.GlobalDefault {
return false, fmt.Errorf("globalDefault of %v PriorityClass must be %v", spc.Name, spc.GlobalDefault)
}
return true, nil
}
}
return false, fmt.Errorf("%v is not a known system priority class", pc.Name)
}

View File

@ -47,7 +47,7 @@ const (
// integer value. The value can be any valid integer.
type PriorityClass struct {
metav1.TypeMeta
// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata.
// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata.
// +optional
metav1.ObjectMeta
@ -80,7 +80,7 @@ type PriorityClass struct {
type PriorityClassList struct {
metav1.TypeMeta
// Standard list metadata.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
// +optional
metav1.ListMeta

View File

@ -1,57 +0,0 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// IsDefaultStorageClassAnnotation represents a StorageClass annotation that
// marks a class as the default StorageClass
const IsDefaultStorageClassAnnotation = "storageclass.kubernetes.io/is-default-class"
// BetaIsDefaultStorageClassAnnotation is the beta version of IsDefaultStorageClassAnnotation.
// TODO: remove Beta when no longer used
const BetaIsDefaultStorageClassAnnotation = "storageclass.beta.kubernetes.io/is-default-class"
// IsDefaultAnnotationText returns a pretty Yes/No String if
// the annotation is set
// TODO: remove Beta when no longer needed
func IsDefaultAnnotationText(obj metav1.ObjectMeta) string {
if obj.Annotations[IsDefaultStorageClassAnnotation] == "true" {
return "Yes"
}
if obj.Annotations[BetaIsDefaultStorageClassAnnotation] == "true" {
return "Yes"
}
return "No"
}
// IsDefaultAnnotation returns a boolean if
// the annotation is set
// TODO: remove Beta when no longer needed
func IsDefaultAnnotation(obj metav1.ObjectMeta) bool {
if obj.Annotations[IsDefaultStorageClassAnnotation] == "true" {
return true
}
if obj.Annotations[BetaIsDefaultStorageClassAnnotation] == "true" {
return true
}
return false
}