Skip to content

Commit

Permalink
allow inline Environment
Browse files Browse the repository at this point in the history
  • Loading branch information
Duologic committed Oct 19, 2020
1 parent c56435b commit 0124866
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 27 deletions.
9 changes: 5 additions & 4 deletions pkg/spec/v1alpha1/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ func New() *Config {
// Config holds the configuration variables for config version v1alpha1
// ApiVersion and Kind are currently unused, this may change in the future.
type Config struct {
APIVersion string `json:"apiVersion"`
Kind string `json:"kind"`
Metadata Metadata `json:"metadata"`
Spec Spec `json:"spec"`
APIVersion string `json:"apiVersion"`
Kind string `json:"kind"`
Metadata Metadata `json:"metadata"`
Spec Spec `json:"spec"`
Data interface{} `json:"data"`
}

// Metadata is meant for humans and not parsed
Expand Down
87 changes: 65 additions & 22 deletions pkg/tanka/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,20 @@ func (p *loaded) connect() (*kubernetes.Kubernetes, error) {

// load runs all processing stages described at the Processed type
func load(path string, opts Opts) (*loaded, error) {
raw, env, err := eval(path, opts.JsonnetOpts)
_, env, err := eval(path, opts.JsonnetOpts)
if err != nil {
return nil, err
}

if env == nil {
return nil, fmt.Errorf("no Tanka environment found")
}

if err := checkVersion(env.Spec.ExpectVersions.Tanka); err != nil {
return nil, err
}

rec, err := process.Process(raw, *env, opts.Filters)
rec, err := process.Process(env.Data, *env, opts.Filters)
if err != nil {
return nil, err
}
Expand All @@ -87,12 +91,7 @@ func load(path string, opts Opts) (*loaded, error) {
// eval runs all processing stages describe at the Processed type apart from
// post-processing, thus returning the raw Jsonnet result.
func eval(path string, opts jsonnet.Opts) (raw interface{}, env *v1alpha1.Config, err error) {
env, err = parseSpec(path)
if err != nil {
return nil, nil, err
}

raw, err = evalJsonnet(path, env, opts)
raw, env, err = evalJsonnet(path, opts)
if err != nil {
return nil, nil, errors.Wrap(err, "evaluating jsonnet")
}
Expand All @@ -119,7 +118,7 @@ func parseSpec(path string) (*v1alpha1.Config, error) {
log.Println(err)
// spec.json missing. we can still work with the default value
case spec.ErrNoSpec:
return config, nil
return config, err
// some other error
default:
return nil, errors.Wrap(err, "reading spec.json")
Expand All @@ -130,39 +129,83 @@ func parseSpec(path string) (*v1alpha1.Config, error) {
}

// evalJsonnet evaluates the jsonnet environment at the given path
func evalJsonnet(path string, env *v1alpha1.Config, opts jsonnet.Opts) (interface{}, error) {
// make env spec accessible from Jsonnet
jsonEnv, err := json.Marshal(env)
func evalJsonnet(path string, opts jsonnet.Opts) (interface{}, *v1alpha1.Config, error) {
var hasSpec bool
specEnv, err := parseSpec(path)
if err != nil {
return nil, errors.Wrap(err, "marshalling environment config")
switch err.(type) {
case spec.ErrNoSpec:
hasSpec = false
default:
return nil, nil, errors.Wrap(err, "reading spec.json")
}
} else {
hasSpec = true

// original behavior, if env has spec.json
// then make env spec accessible through extCode
jsonEnv, err := json.Marshal(specEnv)
if err != nil {
return nil, nil, errors.Wrap(err, "marshalling environment config")
}
opts.ExtCode.Set(spec.APIGroup+"/environment", string(jsonEnv))
}
opts.ExtCode.Set(spec.APIGroup+"/environment", string(jsonEnv))

// evaluate Jsonnet
var raw string
entrypoint, err := jpath.Entrypoint(path)
if err != nil {
return nil, err
return nil, nil, err
}

// evaluate Jsonnet
var raw string
if opts.EvalPattern != "" {
evalScript := fmt.Sprintf("(import '%s').%s", entrypoint, opts.EvalPattern)
raw, err = jsonnet.Evaluate(entrypoint, evalScript, opts)
if err != nil {
return nil, err
return nil, nil, err
}
} else {
raw, err = jsonnet.EvaluateFile(entrypoint, opts)
if err != nil {
return nil, err
return nil, nil, err
}
}
// parse result

var data interface{}
if err := json.Unmarshal([]byte(raw), &data); err != nil {
return nil, err
return nil, nil, err
}

if opts.EvalPattern != "" {
// EvalPattern has no affinity with an environment, behave as jsonnet interpreter
return data, nil, err
}

var env *v1alpha1.Config
switch data.(type) {
case []interface{}:
env = &v1alpha1.Config{}
// data is array, do not try to unmarshal,
// multiple envs currently unsupported
default:
if err := json.Unmarshal([]byte(raw), &env); err != nil {
return nil, nil, err
}
}

// env is not a v1alpha1.Config, fallback to original behavior
if env.Kind != "Environment" {
if hasSpec {
specEnv.Data = data
// return env from spec.json
return data, specEnv, nil
} else {
// No spec.json found, behave as jsonnet interpreter
return data, nil, nil
}
}
return data, nil
// return env AS IS
return *env, env, nil
}

func checkVersion(constraint string) error {
Expand Down
79 changes: 78 additions & 1 deletion pkg/tanka/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ func TestEvalJsonnet(t *testing.T) {
cases := []struct {
baseDir string
expected interface{}
env *v1alpha1.Config
}{
{
baseDir: "./testdata/cases/array/",
Expand All @@ -25,18 +26,94 @@ func TestEvalJsonnet(t *testing.T) {
map[string]interface{}{"testCase": "nestedArray[1][1]"},
},
},
env: nil,
},
{
baseDir: "./testdata/cases/object/",
expected: map[string]interface{}{
"testCase": "object",
},
env: nil,
},
{
baseDir: "./testdata/cases/withspecjson/",
expected: map[string]interface{}{
"testCase": "object",
},
env: &v1alpha1.Config{
APIVersion: v1alpha1.New().APIVersion,
Kind: v1alpha1.New().Kind,
Metadata: v1alpha1.Metadata{
Name: "cases/withspecjson",
Labels: v1alpha1.New().Metadata.Labels,
},
Spec: v1alpha1.Spec{
APIServer: "https://localhost",
Namespace: "withspec",
},
Data: map[string]interface{}{
"testCase": "object",
},
},
},
{
baseDir: "./testdata/cases/withspecjson/main.jsonnet",
expected: map[string]interface{}{
"testCase": "object",
},
env: &v1alpha1.Config{
APIVersion: v1alpha1.New().APIVersion,
Kind: v1alpha1.New().Kind,
Metadata: v1alpha1.Metadata{
Name: "cases/withspecjson",
Labels: v1alpha1.New().Metadata.Labels,
},
Spec: v1alpha1.Spec{
APIServer: "https://localhost",
Namespace: "withspec",
},
Data: map[string]interface{}{
"testCase": "object",
},
},
},
{
baseDir: "./testdata/cases/withenv/main.jsonnet",
expected: v1alpha1.Config{
APIVersion: v1alpha1.New().APIVersion,
Kind: v1alpha1.New().Kind,
Metadata: v1alpha1.Metadata{
Name: "withenv",
},
Spec: v1alpha1.Spec{
APIServer: "https://localhost",
Namespace: "withenv",
},
Data: map[string]interface{}{
"testCase": "object",
},
},
env: &v1alpha1.Config{
APIVersion: v1alpha1.New().APIVersion,
Kind: v1alpha1.New().Kind,
Metadata: v1alpha1.Metadata{
Name: "withenv",
},
Spec: v1alpha1.Spec{
APIServer: "https://localhost",
Namespace: "withenv",
},
Data: map[string]interface{}{
"testCase": "object",
},
},
},
}

for _, test := range cases {
data, e := evalJsonnet(test.baseDir, v1alpha1.New(), jsonnet.Opts{})
data, env, e := evalJsonnet(test.baseDir, jsonnet.Opts{})
assert.NoError(t, e)
assert.Equal(t, test.expected, data)
assert.Equal(t, test.env, env)
}
}
14 changes: 14 additions & 0 deletions pkg/tanka/testdata/cases/withenv/main.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
apiVersion: 'tanka.dev/v1alpha1',
kind: 'Environment',
metadata: {
name: 'withenv',
},
spec: {
apiServer: 'https://localhost',
namespace: 'withenv',
},
data: {
testCase: 'object',
},
}
3 changes: 3 additions & 0 deletions pkg/tanka/testdata/cases/withspecjson/main.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
testCase: 'object',
}
11 changes: 11 additions & 0 deletions pkg/tanka/testdata/cases/withspecjson/spec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"apiVersion": "tanka.dev/v1alpha1",
"kind": "Environment",
"metadata": {
"name": "withspec"
},
"spec": {
"apiServer": "https://localhost",
"namespace": "withspec"
}
}

0 comments on commit 0124866

Please sign in to comment.