diff --git a/pkg/tanka/evaluators.go b/pkg/tanka/evaluators.go index e8ed91980..5aa1443b5 100644 --- a/pkg/tanka/evaluators.go +++ b/pkg/tanka/evaluators.go @@ -20,6 +20,11 @@ func evalJsonnet(path string, impl types.JsonnetImplementation, opts jsonnet.Opt // evaluate Jsonnet if opts.EvalScript != "" { + // Determine if the entrypoint is a function. + isFunction, err := jsonnet.Evaluate(path, impl, fmt.Sprintf("std.isFunction(import '%s')", entrypoint), opts) + if err != nil { + return "", fmt.Errorf("evaluating jsonnet in path '%s': %w", path, err) + } var tla []string for k := range opts.TLACode { tla = append(tla, k+"="+k) @@ -29,7 +34,7 @@ func evalJsonnet(path string, impl types.JsonnetImplementation, opts jsonnet.Opt %s `, entrypoint, opts.EvalScript) - if len(tla) != 0 { + if isFunction == "true\n" { tlaJoin := strings.Join(tla, ", ") evalScript = fmt.Sprintf(` function(%s) diff --git a/pkg/tanka/evaluators_test.go b/pkg/tanka/evaluators_test.go index f6ce72417..0802a2152 100644 --- a/pkg/tanka/evaluators_test.go +++ b/pkg/tanka/evaluators_test.go @@ -52,3 +52,61 @@ func TestEvalJsonnetWithExpression(t *testing.T) { }) } } + +// An EvalScript with a top-level function containing only optional arguments +// should be evaluated as a function even if no TLAs are provided. +func TestEvalWithOptionalTlas(t *testing.T) { + opts := jsonnet.Opts{ + EvalScript: "main.metadata.name", + } + json, err := evalJsonnet("testdata/cases/with-optional-tlas/main.jsonnet", jsonnetImpl, opts) + assert.NoError(t, err) + assert.Equal(t, `"bar-baz"`, strings.TrimSpace(json)) +} + +// An EvalScript with a top-level function containing should allow passing only +// a subset of the TLAs. +func TestEvalWithOptionalTlasSpecifiedArg2(t *testing.T) { + opts := jsonnet.Opts{ + EvalScript: "main.metadata.name", + TLACode: jsonnet.InjectedCode{"baz": "'changed'"}, + } + json, err := evalJsonnet("testdata/cases/with-optional-tlas/main.jsonnet", jsonnetImpl, opts) + assert.NoError(t, err) + assert.Equal(t, `"bar-changed"`, strings.TrimSpace(json)) +} + +// An EvalScript with a top-level function having no arguments should be +// evaluated as a function even if no TLAs are provided. +func TestEvalFunctionWithNoTlas(t *testing.T) { + opts := jsonnet.Opts{ + EvalScript: "main.metadata.name", + } + json, err := evalJsonnet("testdata/cases/function-with-zero-params/main.jsonnet", jsonnetImpl, opts) + assert.NoError(t, err) + assert.Equal(t, `"inline"`, strings.TrimSpace(json)) +} + +// An EvalScript with a top-level function should return an understandable +// error message if an incorrect TLA is provided. +func TestInvalidTlaArg(t *testing.T) { + opts := jsonnet.Opts{ + EvalScript: "main", + TLACode: jsonnet.InjectedCode{"foo": "'bar'"}, + } + json, err := evalJsonnet("testdata/cases/function-with-zero-params/main.jsonnet", jsonnetImpl, opts) + assert.Contains(t, err.Error(), "function has no parameter foo") + assert.Equal(t, "", json) +} + +// Providing a TLA to an EvalScript with a non-function top level mainfile +// should not return an error. +func TestTlaWithNonFunction(t *testing.T) { + opts := jsonnet.Opts{ + EvalScript: "main", + TLACode: jsonnet.InjectedCode{"foo": "'bar'"}, + } + json, err := evalJsonnet("testdata/cases/withenv/main.jsonnet", jsonnetImpl, opts) + assert.NoError(t, err) + assert.NotEmpty(t, json) +} diff --git a/pkg/tanka/testdata/cases/function-with-zero-params/main.jsonnet b/pkg/tanka/testdata/cases/function-with-zero-params/main.jsonnet new file mode 100644 index 000000000..6227cd3d2 --- /dev/null +++ b/pkg/tanka/testdata/cases/function-with-zero-params/main.jsonnet @@ -0,0 +1,16 @@ +function() { + apiVersion: 'tanka.dev/v1alpha1', + kind: 'Environment', + metadata: { + name: 'inline', + }, + spec: { + apiServer: 'https://localhost', + namespace: 'inline', + }, + data: { + apiVersion: 'v1', + kind: 'ConfigMap', + metadata: { name: 'config' }, + }, +} diff --git a/pkg/tanka/testdata/cases/with-optional-tlas/main.jsonnet b/pkg/tanka/testdata/cases/with-optional-tlas/main.jsonnet new file mode 100644 index 000000000..5294d75da --- /dev/null +++ b/pkg/tanka/testdata/cases/with-optional-tlas/main.jsonnet @@ -0,0 +1,16 @@ +function(bar='bar', baz='baz') { + apiVersion: 'tanka.dev/v1alpha1', + kind: 'Environment', + metadata: { + name: bar + '-' + baz, + }, + spec: { + apiServer: 'https://localhost', + namespace: 'inline', + }, + data: { + apiVersion: 'v1', + kind: 'ConfigMap', + metadata: { name: 'config' }, + }, +}