Skip to content
Snippets Groups Projects
Commit c3fcf414 authored by Alex Clemmer's avatar Alex Clemmer
Browse files

Transition away from cgo, and towards go-jsonnet

parent 098f5fc1
No related branches found
No related tags found
No related merge requests found
......@@ -18,8 +18,8 @@ package cmd
import (
"fmt"
jsonnet "github.com/google/go-jsonnet"
"github.com/spf13/cobra"
jsonnet "github.com/strickyak/jsonnet_cgo"
"k8s.io/client-go/pkg/version"
)
......
......@@ -72,20 +72,27 @@ func visit(node ast.Node, imports *[]ast.Import) error {
switch n := node.(type) {
case *ast.Import:
// Add parameter-type imports to the list of replacements.
if strings.HasPrefix(n.File, paramPrefix) {
param := strings.TrimPrefix(n.File, paramPrefix)
if strings.HasPrefix(n.File.Value, paramPrefix) {
param := strings.TrimPrefix(n.File.Value, paramPrefix)
if len(param) < 1 {
return errors.New("There must be a parameter following import param://")
}
*imports = append(*imports, *n)
}
case *ast.Apply:
for _, arg := range n.Arguments {
for _, arg := range n.Arguments.Positional {
err := visit(arg, imports)
if err != nil {
return err
}
}
for _, arg := range n.Arguments.Named {
err := visit(arg.Arg, imports)
if err != nil {
return err
}
}
return visit(n.Target, imports)
case *ast.ApplyBrace:
err := visit(n.Left, imports)
......@@ -101,11 +108,9 @@ func visit(node ast.Node, imports *[]ast.Import) error {
}
}
case *ast.ArrayComp:
for _, spec := range n.Specs {
err := visitCompSpec(spec, imports)
if err != nil {
return err
}
err := visitCompSpec(n.Spec, imports)
if err != nil {
return err
}
return visit(n.Body, imports)
case *ast.Assert:
......@@ -137,6 +142,13 @@ func visit(node ast.Node, imports *[]ast.Import) error {
case *ast.Error:
return visit(n.Expr, imports)
case *ast.Function:
for _, p := range n.Parameters.Optional {
err := visit(p.DefaultArg, imports)
if err != nil {
return err
}
}
return visit(n.Body, imports)
case *ast.Index:
err := visit(n.Target, imports)
......@@ -193,22 +205,10 @@ func visit(node ast.Node, imports *[]ast.Import) error {
return err
}
}
for _, spec := range n.Specs {
err := visitCompSpec(spec, imports)
if err != nil {
return err
}
}
case *ast.ObjectComprehensionSimple:
err := visit(n.Field, imports)
err := visitCompSpec(n.Spec, imports)
if err != nil {
return err
}
err = visit(n.Value, imports)
if err != nil {
return err
}
return visit(n.Array, imports)
case *ast.SuperIndex:
return visit(n.Index, imports)
case *ast.InSuper:
......@@ -235,11 +235,40 @@ func visit(node ast.Node, imports *[]ast.Import) error {
return nil
}
func visitCompSpec(node ast.CompSpec, imports *[]ast.Import) error {
func visitCompSpec(node ast.ForSpec, imports *[]ast.Import) error {
if node.Outer != nil {
err := visitCompSpec(*node.Outer, imports)
if err != nil {
return err
}
}
for _, ifspec := range node.Conditions {
err := visit(ifspec.Expr, imports)
if err != nil {
return err
}
}
return visit(node.Expr, imports)
}
func visitObjectField(node ast.ObjectField, imports *[]ast.Import) error {
if node.Method != nil {
err := visit(node.Method, imports)
if err != nil {
return err
}
}
if node.Params != nil {
for _, p := range node.Params.Optional {
err := visit(p.DefaultArg, imports)
if err != nil {
return err
}
}
}
err := visit(node.Expr1, imports)
if err != nil {
return err
......@@ -260,6 +289,12 @@ func visitDesugaredObjectField(node ast.DesugaredObjectField, imports *[]ast.Imp
}
func visitLocalBind(node ast.LocalBind, imports *[]ast.Import) error {
if node.Fun != nil {
err := visit(node.Fun, imports)
if err != nil {
return err
}
}
return visit(node.Body, imports)
}
......@@ -280,7 +315,7 @@ func replace(jsonnet string, imports []ast.Import) string {
})
for _, im := range imports {
param := paramReplacementPrefix + strings.TrimPrefix(im.File, paramPrefix) + paramReplacementSuffix
param := paramReplacementPrefix + strings.TrimPrefix(im.File.Value, paramPrefix) + paramReplacementSuffix
lineStart := im.Loc().Begin.Line
lineEnd := im.Loc().End.Line
......
......@@ -8,9 +8,9 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
jsonnet "github.com/google/go-jsonnet"
"github.com/ksonnet/ksonnet/utils"
log "github.com/sirupsen/logrus"
jsonnet "github.com/strickyak/jsonnet_cgo"
)
type Expander struct {
......@@ -31,7 +31,6 @@ func (spec *Expander) Expand(paths []string) ([]*unstructured.Unstructured, erro
if err != nil {
return nil, err
}
defer vm.Destroy()
res := []*unstructured.Unstructured{}
for _, path := range paths {
......@@ -47,18 +46,23 @@ func (spec *Expander) Expand(paths []string) ([]*unstructured.Unstructured, erro
// JsonnetVM constructs a new jsonnet.VM, according to command line
// flags
func (spec *Expander) jsonnetVM() (*jsonnet.VM, error) {
vm := jsonnet.Make()
vm := jsonnet.MakeVM()
importer := jsonnet.FileImporter{
JPaths: []string{},
}
for _, p := range spec.EnvJPath {
log.Debugln("Adding jsonnet search path", p)
vm.JpathAdd(p)
importer.JPaths = append(importer.JPaths, p)
}
for _, p := range spec.FlagJpath {
log.Debugln("Adding jsonnet search path", p)
vm.JpathAdd(p)
importer.JPaths = append(importer.JPaths, p)
}
vm.Importer(&importer)
for _, extvar := range spec.ExtVars {
kv := strings.SplitN(extvar, "=", 2)
switch len(kv) {
......@@ -92,12 +96,12 @@ func (spec *Expander) jsonnetVM() (*jsonnet.VM, error) {
case 1:
v, present := os.LookupEnv(kv[0])
if present {
vm.TlaVar(kv[0], v)
vm.TLAVar(kv[0], v)
} else {
return nil, fmt.Errorf("Missing environment variable: %s", kv[0])
}
case 2:
vm.TlaVar(kv[0], kv[1])
vm.TLAVar(kv[0], kv[1])
}
}
......@@ -110,7 +114,7 @@ func (spec *Expander) jsonnetVM() (*jsonnet.VM, error) {
if err != nil {
return nil, err
}
vm.TlaVar(kv[0], string(v))
vm.TLAVar(kv[0], string(v))
}
for _, extcode := range spec.ExtCodes {
......
......@@ -24,8 +24,8 @@ import (
"os"
"path/filepath"
jsonnet "github.com/google/go-jsonnet"
log "github.com/sirupsen/logrus"
jsonnet "github.com/strickyak/jsonnet_cgo"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/yaml"
......@@ -126,7 +126,12 @@ func jsonWalk(obj interface{}) ([]interface{}, error) {
}
func jsonnetReader(vm *jsonnet.VM, path string) ([]runtime.Object, error) {
jsonstr, err := vm.EvaluateFile(path)
jsonnetBytes, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
jsonstr, err := vm.EvaluateSnippet(path, string(jsonnetBytes))
if err != nil {
return nil, err
}
......
......@@ -24,7 +24,8 @@ import (
goyaml "github.com/ghodss/yaml"
jsonnet "github.com/strickyak/jsonnet_cgo"
jsonnet "github.com/google/go-jsonnet"
"github.com/google/go-jsonnet/ast"
"k8s.io/apimachinery/pkg/util/yaml"
)
......@@ -48,61 +49,110 @@ func RegisterNativeFuncs(vm *jsonnet.VM, resolver Resolver) {
// "*FromJson" functions will be replaced by regular native
// version when libjsonnet is able to support this.
vm.NativeCallback("parseJson", []string{"json"}, func(data []byte) (res interface{}, err error) {
err = json.Unmarshal(data, &res)
return
})
vm.NativeCallback("parseYaml", []string{"yaml"}, func(data []byte) ([]interface{}, error) {
ret := []interface{}{}
d := yaml.NewYAMLToJSONDecoder(bytes.NewReader(data))
for {
var doc interface{}
if err := d.Decode(&doc); err != nil {
if err == io.EOF {
break
vm.NativeFunction(
&jsonnet.NativeFunction{
Name: "parseJson",
Params: ast.Identifiers{"json"},
Func: func(dataString []interface{}) (res interface{}, err error) {
data := []byte(dataString[0].(string))
err = json.Unmarshal(data, &res)
return
},
})
vm.NativeFunction(
&jsonnet.NativeFunction{
Name: "parseYaml",
Params: ast.Identifiers{"yaml"},
Func: func(dataString []interface{}) (interface{}, error) {
data := []byte(dataString[0].(string))
ret := []interface{}{}
d := yaml.NewYAMLToJSONDecoder(bytes.NewReader(data))
for {
var doc interface{}
if err := d.Decode(&doc); err != nil {
if err == io.EOF {
break
}
return nil, err
}
ret = append(ret, doc)
}
return nil, err
}
ret = append(ret, doc)
}
return ret, nil
})
vm.NativeCallback("manifestJsonFromJson", []string{"json", "indent"}, func(data []byte, indent int) (string, error) {
data = bytes.TrimSpace(data)
buf := bytes.Buffer{}
if err := json.Indent(&buf, data, "", strings.Repeat(" ", indent)); err != nil {
return "", err
}
buf.WriteString("\n")
return buf.String(), nil
})
vm.NativeCallback("manifestYamlFromJson", []string{"json"}, func(data []byte) (string, error) {
var input interface{}
if err := json.Unmarshal(data, &input); err != nil {
return "", err
}
output, err := goyaml.Marshal(input)
return string(output), err
})
vm.NativeCallback("resolveImage", []string{"image"}, func(image string) (string, error) {
return resolveImage(resolver, image)
})
vm.NativeCallback("escapeStringRegex", []string{"str"}, func(s string) (string, error) {
return regexp.QuoteMeta(s), nil
})
vm.NativeCallback("regexMatch", []string{"regex", "string"}, regexp.MatchString)
vm.NativeCallback("regexSubst", []string{"regex", "src", "repl"}, func(regex, src, repl string) (string, error) {
r, err := regexp.Compile(regex)
if err != nil {
return "", err
}
return r.ReplaceAllString(src, repl), nil
})
return ret, nil
},
})
vm.NativeFunction(
&jsonnet.NativeFunction{
Name: "manifestJsonFromJson",
Params: ast.Identifiers{"json", "indent"},
Func: func(data []interface{}) (interface{}, error) {
indent := int(data[1].(float64))
dataBytes := []byte(data[0].(string))
dataBytes = bytes.TrimSpace(dataBytes)
buf := bytes.Buffer{}
if err := json.Indent(&buf, dataBytes, "", strings.Repeat(" ", indent)); err != nil {
return "", err
}
buf.WriteString("\n")
return buf.String(), nil
},
})
vm.NativeFunction(
&jsonnet.NativeFunction{
Name: "manifestYamlFromJson",
Params: ast.Identifiers{"json"},
Func: func(data []interface{}) (interface{}, error) {
var input interface{}
dataBytes := []byte(data[0].(string))
if err := json.Unmarshal(dataBytes, &input); err != nil {
return "", err
}
output, err := goyaml.Marshal(input)
return string(output), err
},
})
vm.NativeFunction(
&jsonnet.NativeFunction{
Name: "resolveImage",
Params: ast.Identifiers{"image"},
Func: func(image []interface{}) (interface{}, error) {
return resolveImage(resolver, image[0].(string))
},
})
vm.NativeFunction(
&jsonnet.NativeFunction{
Name: "escapeStringRegex",
Params: ast.Identifiers{"str"},
Func: func(s []interface{}) (interface{}, error) {
return regexp.QuoteMeta(s[0].(string)), nil
},
})
vm.NativeFunction(
&jsonnet.NativeFunction{
Name: "regexMatch",
Params: ast.Identifiers{"regex", "string"},
Func: func(s []interface{}) (interface{}, error) {
return regexp.MatchString(s[0].(string), s[1].(string))
},
})
vm.NativeFunction(
&jsonnet.NativeFunction{
Name: "regexSubst",
Params: ast.Identifiers{"regex", "src", "repl"},
Func: func(data []interface{}) (interface{}, error) {
regex, src, repl := data[0].(string), data[1].(string), data[2].(string)
r, err := regexp.Compile(regex)
if err != nil {
return "", err
}
return r.ReplaceAllString(src, repl), nil
},
})
}
......@@ -18,7 +18,7 @@ package utils
import (
"testing"
jsonnet "github.com/strickyak/jsonnet_cgo"
jsonnet "github.com/google/go-jsonnet"
)
// check there is no err, and a == b.
......@@ -31,8 +31,7 @@ func check(t *testing.T, err error, actual, expected string) {
}
func TestParseJson(t *testing.T) {
vm := jsonnet.Make()
defer vm.Destroy()
vm := jsonnet.MakeVM()
RegisterNativeFuncs(vm, NewIdentityResolver())
_, err := vm.EvaluateSnippet("failtest", `std.native("parseJson")("barf{")`)
......@@ -41,17 +40,16 @@ func TestParseJson(t *testing.T) {
}
x, err := vm.EvaluateSnippet("test", `std.native("parseJson")("null")`)
check(t, err, x, "null\n")
check(t, err, x, "null")
x, err = vm.EvaluateSnippet("test", `
local a = std.native("parseJson")('{"foo": 3, "bar": 4}');
a.foo + a.bar`)
check(t, err, x, "7\n")
check(t, err, x, "7")
}
func TestParseYaml(t *testing.T) {
vm := jsonnet.Make()
defer vm.Destroy()
vm := jsonnet.MakeVM()
RegisterNativeFuncs(vm, NewIdentityResolver())
_, err := vm.EvaluateSnippet("failtest", `std.native("parseYaml")("[barf")`)
......@@ -60,22 +58,21 @@ func TestParseYaml(t *testing.T) {
}
x, err := vm.EvaluateSnippet("test", `std.native("parseYaml")("")`)
check(t, err, x, "[ ]\n")
check(t, err, x, "[ ]")
x, err = vm.EvaluateSnippet("test", `
local a = std.native("parseYaml")("foo:\n- 3\n- 4\n")[0];
a.foo[0] + a.foo[1]`)
check(t, err, x, "7\n")
check(t, err, x, "7")
x, err = vm.EvaluateSnippet("test", `
local a = std.native("parseYaml")("---\nhello\n---\nworld");
a[0] + a[1]`)
check(t, err, x, "\"helloworld\"\n")
check(t, err, x, "\"helloworld\"")
}
func TestRegexMatch(t *testing.T) {
vm := jsonnet.Make()
defer vm.Destroy()
vm := jsonnet.MakeVM()
RegisterNativeFuncs(vm, NewIdentityResolver())
_, err := vm.EvaluateSnippet("failtest", `std.native("regexMatch")("[f", "foo")`)
......@@ -84,15 +81,14 @@ func TestRegexMatch(t *testing.T) {
}
x, err := vm.EvaluateSnippet("test", `std.native("regexMatch")("foo.*", "seafood")`)
check(t, err, x, "true\n")
check(t, err, x, "true")
x, err = vm.EvaluateSnippet("test", `std.native("regexMatch")("bar.*", "seafood")`)
check(t, err, x, "false\n")
check(t, err, x, "false")
}
func TestRegexSubst(t *testing.T) {
vm := jsonnet.Make()
defer vm.Destroy()
vm := jsonnet.MakeVM()
RegisterNativeFuncs(vm, NewIdentityResolver())
_, err := vm.EvaluateSnippet("failtest", `std.native("regexSubst")("[f",s "foo", "bar")`)
......@@ -101,8 +97,8 @@ func TestRegexSubst(t *testing.T) {
}
x, err := vm.EvaluateSnippet("test", `std.native("regexSubst")("a(x*)b", "-ab-axxb-", "T")`)
check(t, err, x, "\"-T-T-\"\n")
check(t, err, x, "\"-T-T-\"")
x, err = vm.EvaluateSnippet("test", `std.native("regexSubst")("a(x*)b", "-ab-axxb-", "${1}W")`)
check(t, err, x, "\"-W-xxW-\"\n")
check(t, err, x, "\"-W-xxW-\"")
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment