github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/cli/visitor/decode.go (about)

     1  package visitor
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  
     9  	"github.com/pkg/errors"
    10  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    11  	"k8s.io/apimachinery/pkg/runtime"
    12  	"k8s.io/apimachinery/pkg/util/yaml"
    13  )
    14  
    15  // Given a set of YAML inputs, decode them into real API objects.
    16  //
    17  // The scheme is used to lookup the objects by group/version/kind.
    18  func DecodeAll(scheme *runtime.Scheme, vs []Interface) ([]runtime.Object, error) {
    19  	result := []runtime.Object{}
    20  	for _, v := range vs {
    21  		objs, err := Decode(scheme, v)
    22  		if err != nil {
    23  			return nil, err
    24  		}
    25  		result = append(result, objs...)
    26  	}
    27  	return result, nil
    28  }
    29  
    30  func Decode(scheme *runtime.Scheme, v Interface) ([]runtime.Object, error) {
    31  	r, err := v.Open()
    32  	if err != nil {
    33  		return nil, err
    34  	}
    35  	defer r.Close()
    36  
    37  	result, err := ParseStream(scheme, r)
    38  	if err != nil {
    39  		return nil, errors.Wrapf(err, "visiting %s", v.Name())
    40  	}
    41  	return result, nil
    42  }
    43  
    44  // Parses a stream of Tilt configuration objects.
    45  //
    46  // In kubectl, the CLI has to get the type information from the server in order
    47  // to perform validation. In Tilt (today), we don't have to worry about version skew,
    48  // so we can more aggressively validate up-front for misspelled fields
    49  // and malformed YAML. So this parser is a bit stricter than the normal kubectl code.
    50  func ParseStream(scheme *runtime.Scheme, r io.Reader) ([]runtime.Object, error) {
    51  	decoder := yaml.NewYAMLOrJSONDecoder(r, 4096)
    52  	result := []runtime.Object{}
    53  	for {
    54  		msg := json.RawMessage{} // First convert into json bytes
    55  		if err := decoder.Decode(&msg); err != nil {
    56  			if err == io.EOF {
    57  				break
    58  			}
    59  			return nil, err
    60  		}
    61  
    62  		// Then decode into the type.
    63  		tm := metav1.TypeMeta{}
    64  		err := json.Unmarshal([]byte(msg), &tm)
    65  		if err != nil {
    66  			return nil, err
    67  		}
    68  
    69  		// Turn the type name into a native go object.
    70  		obj, err := scheme.New(tm.GroupVersionKind())
    71  		if err != nil {
    72  			return nil, err
    73  		}
    74  
    75  		// Then decode the object into its native go object
    76  		objDecoder := json.NewDecoder(bytes.NewBuffer([]byte(msg)))
    77  		objDecoder.DisallowUnknownFields()
    78  		if err := objDecoder.Decode(&obj); err != nil {
    79  			return nil, fmt.Errorf("decoding %s: %v\nOriginal object:\n%s", tm, err, string([]byte(msg)))
    80  		}
    81  
    82  		result = append(result, obj)
    83  	}
    84  	return result, nil
    85  }