Skip to content

Commit 993c559

Browse files
committed
allow inline Environment
1 parent 1220d14 commit 993c559

File tree

6 files changed

+176
-27
lines changed

6 files changed

+176
-27
lines changed

pkg/spec/v1alpha1/config.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ func New() *Config {
2121
// Config holds the configuration variables for config version v1alpha1
2222
// ApiVersion and Kind are currently unused, this may change in the future.
2323
type Config struct {
24-
APIVersion string `json:"apiVersion"`
25-
Kind string `json:"kind"`
26-
Metadata Metadata `json:"metadata"`
27-
Spec Spec `json:"spec"`
24+
APIVersion string `json:"apiVersion"`
25+
Kind string `json:"kind"`
26+
Metadata Metadata `json:"metadata"`
27+
Spec Spec `json:"spec"`
28+
Data interface{} `json:"data"`
2829
}
2930

3031
// Metadata is meant for humans and not parsed

pkg/tanka/parse.go

+65-22
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,20 @@ func (p *loaded) connect() (*kubernetes.Kubernetes, error) {
6464

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

72+
if env == nil {
73+
return nil, fmt.Errorf("no Tanka environment found")
74+
}
75+
7276
if err := checkVersion(env.Spec.ExpectVersions.Tanka); err != nil {
7377
return nil, err
7478
}
7579

76-
rec, err := process.Process(raw, *env, opts.Filters)
80+
rec, err := process.Process(env.Data, *env, opts.Filters)
7781
if err != nil {
7882
return nil, err
7983
}
@@ -87,12 +91,7 @@ func load(path string, opts Opts) (*loaded, error) {
8791
// eval runs all processing stages describe at the Processed type apart from
8892
// post-processing, thus returning the raw Jsonnet result.
8993
func eval(path string, opts jsonnet.Opts) (raw interface{}, env *v1alpha1.Config, err error) {
90-
env, err = parseSpec(path)
91-
if err != nil {
92-
return nil, nil, err
93-
}
94-
95-
raw, err = evalJsonnet(path, env, opts)
94+
raw, env, err = evalJsonnet(path, opts)
9695
if err != nil {
9796
return nil, nil, errors.Wrap(err, "evaluating jsonnet")
9897
}
@@ -119,7 +118,7 @@ func parseSpec(path string) (*v1alpha1.Config, error) {
119118
log.Println(err)
120119
// spec.json missing. we can still work with the default value
121120
case spec.ErrNoSpec:
122-
return config, nil
121+
return config, err
123122
// some other error
124123
default:
125124
return nil, errors.Wrap(err, "reading spec.json")
@@ -130,39 +129,83 @@ func parseSpec(path string) (*v1alpha1.Config, error) {
130129
}
131130

132131
// evalJsonnet evaluates the jsonnet environment at the given path
133-
func evalJsonnet(path string, env *v1alpha1.Config, opts jsonnet.Opts) (interface{}, error) {
134-
// make env spec accessible from Jsonnet
135-
jsonEnv, err := json.Marshal(env)
132+
func evalJsonnet(path string, opts jsonnet.Opts) (interface{}, *v1alpha1.Config, error) {
133+
var hasSpec bool
134+
specEnv, err := parseSpec(path)
136135
if err != nil {
137-
return nil, errors.Wrap(err, "marshalling environment config")
136+
switch err.(type) {
137+
case spec.ErrNoSpec:
138+
hasSpec = false
139+
default:
140+
return nil, nil, errors.Wrap(err, "reading spec.json")
141+
}
142+
} else {
143+
hasSpec = true
144+
145+
// original behavior, if env has spec.json
146+
// then make env spec accessible through extCode
147+
jsonEnv, err := json.Marshal(specEnv)
148+
if err != nil {
149+
return nil, nil, errors.Wrap(err, "marshalling environment config")
150+
}
151+
opts.ExtCode.Set(spec.APIGroup+"/environment", string(jsonEnv))
138152
}
139-
opts.ExtCode.Set(spec.APIGroup+"/environment", string(jsonEnv))
140153

141-
// evaluate Jsonnet
142-
var raw string
143154
entrypoint, err := jpath.Entrypoint(path)
144155
if err != nil {
145-
return nil, err
156+
return nil, nil, err
146157
}
147158

159+
// evaluate Jsonnet
160+
var raw string
148161
if opts.EvalPattern != "" {
149162
evalScript := fmt.Sprintf("(import '%s').%s", entrypoint, opts.EvalPattern)
150163
raw, err = jsonnet.Evaluate(entrypoint, evalScript, opts)
151164
if err != nil {
152-
return nil, err
165+
return nil, nil, err
153166
}
154167
} else {
155168
raw, err = jsonnet.EvaluateFile(entrypoint, opts)
156169
if err != nil {
157-
return nil, err
170+
return nil, nil, err
158171
}
159172
}
160-
// parse result
173+
161174
var data interface{}
162175
if err := json.Unmarshal([]byte(raw), &data); err != nil {
163-
return nil, err
176+
return nil, nil, err
177+
}
178+
179+
if opts.EvalPattern != "" {
180+
// EvalPattern has no affinity with an environment, behave as jsonnet interpreter
181+
return data, nil, err
182+
}
183+
184+
var env *v1alpha1.Config
185+
switch data.(type) {
186+
case []interface{}:
187+
env = &v1alpha1.Config{}
188+
// data is array, do not try to unmarshal,
189+
// multiple envs currently unsupported
190+
default:
191+
if err := json.Unmarshal([]byte(raw), &env); err != nil {
192+
return nil, nil, err
193+
}
194+
}
195+
196+
// env is not a v1alpha1.Config, fallback to original behavior
197+
if env.Kind != "Environment" {
198+
if hasSpec {
199+
specEnv.Data = data
200+
// return env from spec.json
201+
return data, specEnv, nil
202+
} else {
203+
// No spec.json found, behave as jsonnet interpreter
204+
return data, nil, nil
205+
}
164206
}
165-
return data, nil
207+
// return env AS IS
208+
return *env, env, nil
166209
}
167210

168211
func checkVersion(constraint string) error {

pkg/tanka/parse_test.go

+78-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ func TestEvalJsonnet(t *testing.T) {
1212
cases := []struct {
1313
baseDir string
1414
expected interface{}
15+
env *v1alpha1.Config
1516
}{
1617
{
1718
baseDir: "./testdata/cases/array/",
@@ -25,18 +26,94 @@ func TestEvalJsonnet(t *testing.T) {
2526
map[string]interface{}{"testCase": "nestedArray[1][1]"},
2627
},
2728
},
29+
env: nil,
2830
},
2931
{
3032
baseDir: "./testdata/cases/object/",
3133
expected: map[string]interface{}{
3234
"testCase": "object",
3335
},
36+
env: nil,
37+
},
38+
{
39+
baseDir: "./testdata/cases/withspecjson/",
40+
expected: map[string]interface{}{
41+
"testCase": "object",
42+
},
43+
env: &v1alpha1.Config{
44+
APIVersion: v1alpha1.New().APIVersion,
45+
Kind: v1alpha1.New().Kind,
46+
Metadata: v1alpha1.Metadata{
47+
Name: "cases/withspecjson",
48+
Labels: v1alpha1.New().Metadata.Labels,
49+
},
50+
Spec: v1alpha1.Spec{
51+
APIServer: "https://localhost",
52+
Namespace: "withspec",
53+
},
54+
Data: map[string]interface{}{
55+
"testCase": "object",
56+
},
57+
},
58+
},
59+
{
60+
baseDir: "./testdata/cases/withspecjson/main.jsonnet",
61+
expected: map[string]interface{}{
62+
"testCase": "object",
63+
},
64+
env: &v1alpha1.Config{
65+
APIVersion: v1alpha1.New().APIVersion,
66+
Kind: v1alpha1.New().Kind,
67+
Metadata: v1alpha1.Metadata{
68+
Name: "cases/withspecjson",
69+
Labels: v1alpha1.New().Metadata.Labels,
70+
},
71+
Spec: v1alpha1.Spec{
72+
APIServer: "https://localhost",
73+
Namespace: "withspec",
74+
},
75+
Data: map[string]interface{}{
76+
"testCase": "object",
77+
},
78+
},
79+
},
80+
{
81+
baseDir: "./testdata/cases/withenv/main.jsonnet",
82+
expected: v1alpha1.Config{
83+
APIVersion: v1alpha1.New().APIVersion,
84+
Kind: v1alpha1.New().Kind,
85+
Metadata: v1alpha1.Metadata{
86+
Name: "withenv",
87+
},
88+
Spec: v1alpha1.Spec{
89+
APIServer: "https://localhost",
90+
Namespace: "withenv",
91+
},
92+
Data: map[string]interface{}{
93+
"testCase": "object",
94+
},
95+
},
96+
env: &v1alpha1.Config{
97+
APIVersion: v1alpha1.New().APIVersion,
98+
Kind: v1alpha1.New().Kind,
99+
Metadata: v1alpha1.Metadata{
100+
Name: "withenv",
101+
},
102+
Spec: v1alpha1.Spec{
103+
APIServer: "https://localhost",
104+
Namespace: "withenv",
105+
},
106+
Data: map[string]interface{}{
107+
"testCase": "object",
108+
},
109+
},
34110
},
35111
}
36112

37113
for _, test := range cases {
38-
data, e := evalJsonnet(test.baseDir, v1alpha1.New(), jsonnet.Opts{})
114+
data, env, e := evalJsonnet(test.baseDir, jsonnet.Opts{})
39115
assert.NoError(t, e)
40116
assert.Equal(t, test.expected, data)
117+
assert.Equal(t, test.env, env)
41118
}
42119
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
apiVersion: 'tanka.dev/v1alpha1',
3+
kind: 'Environment',
4+
metadata: {
5+
name: 'withenv',
6+
},
7+
spec: {
8+
apiServer: 'https://localhost',
9+
namespace: 'withenv',
10+
},
11+
data: {
12+
testCase: 'object',
13+
},
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
testCase: 'object',
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"apiVersion": "tanka.dev/v1alpha1",
3+
"kind": "Environment",
4+
"metadata": {
5+
"name": "withspec"
6+
},
7+
"spec": {
8+
"apiServer": "https://localhost",
9+
"namespace": "withspec"
10+
}
11+
}

0 commit comments

Comments
 (0)