-
Notifications
You must be signed in to change notification settings - Fork 165
/
jsonnet.go
86 lines (74 loc) · 2.62 KB
/
jsonnet.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
package kustomize
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"github.com/google/go-jsonnet"
"github.com/google/go-jsonnet/ast"
"github.com/grafana/tanka/pkg/kubernetes/manifest"
)
// JsonnetOpts are additional properties the consumer of the native func might
// pass.
type JsonnetOpts struct {
// CalledFrom is the file that calls kustomizeBuild. This is used to find the
// vendored Kustomize relative to this file
CalledFrom string `json:"calledFrom"`
// NameBuild is used to create the keys in the resulting map
NameFormat string `json:"nameFormat"`
}
// NativeFunc returns a jsonnet native function that provides the same
// functionality as `Kustomize.Build` of this package. Kustomize yamls are required to be
// present on the local filesystem, at a relative location to the file that
// calls `kustomize.build()` / `std.native('kustomizeBuild')`. This guarantees
// hermeticity
func NativeFunc(k Kustomize) *jsonnet.NativeFunction {
return &jsonnet.NativeFunction{
Name: "kustomizeBuild",
// Similar to `kustomize build {path}` where {path} is a local path
Params: ast.Identifiers{"path", "opts"},
Func: func(data []interface{}) (interface{}, error) {
path, ok := data[0].(string)
if !ok {
return nil, fmt.Errorf("Argument 'path' must be of 'string' type, got '%T' instead", data[0])
}
// TODO: validate data[1] actually follows the struct scheme
opts, err := parseOpts(data[1])
if err != nil {
return "", err
}
// resolve the Kustomize path relative to the caller
callerDir := filepath.Dir(opts.CalledFrom)
actual_path := filepath.Join(callerDir, path)
if _, err := os.Stat(actual_path); err != nil {
return nil, fmt.Errorf("kustomizeBuild: Failed to find kustomization at '%s': %s. See https://tanka.dev/kustomize#failed-to-find-kustomization", actual_path, err)
}
// render resources
list, err := k.Build(actual_path)
if err != nil {
return nil, err
}
// convert list to map
out, err := manifest.ListAsMap(list, opts.NameFormat)
if err != nil {
return nil, err
}
return out, nil
},
}
}
func parseOpts(data interface{}) (*JsonnetOpts, error) {
c, err := json.Marshal(data)
if err != nil {
return nil, err
}
var opts JsonnetOpts
if err := json.Unmarshal(c, &opts); err != nil {
return nil, err
}
// Kustomize paths are only allowed at relative paths. Use conf.CalledFrom to find the callers directory
if opts.CalledFrom == "" {
return nil, fmt.Errorf("kustomizeBuild: 'opts.calledFrom' is unset or empty.\nTanka needs this to find your Kustomize. See https://tanka.dev/kustomize#optscalledfrom-unset\n")
}
return &opts, nil
}