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 }