@@ -6,14 +6,18 @@ import (
6
6
"encoding/json"
7
7
"fmt"
8
8
"io"
9
+ "os"
10
+ "path/filepath"
9
11
"regexp"
12
+ "slices"
10
13
"strings"
11
14
12
15
jsonnet "github.com/google/go-jsonnet"
13
16
"github.com/google/go-jsonnet/ast"
14
17
"github.com/grafana/tanka/pkg/helm"
15
18
"github.com/grafana/tanka/pkg/kustomize"
16
19
"github.com/pkg/errors"
20
+ "github.com/rs/zerolog/log"
17
21
yaml "gopkg.in/yaml.v3"
18
22
)
19
23
@@ -42,6 +46,14 @@ func Funcs() []*jsonnet.NativeFunction {
42
46
}
43
47
}
44
48
49
+ // VMFuncs returns a slice of functions similar to Funcs but are passed the jsonnet VM
50
+ // for in-line evaluation
51
+ func VMFuncs (vm * jsonnet.VM ) []* jsonnet.NativeFunction {
52
+ return []* jsonnet.NativeFunction {
53
+ importFiles (vm ),
54
+ }
55
+ }
56
+
45
57
// parseJSON wraps `json.Unmarshal` to convert a json string into a dict
46
58
func parseJSON () * jsonnet.NativeFunction {
47
59
return & jsonnet.NativeFunction {
@@ -178,3 +190,75 @@ func regexSubst() *jsonnet.NativeFunction {
178
190
},
179
191
}
180
192
}
193
+
194
+ type importFilesOpts struct {
195
+ CalledFrom string `json:"calledFrom"`
196
+ Exclude []string `json:"exclude"`
197
+ Extension string `json:"extension"`
198
+ }
199
+
200
+ func parseImportOpts (data interface {}) (* importFilesOpts , error ) {
201
+ c , err := json .Marshal (data )
202
+ if err != nil {
203
+ return nil , err
204
+ }
205
+
206
+ // default extension to `.libsonnet`
207
+ opts := importFilesOpts {
208
+ Extension : ".libsonnet" ,
209
+ }
210
+ if err := json .Unmarshal (c , & opts ); err != nil {
211
+ return nil , err
212
+ }
213
+ if opts .CalledFrom == "" {
214
+ return nil , fmt .Errorf ("importFiles: `opts.calledFrom` is unset or empty\n Tanka needs this to find your directory." )
215
+ }
216
+ return & opts , nil
217
+ }
218
+
219
+ // importFiles imports and evaluates all matching jsonnet files in the given relative directory
220
+ func importFiles (vm * jsonnet.VM ) * jsonnet.NativeFunction {
221
+ return & jsonnet.NativeFunction {
222
+ Name : "importFiles" ,
223
+ Params : ast.Identifiers {"directory" , "opts" },
224
+ Func : func (data []interface {}) (interface {}, error ) {
225
+ dir , ok := data [0 ].(string )
226
+ if ! ok {
227
+ return nil , fmt .Errorf ("first argument 'directory' must be of 'string' type, got '%T' instead" , data [0 ])
228
+ }
229
+ opts , err := parseImportOpts (data [1 ])
230
+ if err != nil {
231
+ return nil , err
232
+ }
233
+ dirPath := filepath .Join (filepath .Dir (opts .CalledFrom ), dir )
234
+ imports := make (map [string ]interface {})
235
+ err = filepath .Walk (dirPath , func (path string , info os.FileInfo , err error ) error {
236
+ if err != nil {
237
+ return err
238
+ }
239
+ if info .IsDir () || ! strings .HasSuffix (info .Name (), opts .Extension ) {
240
+ return nil
241
+ }
242
+ if slices .Contains (opts .Exclude , info .Name ()) {
243
+ return nil
244
+ }
245
+ log .Debug ().Msgf ("importFiles: parsing file %s" , info .Name ())
246
+ resultStr , err := vm .EvaluateFile (path )
247
+ if err != nil {
248
+ return fmt .Errorf ("importFiles: failed to evaluate %s: %s" , path , err )
249
+ }
250
+ var result interface {}
251
+ err = json .Unmarshal ([]byte (resultStr ), & result )
252
+ if err != nil {
253
+ return err
254
+ }
255
+ imports [info .Name ()] = result
256
+ return nil
257
+ })
258
+ if err != nil {
259
+ return nil , err
260
+ }
261
+ return imports , nil
262
+ },
263
+ }
264
+ }
0 commit comments