...

Source file src/sigs.k8s.io/cli-utils/pkg/object/validation/validate.go

Documentation: sigs.k8s.io/cli-utils/pkg/object/validation

     1  // Copyright 2022 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package validation
     5  
     6  import (
     7  	"k8s.io/apimachinery/pkg/api/meta"
     8  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
     9  	"k8s.io/apimachinery/pkg/util/validation/field"
    10  	"sigs.k8s.io/cli-utils/pkg/multierror"
    11  	"sigs.k8s.io/cli-utils/pkg/object"
    12  )
    13  
    14  // Validator contains functionality for validating a set of resources prior
    15  // to being used by the Apply functionality. This imposes some constraint not
    16  // always required, such as namespaced resources must have the namespace set.
    17  type Validator struct {
    18  	Mapper    meta.RESTMapper
    19  	Collector *Collector
    20  }
    21  
    22  // Validate validates the provided resources. A RESTMapper will be used
    23  // to fetch type information from the live cluster.
    24  func (v *Validator) Validate(objs []*unstructured.Unstructured) {
    25  	crds := findCRDs(objs)
    26  	for _, obj := range objs {
    27  		var objErrors []error
    28  		if err := v.validateKind(obj); err != nil {
    29  			objErrors = append(objErrors, err)
    30  		}
    31  		if err := v.validateName(obj); err != nil {
    32  			objErrors = append(objErrors, err)
    33  		}
    34  		if err := v.validateNamespace(obj, crds); err != nil {
    35  			objErrors = append(objErrors, err)
    36  		}
    37  		if len(objErrors) > 0 {
    38  			// one error per object
    39  			v.Collector.Collect(NewError(
    40  				multierror.Wrap(objErrors...),
    41  				object.UnstructuredToObjMetadata(obj),
    42  			))
    43  		}
    44  	}
    45  }
    46  
    47  // findCRDs looks through the provided resources and returns a slice with
    48  // the resources that are CRDs.
    49  func findCRDs(us []*unstructured.Unstructured) []*unstructured.Unstructured {
    50  	var crds []*unstructured.Unstructured
    51  	for _, u := range us {
    52  		if object.IsCRD(u) {
    53  			crds = append(crds, u)
    54  		}
    55  	}
    56  	return crds
    57  }
    58  
    59  // validateKind validates the value of the kind field of the resource.
    60  func (v *Validator) validateKind(u *unstructured.Unstructured) error {
    61  	if u.GetKind() == "" {
    62  		return field.Required(field.NewPath("kind"), "kind is required")
    63  	}
    64  	return nil
    65  }
    66  
    67  // validateName validates the value of the name field of the resource.
    68  func (v *Validator) validateName(u *unstructured.Unstructured) error {
    69  	if u.GetName() == "" {
    70  		return field.Required(field.NewPath("metadata", "name"), "name is required")
    71  	}
    72  	return nil
    73  }
    74  
    75  // validateNamespace validates the value of the namespace field of the resource.
    76  func (v *Validator) validateNamespace(u *unstructured.Unstructured, crds []*unstructured.Unstructured) error {
    77  	// skip namespace validation if kind is missing (avoid redundant error)
    78  	if u.GetKind() == "" {
    79  		return nil
    80  	}
    81  	scope, err := object.LookupResourceScope(u, crds, v.Mapper)
    82  	if err != nil {
    83  		return err
    84  	}
    85  
    86  	ns := u.GetNamespace()
    87  	if scope == meta.RESTScopeNamespace && ns == "" {
    88  		return field.Required(field.NewPath("metadata", "namespace"), "namespace is required")
    89  	}
    90  	if scope == meta.RESTScopeRoot && ns != "" {
    91  		return field.Invalid(field.NewPath("metadata", "namespace"), ns, "namespace must be empty")
    92  	}
    93  	return nil
    94  }
    95  

View as plain text