-
Notifications
You must be signed in to change notification settings - Fork 303
/
yaml.go
189 lines (156 loc) · 5.44 KB
/
yaml.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
package encoding
import (
"fmt"
"io"
"os"
"strings"
"github.com/pkg/errors"
"go.starlark.net/starlark"
k8syaml "k8s.io/apimachinery/pkg/util/yaml"
"sigs.k8s.io/yaml"
tiltfile_io "github.com/tilt-dev/tilt/internal/tiltfile/io"
"github.com/tilt-dev/tilt/internal/tiltfile/starkit"
"github.com/tilt-dev/tilt/internal/tiltfile/value"
)
// takes a list of objects that came from deserializing a potential starlark stream
// ensures there's only one, and returns it
func singleYAMLDoc(l *starlark.List) (starlark.Value, error) {
switch l.Len() {
case 0:
// if there are zero documents in the stream, that's actually an empty yaml document, which is a yaml
// document with a scalar value of NULL
return starlark.None, nil
case 1:
return l.Index(0), nil
default:
return nil, fmt.Errorf("expected a yaml document but found a yaml stream (documents separated by `---`). use %s instead to decode a yaml stream", decodeYAMLStreamN)
}
}
func readYAMLStreamAsStarlarkList(thread *starlark.Thread, path starlark.String, defaultValue *starlark.List) (*starlark.List, error) {
localPath, err := value.ValueToAbsPath(thread, path)
if err != nil {
return nil, fmt.Errorf("Argument 0 (paths): %v", err)
}
contents, err := tiltfile_io.ReadFile(thread, localPath)
if err != nil {
// Return the default value if the file doesn't exist AND a default value was given
if os.IsNotExist(err) && defaultValue != nil {
return defaultValue, nil
}
return nil, err
}
return yamlStreamToStarlark(string(contents), path.GoString())
}
func readYAMLStream(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var path starlark.String
var defaultValue *starlark.List
if err := starkit.UnpackArgs(thread, fn.Name(), args, kwargs, "paths", &path, "default?", &defaultValue); err != nil {
return nil, err
}
return readYAMLStreamAsStarlarkList(thread, path, defaultValue)
}
func readYAML(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var path starlark.String
var defaultValue starlark.Value
if err := starkit.UnpackArgs(thread, fn.Name(), args, kwargs, "paths", &path, "default?", &defaultValue); err != nil {
return nil, err
}
var defaultValueList *starlark.List
if defaultValue != nil {
defaultValueList = starlark.NewList([]starlark.Value{defaultValue})
}
l, err := readYAMLStreamAsStarlarkList(thread, path, defaultValueList)
if err != nil {
return nil, err
}
return singleYAMLDoc(l)
}
func decodeYAMLStreamAsStarlarkList(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (*starlark.List, error) {
var contents value.Stringable
if err := starkit.UnpackArgs(thread, fn.Name(), args, kwargs, "yaml", &contents); err != nil {
return nil, err
}
return yamlStreamToStarlark(contents.Value, "")
}
func decodeYAMLStream(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
return decodeYAMLStreamAsStarlarkList(thread, fn, args, kwargs)
}
func decodeYAML(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
l, err := decodeYAMLStreamAsStarlarkList(thread, fn, args, kwargs)
if err != nil {
return nil, err
}
return singleYAMLDoc(l)
}
func yamlStreamToStarlark(s string, source string) (*starlark.List, error) {
var ret []starlark.Value
d := k8syaml.NewYAMLToJSONDecoder(strings.NewReader(s))
for {
var decodedYAML interface{}
err := d.Decode(&decodedYAML)
if err == io.EOF {
break
}
if err != nil {
errmsg := "error parsing YAML"
if source != "" {
errmsg += fmt.Sprintf(" from %s", source)
}
return nil, errors.Wrap(err, errmsg)
}
v, err := convertStructuredDataToStarlark(decodedYAML)
if err != nil {
errmsg := "error converting YAML to Starlark"
if source != "" {
errmsg += fmt.Sprintf(" from %s", source)
}
return nil, errors.Wrap(err, errmsg)
}
if v == starlark.None {
continue // ignore empty entries
}
ret = append(ret, v)
}
return starlark.NewList(ret), nil
}
// dumps yaml to a string
func encodeYAML(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var obj starlark.Value
if err := starkit.UnpackArgs(thread, fn.Name(), args, kwargs, "obj", &obj); err != nil {
return nil, err
}
ret, err := starlarkToYAMLString(obj)
if err != nil {
return nil, err
}
return tiltfile_io.NewBlob(ret, "encode_yaml"), nil
}
func encodeYAMLStream(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var objs *starlark.List
if err := starkit.UnpackArgs(thread, fn.Name(), args, kwargs, "objs", &objs); err != nil {
return nil, err
}
var yamlDocs []string
it := objs.Iterate()
defer it.Done()
var v starlark.Value
for it.Next(&v) {
s, err := starlarkToYAMLString(v)
if err != nil {
return nil, err
}
yamlDocs = append(yamlDocs, s)
}
return tiltfile_io.NewBlob(strings.Join(yamlDocs, "---\n"), "encode_yaml_stream"), nil
}
func starlarkToYAMLString(obj starlark.Value) (string, error) {
v, err := convertStarlarkToStructuredData(obj)
if err != nil {
return "", errors.Wrap(err, "error converting object from starlark")
}
b, err := yaml.Marshal(v)
if err != nil {
return "", errors.Wrap(err, "error serializing object to yaml")
}
return string(b), nil
}