@@ -90,7 +90,7 @@ func isOutput(outputs []PipelineTaskOutputResource, resource string) bool {
9090
9191// validateFrom ensures that the `from` values make sense: that they rely on values from Tasks
9292// that ran previously, and that the PipelineResource is actually an output of the Task it should come from.
93- func validateFrom (tasks []PipelineTask ) error {
93+ func validateFrom (tasks []PipelineTask ) * apis. FieldError {
9494 taskOutputs := map [string ][]PipelineTaskOutputResource {}
9595 for _ , task := range tasks {
9696 var to []PipelineTaskOutputResource
@@ -114,10 +114,12 @@ func validateFrom(tasks []PipelineTask) error {
114114 for _ , pt := range rd .From {
115115 outputs , found := taskOutputs [pt ]
116116 if ! found {
117- return fmt .Errorf ("expected resource %s to be from task %s, but task %s doesn't exist" , rd .Resource , pt , pt )
117+ return apis .ErrInvalidValue (fmt .Sprintf ("expected resource %s to be from task %s, but task %s doesn't exist" , rd .Resource , pt , pt ),
118+ "spec.tasks.resources.inputs.from" )
118119 }
119120 if ! isOutput (outputs , rd .Resource ) {
120- return fmt .Errorf ("the resource %s from %s must be an output but is an input" , rd .Resource , pt )
121+ return apis .ErrInvalidValue (fmt .Sprintf ("the resource %s from %s must be an output but is an input" , rd .Resource , pt ),
122+ "spec.tasks.resources.inputs.from" )
121123 }
122124 }
123125 }
@@ -142,45 +144,9 @@ func (ps *PipelineSpec) Validate(ctx context.Context) *apis.FieldError {
142144 return apis .ErrGeneric ("expected at least one, got none" , "spec.description" , "spec.params" , "spec.resources" , "spec.tasks" , "spec.workspaces" )
143145 }
144146
145- // Names cannot be duplicated
146- taskNames := map [string ]struct {}{}
147- for i , t := range ps .Tasks {
148- if errs := validation .IsDNS1123Label (t .Name ); len (errs ) > 0 {
149- return & apis.FieldError {
150- Message : fmt .Sprintf ("invalid value %q" , t .Name ),
151- Paths : []string {fmt .Sprintf ("spec.tasks[%d].name" , i )},
152- Details : "Pipeline Task name must be a valid DNS Label. For more info refer to https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names" ,
153- }
154- }
155- // can't have both taskRef and taskSpec at the same time
156- if (t .TaskRef != nil && t .TaskRef .Name != "" ) && t .TaskSpec != nil {
157- return apis .ErrMultipleOneOf (fmt .Sprintf ("spec.tasks[%d].taskRef" , i ), fmt .Sprintf ("spec.tasks[%d].taskSpec" , i ))
158- }
159- // Check that one of TaskRef and TaskSpec is present
160- if (t .TaskRef == nil || (t .TaskRef != nil && t .TaskRef .Name == "" )) && t .TaskSpec == nil {
161- return apis .ErrMissingOneOf (fmt .Sprintf ("spec.tasks[%d].taskRef" , i ), fmt .Sprintf ("spec.tasks[%d].taskSpec" , i ))
162- }
163- // Validate TaskSpec if it's present
164- if t .TaskSpec != nil {
165- if err := t .TaskSpec .Validate (ctx ); err != nil {
166- return err
167- }
168- }
169- if t .TaskRef != nil && t .TaskRef .Name != "" {
170- // Task names are appended to the container name, which must exist and
171- // must be a valid k8s name
172- if errSlice := validation .IsQualifiedName (t .Name ); len (errSlice ) != 0 {
173- return apis .ErrInvalidValue (strings .Join (errSlice , "," ), fmt .Sprintf ("spec.tasks[%d].name" , i ))
174- }
175- // TaskRef name must be a valid k8s name
176- if errSlice := validation .IsQualifiedName (t .TaskRef .Name ); len (errSlice ) != 0 {
177- return apis .ErrInvalidValue (strings .Join (errSlice , "," ), fmt .Sprintf ("spec.tasks[%d].taskRef.name" , i ))
178- }
179- if _ , ok := taskNames [t .Name ]; ok {
180- return apis .ErrMultipleOneOf (fmt .Sprintf ("spec.tasks[%d].name" , i ))
181- }
182- taskNames [t .Name ] = struct {}{}
183- }
147+ // PipelineTask must have a valid unique label and at least one of taskRef or taskSpec should be specified
148+ if err := validatePipelineTasks (ctx , ps .Tasks ); err != nil {
149+ return err
184150 }
185151
186152 // All declared resources should be used, and the Pipeline shouldn't try to use any resources
@@ -191,7 +157,7 @@ func (ps *PipelineSpec) Validate(ctx context.Context) *apis.FieldError {
191157
192158 // The from values should make sense
193159 if err := validateFrom (ps .Tasks ); err != nil {
194- return apis . ErrInvalidValue ( err . Error (), "spec.tasks.resources.inputs.from" )
160+ return err
195161 }
196162
197163 // Validate the pipeline task graph
@@ -212,6 +178,59 @@ func (ps *PipelineSpec) Validate(ctx context.Context) *apis.FieldError {
212178 return nil
213179}
214180
181+ func validatePipelineTasks (ctx context.Context , tasks []PipelineTask ) * apis.FieldError {
182+ // Names cannot be duplicated
183+ taskNames := map [string ]struct {}{}
184+ var err * apis.FieldError
185+ for i , t := range tasks {
186+ if err = validatePipelineTaskName (ctx , "spec.tasks" , i , t , taskNames ); err != nil {
187+ return err
188+ }
189+ }
190+ return nil
191+ }
192+
193+ func validatePipelineTaskName (ctx context.Context , prefix string , i int , t PipelineTask , taskNames map [string ]struct {}) * apis.FieldError {
194+ if errs := validation .IsDNS1123Label (t .Name ); len (errs ) > 0 {
195+ return & apis.FieldError {
196+ Message : fmt .Sprintf ("invalid value %q" , t .Name ),
197+ Paths : []string {fmt .Sprintf (prefix + "[%d].name" , i )},
198+ Details : "Pipeline Task name must be a valid DNS Label." +
199+ "For more info refer to https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names" ,
200+ }
201+ }
202+ // can't have both taskRef and taskSpec at the same time
203+ if (t .TaskRef != nil && t .TaskRef .Name != "" ) && t .TaskSpec != nil {
204+ return apis .ErrMultipleOneOf (fmt .Sprintf (prefix + "[%d].taskRef" , i ), fmt .Sprintf (prefix + "[%d].taskSpec" , i ))
205+ }
206+ // Check that one of TaskRef and TaskSpec is present
207+ if (t .TaskRef == nil || (t .TaskRef != nil && t .TaskRef .Name == "" )) && t .TaskSpec == nil {
208+ return apis .ErrMissingOneOf (fmt .Sprintf (prefix + "[%d].taskRef" , i ), fmt .Sprintf (prefix + "[%d].taskSpec" , i ))
209+ }
210+ // Validate TaskSpec if it's present
211+ if t .TaskSpec != nil {
212+ if err := t .TaskSpec .Validate (ctx ); err != nil {
213+ return err
214+ }
215+ }
216+ if t .TaskRef != nil && t .TaskRef .Name != "" {
217+ // Task names are appended to the container name, which must exist and
218+ // must be a valid k8s name
219+ if errSlice := validation .IsQualifiedName (t .Name ); len (errSlice ) != 0 {
220+ return apis .ErrInvalidValue (strings .Join (errSlice , "," ), fmt .Sprintf (prefix + "[%d].name" , i ))
221+ }
222+ // TaskRef name must be a valid k8s name
223+ if errSlice := validation .IsQualifiedName (t .TaskRef .Name ); len (errSlice ) != 0 {
224+ return apis .ErrInvalidValue (strings .Join (errSlice , "," ), fmt .Sprintf (prefix + "[%d].taskRef.name" , i ))
225+ }
226+ if _ , ok := taskNames [t .Name ]; ok {
227+ return apis .ErrMultipleOneOf (fmt .Sprintf (prefix + "[%d].name" , i ))
228+ }
229+ taskNames [t .Name ] = struct {}{}
230+ }
231+ return nil
232+ }
233+
215234func validatePipelineWorkspaces (wss []WorkspacePipelineDeclaration , pts []PipelineTask ) * apis.FieldError {
216235 // Workspace names must be non-empty and unique.
217236 wsTable := make (map [string ]struct {})
0 commit comments