@@ -89,6 +89,36 @@ func load(path string, opts Opts) (*loaded, error) {
89
89
}, nil
90
90
}
91
91
92
+ // eval evaluates the jsonnet environment at the given path
93
+ func eval (path string , opts jsonnet.Opts ) (interface {}, * v1alpha1.Config , error ) {
94
+ return parseEnv (
95
+ path ,
96
+ opts ,
97
+ func (path string , opts jsonnet.Opts ) (string , error ) {
98
+ entrypoint , err := jpath .Entrypoint (path )
99
+ if err != nil {
100
+ return "" , err
101
+ }
102
+
103
+ // evaluate Jsonnet
104
+ var raw string
105
+ if opts .EvalPattern != "" {
106
+ evalScript := fmt .Sprintf ("(import '%s').%s" , entrypoint , opts .EvalPattern )
107
+ raw , err = jsonnet .Evaluate (entrypoint , evalScript , opts )
108
+ if err != nil {
109
+ return "" , errors .Wrap (err , "evaluating jsonnet" )
110
+ }
111
+ } else {
112
+ raw , err = jsonnet .EvaluateFile (entrypoint , opts )
113
+ if err != nil {
114
+ return "" , errors .Wrap (err , "evaluating jsonnet" )
115
+ }
116
+ }
117
+ return raw , nil
118
+ },
119
+ )
120
+ }
121
+
92
122
// parseSpec parses the `spec.json` of the environment and returns a
93
123
// *kubernetes.Kubernetes from it
94
124
func parseSpec (path string ) (* v1alpha1.Config , error ) {
@@ -118,20 +148,19 @@ func parseSpec(path string) (*v1alpha1.Config, error) {
118
148
return config , nil
119
149
}
120
150
121
- // eval evaluates the jsonnet environment at the given path
122
- func eval (path string , opts jsonnet.Opts ) (interface {}, * v1alpha1.Config , error ) {
123
- var hasSpec bool
151
+ type evaluateFunc func (path string , opts jsonnet.Opts ) (string , error )
152
+
153
+ // parseEnv finds the Environment object at the given path
154
+ func parseEnv (path string , opts jsonnet.Opts , evalFn evaluateFunc ) (interface {}, * v1alpha1.Config , error ) {
124
155
specEnv , err := parseSpec (path )
125
156
if err != nil {
126
157
switch err .(type ) {
127
158
case spec.ErrNoSpec :
128
- hasSpec = false
159
+ specEnv = nil
129
160
default :
130
161
return nil , nil , errors .Wrap (err , "reading spec.json" )
131
162
}
132
163
} else {
133
- hasSpec = true
134
-
135
164
// original behavior, if env has spec.json
136
165
// then make env spec accessible through extCode
137
166
jsonEnv , err := json .Marshal (specEnv )
@@ -141,26 +170,11 @@ func eval(path string, opts jsonnet.Opts) (interface{}, *v1alpha1.Config, error)
141
170
opts .ExtCode .Set (spec .APIGroup + "/environment" , string (jsonEnv ))
142
171
}
143
172
144
- entrypoint , err := jpath . Entrypoint (path )
173
+ raw , err := evalFn (path , opts )
145
174
if err != nil {
146
175
return nil , nil , err
147
176
}
148
177
149
- // evaluate Jsonnet
150
- var raw string
151
- if opts .EvalPattern != "" {
152
- evalScript := fmt .Sprintf ("(import '%s').%s" , entrypoint , opts .EvalPattern )
153
- raw , err = jsonnet .Evaluate (entrypoint , evalScript , opts )
154
- if err != nil {
155
- return nil , nil , errors .Wrap (err , "evaluating jsonnet" )
156
- }
157
- } else {
158
- raw , err = jsonnet .EvaluateFile (entrypoint , opts )
159
- if err != nil {
160
- return nil , nil , errors .Wrap (err , "evaluating jsonnet" )
161
- }
162
- }
163
-
164
178
var data interface {}
165
179
if err := json .Unmarshal ([]byte (raw ), & data ); err != nil {
166
180
return nil , nil , errors .Wrap (err , "unmarshalling data" )
@@ -173,7 +187,7 @@ func eval(path string, opts jsonnet.Opts) (interface{}, *v1alpha1.Config, error)
173
187
174
188
extract , err := extractEnvironments (data )
175
189
if _ , ok := err .(process.ErrorPrimitiveReached ); ok {
176
- if ! hasSpec {
190
+ if specEnv == nil {
177
191
// if no environments or spec found, behave as jsonnet interpreter
178
192
return data , nil , err
179
193
}
@@ -184,25 +198,24 @@ func eval(path string, opts jsonnet.Opts) (interface{}, *v1alpha1.Config, error)
184
198
var env * v1alpha1.Config
185
199
186
200
if len (extract ) > 1 {
187
- return nil , nil , fmt . Errorf ( "more than 1 environments found" )
201
+ return data , nil , ErrMultipleEnvs { path }
188
202
} else if len (extract ) == 1 {
189
- data , err := json .Marshal (extract [0 ])
203
+ marshalled , err := json .Marshal (extract [0 ])
190
204
if err != nil {
191
205
return nil , nil , err
192
206
}
193
- env , err = spec .Parse (data )
207
+ env , err = spec .Parse (marshalled )
194
208
if err != nil {
195
209
return nil , nil , err
196
210
}
197
- } else if hasSpec {
211
+ return data , env , nil
212
+ } else if specEnv != nil {
198
213
// if no environments found, fallback to original behavior
199
214
specEnv .Data = data
200
215
return data , specEnv , nil
201
- } else {
202
- // if no environments or spec found, behave as jsonnet interpreter
203
- return data , nil , fmt .Errorf ("no environments found" )
204
216
}
205
- return data , env , nil
217
+ // if no environments or spec found, behave as jsonnet interpreter
218
+ return data , nil , ErrNoEnv {path }
206
219
}
207
220
208
221
func checkVersion (constraint string ) error {
@@ -251,3 +264,57 @@ func extractEnvironments(data interface{}) (manifest.List, error) {
251
264
// Extract only object of Kind: Environment
252
265
return process .Filter (out , process .MustStrExps ("Environment/.*" )), nil
253
266
}
267
+
268
+ // EvalEnvs finds the Environment object (without its .data object) at the given path
269
+ // intended for use by the `tk env` command
270
+ func EvalEnvs (path string , opts jsonnet.Opts ) (* v1alpha1.Config , error ) {
271
+ _ , env , err := parseEnv (
272
+ path ,
273
+ opts ,
274
+ func (path string , opts jsonnet.Opts ) (string , error ) {
275
+ entrypoint , err := jpath .Entrypoint (path )
276
+ if err != nil {
277
+ return "" , err
278
+ }
279
+
280
+ // Snippet to find all Environment objects and remove the .data object for faster evaluation
281
+ noData := `
282
+ local noDataEnv(object) =
283
+ if std.isObject(object)
284
+ then
285
+ if std.objectHas(object, 'apiVersion')
286
+ && std.objectHas(object, 'kind')
287
+ then
288
+ if object.kind == 'Environment'
289
+ then object { data:: {} }
290
+ else {}
291
+ else
292
+ std.mapWithKey(
293
+ function(key, obj)
294
+ noDataEnv(obj),
295
+ object
296
+ )
297
+ else if std.isArray(object)
298
+ then
299
+ std.map(
300
+ function(obj)
301
+ noDataEnv(obj),
302
+ object
303
+ )
304
+ else {};
305
+
306
+ noDataEnv(import '%s')
307
+ `
308
+
309
+ // evaluate Jsonnet with noData snippet
310
+ var raw string
311
+ evalScript := fmt .Sprintf (noData , entrypoint )
312
+ raw , err = jsonnet .Evaluate (entrypoint , evalScript , opts )
313
+ if err != nil {
314
+ return "" , errors .Wrap (err , "evaluating jsonnet" )
315
+ }
316
+ return raw , nil
317
+ },
318
+ )
319
+ return env , err
320
+ }
0 commit comments