func ValidateClusterAutoscaler(autoscaler *extensions.ClusterAutoscaler) validation.ErrorList { allErrs := validation.ErrorList{} if autoscaler.Name != "ClusterAutoscaler" { allErrs = append(allErrs, validation.NewInvalidError(validation.NewFieldPath("metadata", "name"), autoscaler.Name, `name must be ClusterAutoscaler`)) } if autoscaler.Namespace != api.NamespaceDefault { allErrs = append(allErrs, validation.NewInvalidError(validation.NewFieldPath("metadata", "namespace"), autoscaler.Namespace, `namespace must be default`)) } allErrs = append(allErrs, validateClusterAutoscalerSpec(autoscaler.Spec, validation.NewFieldPath("spec"))...) return allErrs }
// TODO: add other common fields that require global validation. func validateCommonFields(obj, old runtime.Object) utilvalidation.ErrorList { allErrs := utilvalidation.ErrorList{} objectMeta, err := api.ObjectMetaFor(obj) if err != nil { return append(allErrs, utilvalidation.NewInternalError(utilvalidation.NewFieldPath("metadata"), err)) } oldObjectMeta, err := api.ObjectMetaFor(old) if err != nil { return append(allErrs, utilvalidation.NewInternalError(utilvalidation.NewFieldPath("metadata"), err)) } allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(objectMeta, oldObjectMeta, utilvalidation.NewFieldPath("metadata"))...) return allErrs }
// ValidateDaemonSetUpdate tests if required fields in the DaemonSet are set. func ValidateDaemonSetUpdate(controller, oldController *extensions.DaemonSet) validation.ErrorList { allErrs := validation.ErrorList{} allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&controller.ObjectMeta, &oldController.ObjectMeta, validation.NewFieldPath("metadata"))...) allErrs = append(allErrs, ValidateDaemonSetSpec(&controller.Spec, validation.NewFieldPath("spec"))...) allErrs = append(allErrs, ValidateDaemonSetTemplateUpdate(controller.Spec.Template, oldController.Spec.Template, validation.NewFieldPath("spec", "template"))...) return allErrs }
// ValidateEvent makes sure that the event makes sense. func ValidateEvent(event *api.Event) validation.ErrorList { allErrs := validation.ErrorList{} // There is no namespace required for node. if event.InvolvedObject.Kind == "Node" && event.Namespace != "" { allErrs = append(allErrs, validation.NewInvalidError(validation.NewFieldPath("involvedObject", "namespace"), event.InvolvedObject.Namespace, "namespace is not required for node")) } if event.InvolvedObject.Kind != "Node" && event.Namespace != event.InvolvedObject.Namespace { allErrs = append(allErrs, validation.NewInvalidError(validation.NewFieldPath("involvedObject", "namespace"), event.InvolvedObject.Namespace, "does not match involvedObject")) } if !validation.IsDNS1123Subdomain(event.Namespace) { allErrs = append(allErrs, validation.NewInvalidError(validation.NewFieldPath("namespace"), event.Namespace, "")) } return allErrs }
func filterInvalidPods(pods []*api.Pod, source string, recorder record.EventRecorder) (filtered []*api.Pod) { names := sets.String{} for i, pod := range pods { var errlist utilvalidation.ErrorList if errs := validation.ValidatePod(pod); len(errs) != 0 { errlist = append(errlist, errs...) // If validation fails, don't trust it any further - // even Name could be bad. } else { name := kubecontainer.GetPodFullName(pod) if names.Has(name) { //FIXME: this implies an API version errlist = append(errlist, utilvalidation.NewDuplicateError(utilvalidation.NewFieldPath("metadata", "name"), pod.Name)) } else { names.Insert(name) } } if len(errlist) > 0 { name := bestPodIdentString(pod) err := errlist.ToAggregate() glog.Warningf("Pod[%d] (%s) from %s failed validation, ignoring: %v", i+1, name, source, err) recorder.Eventf(pod, api.EventTypeWarning, kubecontainer.FailedValidation, "Error validating pod %s from %s, ignoring: %v", name, source, err) continue } filtered = append(filtered, pod) } return }
func ValidateThirdPartyResourceData(obj *extensions.ThirdPartyResourceData) validation.ErrorList { allErrs := validation.ErrorList{} if len(obj.Name) == 0 { allErrs = append(allErrs, validation.NewInvalidError(validation.NewFieldPath("name"), obj.Name, "must be non-empty")) } return allErrs }
func ValidateJob(job *extensions.Job) validation.ErrorList { allErrs := validation.ErrorList{} // Jobs and rcs have the same name validation allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&job.ObjectMeta, true, apivalidation.ValidateReplicationControllerName, validation.NewFieldPath("metadata"))...) allErrs = append(allErrs, ValidateJobSpec(&job.Spec, validation.NewFieldPath("spec"))...) return allErrs }
func ValidateThirdPartyResource(obj *extensions.ThirdPartyResource) validation.ErrorList { allErrs := validation.ErrorList{} allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&obj.ObjectMeta, true, ValidateThirdPartyResourceName, validation.NewFieldPath("metadata"))...) versions := sets.String{} for ix := range obj.Versions { version := &obj.Versions[ix] if len(version.Name) == 0 { allErrs = append(allErrs, validation.NewInvalidError(validation.NewFieldPath("versions").Index(ix).Child("name"), version, "can not be empty")) } if versions.Has(version.Name) { allErrs = append(allErrs, validation.NewDuplicateError(validation.NewFieldPath("versions").Index(ix).Child("name"), version)) } versions.Insert(version.Name) } return allErrs }
func ValidateScale(scale *extensions.Scale) validation.ErrorList { allErrs := validation.ErrorList{} allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&scale.ObjectMeta, true, apivalidation.NameIsDNSSubdomain, validation.NewFieldPath("metadata"))...) if scale.Spec.Replicas < 0 { allErrs = append(allErrs, validation.NewInvalidError(validation.NewFieldPath("spec", "replicas"), scale.Spec.Replicas, "must be non-negative")) } return allErrs }
// ParseWatchResourceVersion takes a resource version argument and converts it to // the etcd version we should pass to helper.Watch(). Because resourceVersion is // an opaque value, the default watch behavior for non-zero watch is to watch // the next value (if you pass "1", you will see updates from "2" onwards). func ParseWatchResourceVersion(resourceVersion string) (uint64, error) { if resourceVersion == "" || resourceVersion == "0" { return 0, nil } version, err := strconv.ParseUint(resourceVersion, 10, 64) if err != nil { return 0, errors.NewInvalid("", "", utilvalidation.ErrorList{ // Validation errors are supposed to return version-specific field // paths, but this is probably close enough. utilvalidation.NewInvalidError(utilvalidation.NewFieldPath("resourceVersion"), resourceVersion, err.Error()), }) } return version + 1, nil }
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { service := obj.(*api.Service) if !api.ValidNamespace(ctx, &service.ObjectMeta) { return nil, false, errors.NewConflict("service", service.Namespace, fmt.Errorf("Service.Namespace does not match the provided context")) } oldService, err := rs.registry.GetService(ctx, service.Name) if err != nil { return nil, false, err } // Copy over non-user fields // TODO: make this a merge function if errs := validation.ValidateServiceUpdate(service, oldService); len(errs) > 0 { return nil, false, errors.NewInvalid("service", service.Name, errs) } nodePortOp := portallocator.StartOperation(rs.serviceNodePorts) defer nodePortOp.Finish() assignNodePorts := shouldAssignNodePorts(service) oldNodePorts := CollectServiceNodePorts(oldService) newNodePorts := []int{} if assignNodePorts { for i := range service.Spec.Ports { servicePort := &service.Spec.Ports[i] nodePort := servicePort.NodePort if nodePort != 0 { if !contains(oldNodePorts, nodePort) { err := nodePortOp.Allocate(nodePort) if err != nil { el := utilvalidation.ErrorList{utilvalidation.NewInvalidError(utilvalidation.NewFieldPath("spec", "ports").Index(i).Child("nodePort"), nodePort, err.Error())} return nil, false, errors.NewInvalid("Service", service.Name, el) } } } else { nodePort, err = nodePortOp.AllocateNext() if err != nil { el := utilvalidation.ErrorList{utilvalidation.NewInvalidError(utilvalidation.NewFieldPath("spec", "ports").Index(i).Child("nodePort"), nodePort, err.Error())} return nil, false, errors.NewInvalid("Service", service.Name, el) } servicePort.NodePort = nodePort } // Detect duplicate node ports; this should have been caught by validation, so we panic if contains(newNodePorts, nodePort) { panic("duplicate node port") } newNodePorts = append(newNodePorts, nodePort) } } else { // Validate should have validated that nodePort == 0 } // The comparison loops are O(N^2), but we don't expect N to be huge // (there's a hard-limit at 2^16, because they're ports; and even 4 ports would be a lot) for _, oldNodePort := range oldNodePorts { if !contains(newNodePorts, oldNodePort) { continue } nodePortOp.ReleaseDeferred(oldNodePort) } // Remove any LoadBalancerStatus now if Type != LoadBalancer; // although loadbalancer delete is actually asynchronous, we don't need to expose the user to that complexity. if service.Spec.Type != api.ServiceTypeLoadBalancer { service.Status.LoadBalancer = api.LoadBalancerStatus{} } out, err := rs.registry.UpdateService(ctx, service) if err == nil { el := nodePortOp.Commit() if el != nil { // problems should be fixed by an eventual reconciliation / restart glog.Errorf("error(s) committing NodePorts changes: %v", el) } } return out, false, err }
// Create ensures a pod is bound to a specific host. func (r *BindingREST) Create(ctx api.Context, obj runtime.Object) (out runtime.Object, err error) { binding := obj.(*api.Binding) // TODO: move me to a binding strategy if len(binding.Target.Kind) != 0 && binding.Target.Kind != "Node" { return nil, errors.NewInvalid("binding", binding.Name, validation.ErrorList{validation.NewInvalidError(validation.NewFieldPath("target", "kind"), binding.Target.Kind, "must be empty or 'Node'")}) } if len(binding.Target.Name) == 0 { return nil, errors.NewInvalid("binding", binding.Name, validation.ErrorList{validation.NewRequiredError(validation.NewFieldPath("target", "name"))}) } err = r.assignPod(ctx, binding.Name, binding.Target.Name, binding.Annotations) out = &unversioned.Status{Status: unversioned.StatusSuccess} return }
func TestNewInvalid(t *testing.T) { testCases := []struct { Err *validation.Error Details *unversioned.StatusDetails }{ { validation.NewDuplicateError(validation.NewFieldPath("field[0].name"), "bar"), &unversioned.StatusDetails{ Kind: "kind", Name: "name", Causes: []unversioned.StatusCause{{ Type: unversioned.CauseTypeFieldValueDuplicate, Field: "field[0].name", }}, }, }, { validation.NewInvalidError(validation.NewFieldPath("field[0].name"), "bar", "detail"), &unversioned.StatusDetails{ Kind: "kind", Name: "name", Causes: []unversioned.StatusCause{{ Type: unversioned.CauseTypeFieldValueInvalid, Field: "field[0].name", }}, }, }, { validation.NewNotFoundError(validation.NewFieldPath("field[0].name"), "bar"), &unversioned.StatusDetails{ Kind: "kind", Name: "name", Causes: []unversioned.StatusCause{{ Type: unversioned.CauseTypeFieldValueNotFound, Field: "field[0].name", }}, }, }, { validation.NewNotSupportedError(validation.NewFieldPath("field[0].name"), "bar", nil), &unversioned.StatusDetails{ Kind: "kind", Name: "name", Causes: []unversioned.StatusCause{{ Type: unversioned.CauseTypeFieldValueNotSupported, Field: "field[0].name", }}, }, }, { validation.NewRequiredError(validation.NewFieldPath("field[0].name")), &unversioned.StatusDetails{ Kind: "kind", Name: "name", Causes: []unversioned.StatusCause{{ Type: unversioned.CauseTypeFieldValueRequired, Field: "field[0].name", }}, }, }, } for i, testCase := range testCases { vErr, expected := testCase.Err, testCase.Details expected.Causes[0].Message = vErr.ErrorBody() err := NewInvalid("kind", "name", validation.ErrorList{vErr}) status := err.(*StatusError).ErrStatus if status.Code != 422 || status.Reason != unversioned.StatusReasonInvalid { t.Errorf("%d: unexpected status: %#v", i, status) } if !reflect.DeepEqual(expected, status.Details) { t.Errorf("%d: expected %#v, got %#v", i, expected, status.Details) } } }
func ValidateHorizontalPodAutoscalerUpdate(newAutoscaler, oldAutoscaler *extensions.HorizontalPodAutoscaler) validation.ErrorList { allErrs := validation.ErrorList{} allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&newAutoscaler.ObjectMeta, &oldAutoscaler.ObjectMeta, validation.NewFieldPath("metadata"))...) allErrs = append(allErrs, validateHorizontalPodAutoscalerSpec(newAutoscaler.Spec, validation.NewFieldPath("spec"))...) return allErrs }
func ValidateHorizontalPodAutoscalerStatusUpdate(controller, oldController *extensions.HorizontalPodAutoscaler) validation.ErrorList { allErrs := validation.ErrorList{} allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&controller.ObjectMeta, &oldController.ObjectMeta, validation.NewFieldPath("metadata"))...) status := controller.Status allErrs = append(allErrs, apivalidation.ValidatePositiveField(int64(status.CurrentReplicas), validation.NewFieldPath("status", "currentReplicas"))...) allErrs = append(allErrs, apivalidation.ValidatePositiveField(int64(status.DesiredReplicas), validation.NewFieldPath("status", "desiredReplicasa"))...) return allErrs }
func ValidateThirdPartyResourceUpdate(update, old *extensions.ThirdPartyResource) validation.ErrorList { allErrs := validation.ErrorList{} allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, validation.NewFieldPath("metadata"))...) allErrs = append(allErrs, ValidateThirdPartyResource(update)...) return allErrs }
func ValidateHorizontalPodAutoscaler(autoscaler *extensions.HorizontalPodAutoscaler) validation.ErrorList { allErrs := validation.ErrorList{} allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&autoscaler.ObjectMeta, true, ValidateHorizontalPodAutoscalerName, validation.NewFieldPath("metadata"))...) allErrs = append(allErrs, validateHorizontalPodAutoscalerSpec(autoscaler.Spec, validation.NewFieldPath("spec"))...) return allErrs }
// ValidateIngressStatusUpdate tests if required fields in the Ingress are set when updating status. func ValidateIngressStatusUpdate(ingress, oldIngress *extensions.Ingress) validation.ErrorList { allErrs := validation.ErrorList{} allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta, validation.NewFieldPath("metadata"))...) allErrs = append(allErrs, apivalidation.ValidateLoadBalancerStatus(&ingress.Status.LoadBalancer, validation.NewFieldPath("status", "loadBalancer"))...) return allErrs }
// ValidateDaemonSet tests if required fields in the DaemonSet are set. func ValidateDaemonSet(controller *extensions.DaemonSet) validation.ErrorList { allErrs := validation.ErrorList{} allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&controller.ObjectMeta, true, ValidateDaemonSetName, validation.NewFieldPath("metadata"))...) allErrs = append(allErrs, ValidateDaemonSetSpec(&controller.Spec, validation.NewFieldPath("spec"))...) return allErrs }
func validateObject(obj runtime.Object) (errors utilvalidation.ErrorList) { switch t := obj.(type) { case *api.ReplicationController: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidateReplicationController(t) case *api.ReplicationControllerList: for i := range t.Items { errors = append(errors, validateObject(&t.Items[i])...) } case *api.Service: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidateService(t) case *api.ServiceList: for i := range t.Items { errors = append(errors, validateObject(&t.Items[i])...) } case *api.Pod: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidatePod(t) case *api.PodList: for i := range t.Items { errors = append(errors, validateObject(&t.Items[i])...) } case *api.PersistentVolume: errors = validation.ValidatePersistentVolume(t) case *api.PersistentVolumeClaim: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidatePersistentVolumeClaim(t) case *api.PodTemplate: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidatePodTemplate(t) case *api.Endpoints: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidateEndpoints(t) case *api.Namespace: errors = validation.ValidateNamespace(t) case *api.Secret: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidateSecret(t) case *api.LimitRange: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidateLimitRange(t) case *api.ResourceQuota: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidateResourceQuota(t) case *extensions.Deployment: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = expvalidation.ValidateDeployment(t) case *extensions.Job: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = expvalidation.ValidateJob(t) case *extensions.Ingress: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = expvalidation.ValidateIngress(t) case *extensions.DaemonSet: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = expvalidation.ValidateDaemonSet(t) default: return utilvalidation.ErrorList{utilvalidation.NewInternalError(utilvalidation.NewFieldPath(""), fmt.Errorf("no validation defined for %#v", obj))} } return errors }
// ValidateIngressUpdate tests if required fields in the Ingress are set. func ValidateIngressUpdate(ingress, oldIngress *extensions.Ingress) validation.ErrorList { allErrs := validation.ErrorList{} allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta, validation.NewFieldPath("metadata"))...) allErrs = append(allErrs, ValidateIngressSpec(&ingress.Spec, validation.NewFieldPath("spec"))...) return allErrs }
// BeforeCreate ensures that common operations for all resources are performed on creation. It only returns // errors that can be converted to api.Status. It invokes PrepareForCreate, then GenerateName, then Validate. // It returns nil if the object should be created. func BeforeCreate(strategy RESTCreateStrategy, ctx api.Context, obj runtime.Object) error { objectMeta, kind, kerr := objectMetaAndKind(strategy, obj) if kerr != nil { return kerr } if strategy.NamespaceScoped() { if !api.ValidNamespace(ctx, objectMeta) { return errors.NewBadRequest("the namespace of the provided object does not match the namespace sent on the request") } } else { objectMeta.Namespace = api.NamespaceNone } objectMeta.DeletionTimestamp = nil objectMeta.DeletionGracePeriodSeconds = nil strategy.PrepareForCreate(obj) api.FillObjectMetaSystemFields(ctx, objectMeta) api.GenerateName(strategy, objectMeta) if errs := strategy.Validate(ctx, obj); len(errs) > 0 { return errors.NewInvalid(kind, objectMeta.Name, errs) } // Custom validation (including name validation) passed // Now run common validation on object meta // Do this *after* custom validation so that specific error messages are shown whenever possible if errs := validation.ValidateObjectMeta(objectMeta, strategy.NamespaceScoped(), validation.ValidatePathSegmentName, utilvalidation.NewFieldPath("metadata")); len(errs) > 0 { return errors.NewInvalid(kind, objectMeta.Name, errs) } strategy.Canonicalize(obj) return nil }
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) { service := obj.(*api.Service) if err := rest.BeforeCreate(Strategy, ctx, obj); err != nil { return nil, err } // TODO: this should probably move to strategy.PrepareForCreate() releaseServiceIP := false defer func() { if releaseServiceIP { if api.IsServiceIPSet(service) { rs.serviceIPs.Release(net.ParseIP(service.Spec.ClusterIP)) } } }() nodePortOp := portallocator.StartOperation(rs.serviceNodePorts) defer nodePortOp.Finish() if api.IsServiceIPRequested(service) { // Allocate next available. ip, err := rs.serviceIPs.AllocateNext() if err != nil { el := utilvalidation.ErrorList{utilvalidation.NewInvalidError(utilvalidation.NewFieldPath("spec", "clusterIP"), service.Spec.ClusterIP, err.Error())} return nil, errors.NewInvalid("Service", service.Name, el) } service.Spec.ClusterIP = ip.String() releaseServiceIP = true } else if api.IsServiceIPSet(service) { // Try to respect the requested IP. if err := rs.serviceIPs.Allocate(net.ParseIP(service.Spec.ClusterIP)); err != nil { el := utilvalidation.ErrorList{utilvalidation.NewInvalidError(utilvalidation.NewFieldPath("spec", "clusterIP"), service.Spec.ClusterIP, err.Error())} return nil, errors.NewInvalid("Service", service.Name, el) } releaseServiceIP = true } assignNodePorts := shouldAssignNodePorts(service) for i := range service.Spec.Ports { servicePort := &service.Spec.Ports[i] if servicePort.NodePort != 0 { err := nodePortOp.Allocate(servicePort.NodePort) if err != nil { el := utilvalidation.ErrorList{utilvalidation.NewInvalidError(utilvalidation.NewFieldPath("spec", "ports").Index(i).Child("nodePort"), servicePort.NodePort, err.Error())} return nil, errors.NewInvalid("Service", service.Name, el) } } else if assignNodePorts { nodePort, err := nodePortOp.AllocateNext() if err != nil { el := utilvalidation.ErrorList{utilvalidation.NewInvalidError(utilvalidation.NewFieldPath("spec", "ports").Index(i).Child("nodePort"), servicePort.NodePort, err.Error())} return nil, errors.NewInvalid("Service", service.Name, el) } servicePort.NodePort = nodePort } } out, err := rs.registry.CreateService(ctx, service) if err != nil { err = rest.CheckGeneratedNameError(Strategy, err, service) } if err == nil { el := nodePortOp.Commit() if el != nil { // these should be caught by an eventual reconciliation / restart glog.Errorf("error(s) committing service node-ports changes: %v", el) } releaseServiceIP = false } return out, err }
// ValidateDaemonSetStatus validates tests if required fields in the DaemonSet Status section func ValidateDaemonSetStatusUpdate(controller, oldController *extensions.DaemonSet) validation.ErrorList { allErrs := validation.ErrorList{} allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&controller.ObjectMeta, &oldController.ObjectMeta, validation.NewFieldPath("metadata"))...) allErrs = append(allErrs, validateDaemonSetStatus(&controller.Status, validation.NewFieldPath("status"))...) return allErrs }
func ValidateDeployment(obj *extensions.Deployment) validation.ErrorList { allErrs := validation.ErrorList{} allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&obj.ObjectMeta, true, ValidateDeploymentName, validation.NewFieldPath("metadata"))...) allErrs = append(allErrs, ValidateDeploymentSpec(&obj.Spec, validation.NewFieldPath("spec"))...) return allErrs }
func ValidateDeploymentUpdate(update, old *extensions.Deployment) validation.ErrorList { allErrs := validation.ErrorList{} allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, validation.NewFieldPath("metadata"))...) allErrs = append(allErrs, ValidateDeploymentSpec(&update.Spec, validation.NewFieldPath("spec"))...) return allErrs }
func ValidateJobUpdateStatus(job, oldJob *extensions.Job) validation.ErrorList { allErrs := validation.ErrorList{} allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&oldJob.ObjectMeta, &job.ObjectMeta, validation.NewFieldPath("metadata"))...) allErrs = append(allErrs, ValidateJobStatusUpdate(job.Status, oldJob.Status)...) return allErrs }
func ValidateJobStatusUpdate(status, oldStatus extensions.JobStatus) validation.ErrorList { allErrs := validation.ErrorList{} allErrs = append(allErrs, ValidateJobStatus(&status, validation.NewFieldPath("status"))...) return allErrs }
func TestCheckInvalidErr(t *testing.T) { tests := []struct { err error expected string }{ { errors.NewInvalid("Invalid1", "invalidation", validation.ErrorList{validation.NewInvalidError(validation.NewFieldPath("field"), "single", "details")}), `Error from server: Invalid1 "invalidation" is invalid: field: invalid value 'single', Details: details`, }, { errors.NewInvalid("Invalid2", "invalidation", validation.ErrorList{validation.NewInvalidError(validation.NewFieldPath("field1"), "multi1", "details"), validation.NewInvalidError(validation.NewFieldPath("field2"), "multi2", "details")}), `Error from server: Invalid2 "invalidation" is invalid: [field1: invalid value 'multi1', Details: details, field2: invalid value 'multi2', Details: details]`, }, { errors.NewInvalid("Invalid3", "invalidation", validation.ErrorList{}), `Error from server: Invalid3 "invalidation" is invalid: <nil>`, }, } var errReturned string errHandle := func(err string) { errReturned = err } for _, test := range tests { checkErr(test.err, errHandle) if errReturned != test.expected { t.Fatalf("Got: %s, expected: %s", errReturned, test.expected) } } }
func TestCompatibility_v1_PodSecurityContext(t *testing.T) { cases := []struct { name string input string expectedKeys map[string]string absentKeys []string }{ { name: "hostNetwork = true", input: ` { "kind":"Pod", "apiVersion":"v1", "metadata":{"name":"my-pod-name", "namespace":"my-pod-namespace"}, "spec": { "hostNetwork": true, "containers":[{ "name":"a", "image":"my-container-image" }] } } `, expectedKeys: map[string]string{ "spec.hostNetwork": "true", }, }, { name: "hostNetwork = false", input: ` { "kind":"Pod", "apiVersion":"v1", "metadata":{"name":"my-pod-name", "namespace":"my-pod-namespace"}, "spec": { "hostNetwork": false, "containers":[{ "name":"a", "image":"my-container-image" }] } } `, absentKeys: []string{ "spec.hostNetwork", }, }, { name: "hostIPC = true", input: ` { "kind":"Pod", "apiVersion":"v1", "metadata":{"name":"my-pod-name", "namespace":"my-pod-namespace"}, "spec": { "hostIPC": true, "containers":[{ "name":"a", "image":"my-container-image" }] } } `, expectedKeys: map[string]string{ "spec.hostIPC": "true", }, }, { name: "hostIPC = false", input: ` { "kind":"Pod", "apiVersion":"v1", "metadata":{"name":"my-pod-name", "namespace":"my-pod-namespace"}, "spec": { "hostIPC": false, "containers":[{ "name":"a", "image":"my-container-image" }] } } `, absentKeys: []string{ "spec.hostIPC", }, }, { name: "hostPID = true", input: ` { "kind":"Pod", "apiVersion":"v1", "metadata":{"name":"my-pod-name", "namespace":"my-pod-namespace"}, "spec": { "hostPID": true, "containers":[{ "name":"a", "image":"my-container-image" }] } } `, expectedKeys: map[string]string{ "spec.hostPID": "true", }, }, { name: "hostPID = false", input: ` { "kind":"Pod", "apiVersion":"v1", "metadata":{"name":"my-pod-name", "namespace":"my-pod-namespace"}, "spec": { "hostPID": false, "containers":[{ "name":"a", "image":"my-container-image" }] } } `, absentKeys: []string{ "spec.hostPID", }, }, { name: "reseting defaults for pre-v1.1 mirror pods", input: ` { "kind":"Pod", "apiVersion":"v1", "metadata":{ "name":"my-pod-name", "namespace":"my-pod-namespace", "annotations": { "kubernetes.io/config.mirror": "mirror" } }, "spec": { "containers":[{ "name":"a", "image":"my-container-image", "resources": { "limits": { "cpu": "100m" } } }] } } `, absentKeys: []string{ "spec.terminationGracePeriodSeconds", "spec.containers[0].resources.requests", }, }, { name: "preserving defaults for v1.1+ mirror pods", input: ` { "kind":"Pod", "apiVersion":"v1", "metadata":{ "name":"my-pod-name", "namespace":"my-pod-namespace", "annotations": { "kubernetes.io/config.mirror": "cbe924f710c7e26f7693d6a341bcfad0" } }, "spec": { "containers":[{ "name":"a", "image":"my-container-image", "resources": { "limits": { "cpu": "100m" } } }] } } `, expectedKeys: map[string]string{ "spec.terminationGracePeriodSeconds": "30", "spec.containers[0].resources.requests": "map[cpu:100m]", }, }, } validator := func(obj runtime.Object) utilvalidation.ErrorList { return validation.ValidatePodSpec(&(obj.(*api.Pod).Spec), utilvalidation.NewFieldPath("spec")) } for _, tc := range cases { t.Logf("Testing 1.0.0 backward compatibility for %v", tc.name) compat.TestCompatibility(t, "v1", []byte(tc.input), validator, tc.expectedKeys, tc.absentKeys) } }