@@ -21,13 +21,29 @@ import (
21
21
"errors"
22
22
"fmt"
23
23
"regexp"
24
+ "slices"
24
25
"strings"
26
+ "time"
25
27
28
+ "github.com/tektoncd/pipeline/internal/artifactref"
26
29
"github.com/tektoncd/pipeline/pkg/apis/config"
30
+ "github.com/tektoncd/pipeline/pkg/apis/pipeline"
31
+ "github.com/tektoncd/pipeline/pkg/internal/resultref"
32
+ "github.com/tektoncd/pipeline/pkg/substitution"
33
+ "k8s.io/apimachinery/pkg/util/sets"
27
34
"k8s.io/apimachinery/pkg/util/validation"
28
35
"knative.dev/pkg/apis"
29
36
)
30
37
38
+ // Validate ensures that a supplied Ref field is populated
39
+ // correctly. No errors are returned for a nil Ref.
40
+ func (ref * Ref ) Validate (ctx context.Context ) (errs * apis.FieldError ) {
41
+ if ref == nil {
42
+ return errs
43
+ }
44
+ return validateRef (ctx , ref .Name , ref .Resolver , ref .Params )
45
+ }
46
+
31
47
func validateRef (ctx context.Context , refName string , refResolver ResolverName , refParams Params ) (errs * apis.FieldError ) {
32
48
switch {
33
49
case refResolver != "" || refParams != nil :
@@ -80,15 +96,6 @@ func validateRef(ctx context.Context, refName string, refResolver ResolverName,
80
96
return errs
81
97
}
82
98
83
- // Validate ensures that a supplied Ref field is populated
84
- // correctly. No errors are returned for a nil Ref.
85
- func (ref * Ref ) Validate (ctx context.Context ) (errs * apis.FieldError ) {
86
- if ref == nil {
87
- return errs
88
- }
89
- return validateRef (ctx , ref .Name , ref .Resolver , ref .Params )
90
- }
91
-
92
99
// RefNameLikeUrl checks if the name is url parsable and returns an error if it isn't.
93
100
func RefNameLikeUrl (name string ) error {
94
101
schemeRegex := regexp .MustCompile (`[\w-]+:\/\/*` )
@@ -97,3 +104,323 @@ func RefNameLikeUrl(name string) error {
97
104
}
98
105
return nil
99
106
}
107
+
108
+ func validateSidecars (sidecars []Sidecar ) (errs * apis.FieldError ) {
109
+ for _ , sc := range sidecars {
110
+ errs = errs .Also (validateSidecarName (sc ))
111
+
112
+ if sc .Image == "" {
113
+ errs = errs .Also (apis .ErrMissingField ("image" ))
114
+ }
115
+
116
+ if sc .Script != "" {
117
+ if len (sc .Command ) > 0 {
118
+ errs = errs .Also (& apis.FieldError {
119
+ Message : "script cannot be used with command" ,
120
+ Paths : []string {"script" },
121
+ })
122
+ }
123
+ }
124
+ }
125
+ return errs
126
+ }
127
+
128
+ func validateSidecarName (sc Sidecar ) (errs * apis.FieldError ) {
129
+ if sc .Name == pipeline .ReservedResultsSidecarName {
130
+ errs = errs .Also (& apis.FieldError {
131
+ Message : fmt .Sprintf ("Invalid: cannot use reserved sidecar name %v " , sc .Name ),
132
+ Paths : []string {"name" },
133
+ })
134
+ }
135
+ return errs
136
+ }
137
+
138
+ func validateSteps (ctx context.Context , steps []Step ) (errs * apis.FieldError ) {
139
+ // Task must not have duplicate step names.
140
+ names := sets .NewString ()
141
+ for idx , s := range steps {
142
+ errs = errs .Also (validateStep (ctx , s , names ).ViaIndex (idx ))
143
+ if s .Results != nil {
144
+ errs = errs .Also (ValidateStepResultsVariables (ctx , s .Results , s .Script ).ViaIndex (idx ))
145
+ errs = errs .Also (ValidateStepResults (ctx , s .Results ).ViaIndex (idx ).ViaField ("results" ))
146
+ }
147
+ if len (s .When ) > 0 {
148
+ errs = errs .Also (s .When .validate (ctx ).ViaIndex (idx ))
149
+ }
150
+ }
151
+ return errs
152
+ }
153
+
154
+ func validateStep (ctx context.Context , s Step , names sets.String ) (errs * apis.FieldError ) {
155
+ if err := validateArtifactsReferencesInStep (ctx , s ); err != nil {
156
+ return err
157
+ }
158
+
159
+ if s .Ref != nil {
160
+ if ! config .FromContextOrDefaults (ctx ).FeatureFlags .EnableStepActions && isCreateOrUpdateAndDiverged (ctx , s ) {
161
+ return apis .ErrGeneric (fmt .Sprintf ("feature flag %s should be set to true to reference StepActions in Steps." , config .EnableStepActions ), "" )
162
+ }
163
+ errs = errs .Also (s .Ref .Validate (ctx ))
164
+ if s .Image != "" {
165
+ errs = errs .Also (& apis.FieldError {
166
+ Message : "image cannot be used with Ref" ,
167
+ Paths : []string {"image" },
168
+ })
169
+ }
170
+ if len (s .Command ) > 0 {
171
+ errs = errs .Also (& apis.FieldError {
172
+ Message : "command cannot be used with Ref" ,
173
+ Paths : []string {"command" },
174
+ })
175
+ }
176
+ if len (s .Args ) > 0 {
177
+ errs = errs .Also (& apis.FieldError {
178
+ Message : "args cannot be used with Ref" ,
179
+ Paths : []string {"args" },
180
+ })
181
+ }
182
+ if s .Script != "" {
183
+ errs = errs .Also (& apis.FieldError {
184
+ Message : "script cannot be used with Ref" ,
185
+ Paths : []string {"script" },
186
+ })
187
+ }
188
+ if s .WorkingDir != "" {
189
+ errs = errs .Also (& apis.FieldError {
190
+ Message : "working dir cannot be used with Ref" ,
191
+ Paths : []string {"workingDir" },
192
+ })
193
+ }
194
+ if s .Env != nil {
195
+ errs = errs .Also (& apis.FieldError {
196
+ Message : "env cannot be used with Ref" ,
197
+ Paths : []string {"env" },
198
+ })
199
+ }
200
+ if len (s .VolumeMounts ) > 0 {
201
+ errs = errs .Also (& apis.FieldError {
202
+ Message : "volumeMounts cannot be used with Ref" ,
203
+ Paths : []string {"volumeMounts" },
204
+ })
205
+ }
206
+ if len (s .Results ) > 0 {
207
+ errs = errs .Also (& apis.FieldError {
208
+ Message : "results cannot be used with Ref" ,
209
+ Paths : []string {"results" },
210
+ })
211
+ }
212
+ } else {
213
+ if len (s .Params ) > 0 {
214
+ errs = errs .Also (& apis.FieldError {
215
+ Message : "params cannot be used without Ref" ,
216
+ Paths : []string {"params" },
217
+ })
218
+ }
219
+ if len (s .Results ) > 0 {
220
+ if ! config .FromContextOrDefaults (ctx ).FeatureFlags .EnableStepActions && isCreateOrUpdateAndDiverged (ctx , s ) {
221
+ return apis .ErrGeneric (fmt .Sprintf ("feature flag %s should be set to true in order to use Results in Steps." , config .EnableStepActions ), "" )
222
+ }
223
+ }
224
+ if len (s .When ) > 0 {
225
+ if ! config .FromContextOrDefaults (ctx ).FeatureFlags .EnableStepActions && isCreateOrUpdateAndDiverged (ctx , s ) {
226
+ return apis .ErrGeneric (fmt .Sprintf ("feature flag %s should be set to true in order to use When in Steps." , config .EnableStepActions ), "" )
227
+ }
228
+ }
229
+ if s .Image == "" {
230
+ errs = errs .Also (apis .ErrMissingField ("Image" ))
231
+ }
232
+
233
+ if s .Script != "" {
234
+ if len (s .Command ) > 0 {
235
+ errs = errs .Also (& apis.FieldError {
236
+ Message : "script cannot be used with command" ,
237
+ Paths : []string {"script" },
238
+ })
239
+ }
240
+ }
241
+ }
242
+
243
+ if s .Name != "" {
244
+ if names .Has (s .Name ) {
245
+ errs = errs .Also (apis .ErrInvalidValue (s .Name , "name" ))
246
+ }
247
+ if e := validation .IsDNS1123Label (s .Name ); len (e ) > 0 {
248
+ errs = errs .Also (& apis.FieldError {
249
+ Message : fmt .Sprintf ("invalid value %q" , s .Name ),
250
+ Paths : []string {"name" },
251
+ Details : "Task step name must be a valid DNS Label, For more info refer to https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names" ,
252
+ })
253
+ }
254
+ names .Insert (s .Name )
255
+ }
256
+
257
+ if s .Timeout != nil {
258
+ if s .Timeout .Duration < time .Duration (0 ) {
259
+ return apis .ErrInvalidValue (s .Timeout .Duration , "negative timeout" )
260
+ }
261
+ }
262
+
263
+ for j , vm := range s .VolumeMounts {
264
+ if strings .HasPrefix (vm .MountPath , "/tekton/" ) &&
265
+ ! strings .HasPrefix (vm .MountPath , "/tekton/home" ) {
266
+ errs = errs .Also (apis .ErrGeneric (fmt .Sprintf ("volumeMount cannot be mounted under /tekton/ (volumeMount %q mounted at %q)" , vm .Name , vm .MountPath ), "mountPath" ).ViaFieldIndex ("volumeMounts" , j ))
267
+ }
268
+ if strings .HasPrefix (vm .Name , "tekton-internal-" ) {
269
+ errs = errs .Also (apis .ErrGeneric (fmt .Sprintf (`volumeMount name %q cannot start with "tekton-internal-"` , vm .Name ), "name" ).ViaFieldIndex ("volumeMounts" , j ))
270
+ }
271
+ }
272
+
273
+ if s .OnError != "" {
274
+ if ! isParamRefs (string (s .OnError )) && s .OnError != Continue && s .OnError != StopAndFail {
275
+ errs = errs .Also (& apis.FieldError {
276
+ Message : fmt .Sprintf ("invalid value: \" %v\" " , s .OnError ),
277
+ Paths : []string {"onError" },
278
+ Details : "Task step onError must be either \" continue\" or \" stopAndFail\" " ,
279
+ })
280
+ }
281
+ }
282
+
283
+ if s .Script != "" {
284
+ cleaned := strings .TrimSpace (s .Script )
285
+ if strings .HasPrefix (cleaned , "#!win" ) {
286
+ errs = errs .Also (config .ValidateEnabledAPIFields (ctx , "windows script support" , config .AlphaAPIFields ).ViaField ("script" ))
287
+ }
288
+ }
289
+
290
+ // StdoutConfig is an alpha feature and will fail validation if it's used in a task spec
291
+ // when the enable-api-fields feature gate is not "alpha".
292
+ if s .StdoutConfig != nil {
293
+ errs = errs .Also (config .ValidateEnabledAPIFields (ctx , "step stdout stream support" , config .AlphaAPIFields ).ViaField ("stdoutconfig" ))
294
+ }
295
+ // StderrConfig is an alpha feature and will fail validation if it's used in a task spec
296
+ // when the enable-api-fields feature gate is not "alpha".
297
+ if s .StderrConfig != nil {
298
+ errs = errs .Also (config .ValidateEnabledAPIFields (ctx , "step stderr stream support" , config .AlphaAPIFields ).ViaField ("stderrconfig" ))
299
+ }
300
+
301
+ // Validate usage of step result reference.
302
+ // Referencing previous step's results are only allowed in `env`, `command` and `args`.
303
+ errs = errs .Also (validateStepResultReference (s ))
304
+
305
+ // Validate usage of step artifacts output reference
306
+ // Referencing previous step's results are only allowed in `env`, `command` and `args`, `script`.
307
+ errs = errs .Also (validateStepArtifactsReference (s ))
308
+ return errs
309
+ }
310
+
311
+ func validateArtifactsReferencesInStep (ctx context.Context , s Step ) * apis.FieldError {
312
+ if ! config .FromContextOrDefaults (ctx ).FeatureFlags .EnableArtifacts {
313
+ var t []string
314
+ t = append (t , s .Script )
315
+ t = append (t , s .Command ... )
316
+ t = append (t , s .Args ... )
317
+ for _ , e := range s .Env {
318
+ t = append (t , e .Value )
319
+ }
320
+ if slices .ContainsFunc (t , stepArtifactReferenceExists ) || slices .ContainsFunc (t , taskArtifactReferenceExists ) {
321
+ return apis .ErrGeneric (fmt .Sprintf ("feature flag %s should be set to true to use artifacts feature." , config .EnableArtifacts ), "" )
322
+ }
323
+ }
324
+ return nil
325
+ }
326
+
327
+ func stepArtifactReferenceExists (src string ) bool {
328
+ return len (artifactref .StepArtifactRegex .FindAllStringSubmatch (src , - 1 )) > 0 || strings .Contains (src , "$(" + artifactref .StepArtifactPathPattern + ")" )
329
+ }
330
+
331
+ func taskArtifactReferenceExists (src string ) bool {
332
+ return len (artifactref .TaskArtifactRegex .FindAllStringSubmatch (src , - 1 )) > 0 || strings .Contains (src , "$(" + artifactref .TaskArtifactPathPattern + ")" )
333
+ }
334
+
335
+ func validateStepResultReference (s Step ) (errs * apis.FieldError ) {
336
+ errs = errs .Also (errorIfStepResultReferencedInField (s .Name , "name" ))
337
+ errs = errs .Also (errorIfStepResultReferencedInField (s .Image , "image" ))
338
+ errs = errs .Also (errorIfStepResultReferencedInField (s .Script , "script" ))
339
+ errs = errs .Also (errorIfStepResultReferencedInField (string (s .ImagePullPolicy ), "imagePullPolicy" ))
340
+ errs = errs .Also (errorIfStepResultReferencedInField (s .WorkingDir , "workingDir" ))
341
+ for _ , e := range s .EnvFrom {
342
+ errs = errs .Also (errorIfStepResultReferencedInField (e .Prefix , "envFrom.prefix" ))
343
+ if e .ConfigMapRef != nil {
344
+ errs = errs .Also (errorIfStepResultReferencedInField (e .ConfigMapRef .LocalObjectReference .Name , "envFrom.configMapRef" ))
345
+ }
346
+ if e .SecretRef != nil {
347
+ errs = errs .Also (errorIfStepResultReferencedInField (e .SecretRef .LocalObjectReference .Name , "envFrom.secretRef" ))
348
+ }
349
+ }
350
+ for _ , v := range s .VolumeMounts {
351
+ errs = errs .Also (errorIfStepResultReferencedInField (v .Name , "volumeMounts.name" ))
352
+ errs = errs .Also (errorIfStepResultReferencedInField (v .MountPath , "volumeMounts.mountPath" ))
353
+ errs = errs .Also (errorIfStepResultReferencedInField (v .SubPath , "volumeMounts.subPath" ))
354
+ }
355
+ for _ , v := range s .VolumeDevices {
356
+ errs = errs .Also (errorIfStepResultReferencedInField (v .Name , "volumeDevices.name" ))
357
+ errs = errs .Also (errorIfStepResultReferencedInField (v .DevicePath , "volumeDevices.devicePath" ))
358
+ }
359
+ return errs
360
+ }
361
+
362
+ func errorIfStepResultReferencedInField (value , fieldName string ) (errs * apis.FieldError ) {
363
+ matches := resultref .StepResultRegex .FindAllStringSubmatch (value , - 1 )
364
+ if len (matches ) > 0 {
365
+ errs = errs .Also (& apis.FieldError {
366
+ Message : "stepResult substitutions are only allowed in env, command and args. Found usage in" ,
367
+ Paths : []string {fieldName },
368
+ })
369
+ }
370
+ return errs
371
+ }
372
+
373
+ func validateStepArtifactsReference (s Step ) (errs * apis.FieldError ) {
374
+ errs = errs .Also (errorIfStepArtifactReferencedInField (s .Name , "name" ))
375
+ errs = errs .Also (errorIfStepArtifactReferencedInField (s .Image , "image" ))
376
+ errs = errs .Also (errorIfStepArtifactReferencedInField (string (s .ImagePullPolicy ), "imagePullPolicy" ))
377
+ errs = errs .Also (errorIfStepArtifactReferencedInField (s .WorkingDir , "workingDir" ))
378
+ for _ , e := range s .EnvFrom {
379
+ errs = errs .Also (errorIfStepArtifactReferencedInField (e .Prefix , "envFrom.prefix" ))
380
+ if e .ConfigMapRef != nil {
381
+ errs = errs .Also (errorIfStepArtifactReferencedInField (e .ConfigMapRef .LocalObjectReference .Name , "envFrom.configMapRef" ))
382
+ }
383
+ if e .SecretRef != nil {
384
+ errs = errs .Also (errorIfStepArtifactReferencedInField (e .SecretRef .LocalObjectReference .Name , "envFrom.secretRef" ))
385
+ }
386
+ }
387
+ for _ , v := range s .VolumeMounts {
388
+ errs = errs .Also (errorIfStepArtifactReferencedInField (v .Name , "volumeMounts.name" ))
389
+ errs = errs .Also (errorIfStepArtifactReferencedInField (v .MountPath , "volumeMounts.mountPath" ))
390
+ errs = errs .Also (errorIfStepArtifactReferencedInField (v .SubPath , "volumeMounts.subPath" ))
391
+ }
392
+ for _ , v := range s .VolumeDevices {
393
+ errs = errs .Also (errorIfStepArtifactReferencedInField (v .Name , "volumeDevices.name" ))
394
+ errs = errs .Also (errorIfStepArtifactReferencedInField (v .DevicePath , "volumeDevices.devicePath" ))
395
+ }
396
+ return errs
397
+ }
398
+
399
+ func errorIfStepArtifactReferencedInField (value , fieldName string ) (errs * apis.FieldError ) {
400
+ if stepArtifactReferenceExists (value ) {
401
+ errs = errs .Also (& apis.FieldError {
402
+ Message : "stepArtifact substitutions are only allowed in env, command, args and script. Found usage in" ,
403
+ Paths : []string {fieldName },
404
+ })
405
+ }
406
+ return errs
407
+ }
408
+
409
+ // ValidateStepResultsVariables validates if the StepResults referenced in step script are defined in step's results.
410
+ func ValidateStepResultsVariables (ctx context.Context , results []StepResult , script string ) (errs * apis.FieldError ) {
411
+ resultsNames := sets .NewString ()
412
+ for _ , r := range results {
413
+ resultsNames .Insert (r .Name )
414
+ }
415
+ errs = errs .Also (substitution .ValidateNoReferencesToUnknownVariables (script , "step.results" , resultsNames ).ViaField ("script" ))
416
+ errs = errs .Also (substitution .ValidateNoReferencesToUnknownVariables (script , "results" , resultsNames ).ViaField ("script" ))
417
+ return errs
418
+ }
419
+
420
+ // ValidateStepResults validates that all of the declared StepResults are valid.
421
+ func ValidateStepResults (ctx context.Context , results []StepResult ) (errs * apis.FieldError ) {
422
+ for index , result := range results {
423
+ errs = errs .Also (result .Validate (ctx ).ViaIndex (index ))
424
+ }
425
+ return errs
426
+ }
0 commit comments