diff --git a/docs/pipelineruns.md b/docs/pipelineruns.md index b4d8553ed45..832d1fb6f71 100644 --- a/docs/pipelineruns.md +++ b/docs/pipelineruns.md @@ -33,7 +33,7 @@ following fields: `PipelineRun` resource object, for example a `name`. - [`spec`][kubernetes-overview] - Specifies the configuration information for your `PipelineRun` resource object. - - `pipelineRef` - Specifies the [`Pipeline`](pipelines.md) you want to run. + - [`pipelineRef` or `pipelineSpec`](#specifiying-a-pipeline) - Specifies the [`Pipeline`](pipelines.md) you want to run. - Optional: - [`resources`](#resources) - Specifies which @@ -55,6 +55,103 @@ following fields: [kubernetes-overview]: https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields +### Specifying a pipeline + +Since a `PipelineRun` is an invocation of a [`Pipeline`](pipelines.md), you must sepcify +what `Pipeline` to invoke. + +You can do this by providing a reference to an existing `Pipeline`: + +```yaml +spec: + pipelineRef: + name: myPipeline + +``` + +Or you can embed the spec of the `Pipeline` directly in the `PipelineRun`: + +```yaml +spec: + pipelineSpec: + tasks: + - name: task1 + taskRef: + name: myTask +``` + +A sample `PipelineRun` to display greetings while embedding the spec of the `Pipeline` directly in the `PipelineRun`: + +```yaml +apiVersion: tekton.dev/v1alpha1 +kind: Task +metadata: + name: task-echo-message +spec: + inputs: + params: + - name: MESSAGE + type: string + default: "Hello World" + steps: + - name: echo + image: ubuntu + command: + - echo + args: + - "$(inputs.params.MESSAGE)" +--- + +apiVersion: tekton.dev/v1alpha1 +kind: PipelineRun +metadata: + name: pipelinerun-echo-greetings +spec: + pipelineSpec: + params: + - name: MORNING_GREETINGS + description: "morning greetings, default is Good Morning!" + type: string + default: "Good Morning!" + - name: NIGHT_GREETINGS + description: "Night greetings, default is Good Night!" + type: string + default: "Good Night!" + tasks: + # Task to display morning greetings + - name: echo-good-morning + taskRef: + name: task-echo-message + params: + - name: MESSAGE + value: $(params.MORNING_GREETINGS) + # Task to display night greetings + - name: echo-good-night + taskRef: + name: task-echo-message + params: + - name: MESSAGE + value: $(params.NIGHT_GREETINGS) + params: + - name: MORNING_GREETINGS + value: "Good Morning, Bob!" + - name: NIGHT_GREETINGS + value: "Good Night, Bob!" +``` + +Logs from a pod displaying morning greetings: + +```bash +kubectl logs $(kubectl get pods -o name | grep pipelinerun-echo-greetings-echo-good-morning) +Good Morning, Bob! +``` + +Logs from a pod displaying morning greetings: +```bash +kubectl logs $(kubectl get pods -o name | grep pipelinerun-echo-greetings-echo-good-night) +Good Night, Bob! +``` + ### Resources When running a [`Pipeline`](pipelines.md), you will need to specify the @@ -85,6 +182,34 @@ spec: name: skaffold-image-leeroy-app ``` +Or you can embed the spec of the `Resource` directly in the `PipelineRun`: + + +```yaml +spec: + resources: + - name: source-repo + resourceSpec: + type: git + params: + - name: revision + value: v0.32.0 + - name: url + value: https://github.com/GoogleContainerTools/skaffold + - name: web-image + resourceSpec: + type: image + params: + - name: url + value: gcr.io/christiewilson-catfactory/leeroy-web + - name: app-image + resourceSpec: + type: image + params: + - name: url + value: gcr.io/christiewilson-catfactory/leeroy-app +``` + ### Service Account Specifies the `name` of a `ServiceAccount` resource object. Use the @@ -121,7 +246,6 @@ spec: - name: build-task taskRef: name: build-push - tasks: - name: test-task taskRef: name: test @@ -174,7 +298,7 @@ spec: tasks: - name: task1 taskRef: - name: myTask + name: myTask --- apiVersion: tekton.dev/v1alpha1 kind: PipelineRun diff --git a/pkg/apis/pipeline/v1alpha1/pipeline_interface.go b/pkg/apis/pipeline/v1alpha1/pipeline_interface.go new file mode 100644 index 00000000000..f363d27af37 --- /dev/null +++ b/pkg/apis/pipeline/v1alpha1/pipeline_interface.go @@ -0,0 +1,26 @@ +/* +Copyright 2019 The Tekton Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// PipelineInterface is implemented by Pipeline and ClusterPipeline +type PipelineInterface interface { + PipelineMetadata() metav1.ObjectMeta + PipelineSpec() PipelineSpec + Copy() PipelineInterface +} diff --git a/pkg/apis/pipeline/v1alpha1/pipeline_types.go b/pkg/apis/pipeline/v1alpha1/pipeline_types.go index bf1b06195f2..a17503d2627 100644 --- a/pkg/apis/pipeline/v1alpha1/pipeline_types.go +++ b/pkg/apis/pipeline/v1alpha1/pipeline_types.go @@ -73,6 +73,18 @@ type Pipeline struct { Status PipelineStatus `json:"status"` } +func (p *Pipeline) PipelineMetadata() metav1.ObjectMeta { + return p.ObjectMeta +} + +func (p *Pipeline) PipelineSpec() PipelineSpec { + return p.Spec +} + +func (p *Pipeline) Copy() PipelineInterface { + return p.DeepCopy() +} + // PipelineTask defines a task in a Pipeline, passing inputs from both // Params and from the output of previous tasks. type PipelineTask struct { diff --git a/pkg/apis/pipeline/v1alpha1/pipelinerun_types.go b/pkg/apis/pipeline/v1alpha1/pipelinerun_types.go index 2e918423e37..63c4616523b 100644 --- a/pkg/apis/pipeline/v1alpha1/pipelinerun_types.go +++ b/pkg/apis/pipeline/v1alpha1/pipelinerun_types.go @@ -43,6 +43,8 @@ var _ apis.Defaultable = (*PipelineRun)(nil) // PipelineRunSpec defines the desired state of PipelineRun type PipelineRunSpec struct { PipelineRef PipelineRef `json:"pipelineRef"` + // +optional + PipelineSpec *PipelineSpec `json:"pipelineSpec,omitempty"` // Resources is a list of bindings specifying which actual instances of // PipelineResources to use for the resources the Pipeline has declared // it needs. diff --git a/pkg/apis/pipeline/v1alpha1/pipelinerun_validation.go b/pkg/apis/pipeline/v1alpha1/pipelinerun_validation.go index 5305f20a2d1..39fed5a394b 100644 --- a/pkg/apis/pipeline/v1alpha1/pipelinerun_validation.go +++ b/pkg/apis/pipeline/v1alpha1/pipelinerun_validation.go @@ -37,9 +37,22 @@ func (ps *PipelineRunSpec) Validate(ctx context.Context) *apis.FieldError { if equality.Semantic.DeepEqual(ps, &PipelineRunSpec{}) { return apis.ErrMissingField("spec") } - // pipeline reference should be present for pipelinerun - if ps.PipelineRef.Name == "" { - return apis.ErrMissingField("pipelinerun.spec.Pipelineref.Name") + + // can't have both pipelinekRef and pipelineSpec at the same time + if ps.PipelineRef.Name != "" && ps.PipelineSpec != nil { + return apis.ErrDisallowedFields("spec.pipelineRef", "spec.pipelineSpec") + } + + // Check that one of PipelineRef and PipelineSpec is present + if ps.PipelineRef.Name == "" && ps.PipelineSpec == nil { + return apis.ErrMissingField("spec.pipelineRef.name", "spec.pipelineSpec") + } + + // Validate PipelineSpec if it's present + if ps.PipelineSpec != nil { + if err := ps.PipelineSpec.Validate(ctx); err != nil { + return err + } } // check for results diff --git a/pkg/apis/pipeline/v1alpha1/pipelinerun_validation_test.go b/pkg/apis/pipeline/v1alpha1/pipelinerun_validation_test.go index a30777d20ea..bf7e1b94ac8 100644 --- a/pkg/apis/pipeline/v1alpha1/pipelinerun_validation_test.go +++ b/pkg/apis/pipeline/v1alpha1/pipelinerun_validation_test.go @@ -63,7 +63,7 @@ func TestPipelineRun_Invalidate(t *testing.T) { ServiceAccount: "foo", }, }, - want: apis.ErrMissingField("pipelinerun.spec.Pipelineref.Name"), + want: apis.ErrMissingField("spec.pipelineRef.name, spec.pipelineSpec"), }, { name: "negative pipeline timeout", pr: v1alpha1.PipelineRun{ @@ -81,11 +81,11 @@ func TestPipelineRun_Invalidate(t *testing.T) { }, } - for _, ts := range tests { - t.Run(ts.name, func(t *testing.T) { - err := ts.pr.Validate(context.Background()) - if d := cmp.Diff(err.Error(), ts.want.Error()); d != "" { - t.Errorf("PipelineRun.Validate/%s (-want, +got) = %v", ts.name, d) + for _, ps := range tests { + t.Run(ps.name, func(t *testing.T) { + err := ps.pr.Validate(context.Background()) + if d := cmp.Diff(err.Error(), ps.want.Error()); d != "" { + t.Errorf("PipelineRun.Validate/%s (-want, +got) = %v", ps.name, d) } }) } @@ -136,3 +136,70 @@ func TestPipelineRun_Validate(t *testing.T) { }) } } + +func TestPipelineRunSpec_Invalidate(t *testing.T) { + tests := []struct{ + name string + spec v1alpha1.PipelineRunSpec + wantErr *apis.FieldError + }{{ + name: "Empty pipelineSpec", + spec: v1alpha1.PipelineRunSpec{}, + wantErr: apis.ErrMissingField("spec"), + }, { + name: "pipelineRef without Pipeline Name", + spec: v1alpha1.PipelineRunSpec{ + PipelineRef: v1alpha1.PipelineRef{}, + }, + wantErr: apis.ErrMissingField("spec"), + }, { + name: "pipelineRef and pipelineSpec together", + spec: v1alpha1.PipelineRunSpec{ + PipelineRef:v1alpha1.PipelineRef{ + Name: "pipelinerefname", + }, + PipelineSpec: &v1alpha1.PipelineSpec{ + Tasks: []v1alpha1.PipelineTask{{ + Name: "mytask", + TaskRef: v1alpha1.TaskRef{ + Name: "mytask", + }, + }}}, + }, + wantErr: apis.ErrDisallowedFields("spec.pipelineSpec", "spec.pipelineRef"), + }} + for _, ps := range tests { + t.Run(ps.name, func(t *testing.T) { + err := ps.spec.Validate(context.Background()) + if d := cmp.Diff(ps.wantErr.Error(), err.Error()); d != "" { + t.Errorf("PipelineRunSpec.Validate/%s (-want, +got) = %v", ps.name, d) + } + }) + } +} + +func TestPipelineRunSpec_Validate(t *testing.T) { + tests := []struct { + name string + spec v1alpha1.PipelineRunSpec + }{{ + name: "PipelineRun without pipelineRef", + spec: v1alpha1.PipelineRunSpec{ + PipelineSpec:&v1alpha1.PipelineSpec{ + Tasks: []v1alpha1.PipelineTask{{ + Name: "mytask", + TaskRef: v1alpha1.TaskRef{ + Name: "mytask", + }, + }}, + }, + }, + }} + for _, ps := range tests { + t.Run(ps.name, func(t *testing.T) { + if err := ps.spec.Validate(context.Background()); err != nil { + t.Errorf("PipelineRunSpec.Validate/%s (-want, +got) = %v", ps.name, err) + } + }) + } +} diff --git a/pkg/apis/pipeline/v1alpha1/resource_types.go b/pkg/apis/pipeline/v1alpha1/resource_types.go index 238ce4482fc..7ef4942a94c 100644 --- a/pkg/apis/pipeline/v1alpha1/resource_types.go +++ b/pkg/apis/pipeline/v1alpha1/resource_types.go @@ -116,6 +116,10 @@ type PipelineResourceBinding struct { // ResourceRef is a reference to the instance of the actual PipelineResource // that should be used ResourceRef PipelineResourceRef `json:"resourceRef,omitempty"` + // +optional + // ResourceSpec is specification of a resource that should be created and + // consumed by the task + ResourceSpec *PipelineResourceSpec `json:"resourceSpec,omitempty"` } // PipelineResourceResult used to export the image name and digest as json diff --git a/pkg/reconciler/pipelinerun/pipelinerun.go b/pkg/reconciler/pipelinerun/pipelinerun.go index ff4e9e5ef78..23a9f478484 100644 --- a/pkg/reconciler/pipelinerun/pipelinerun.go +++ b/pkg/reconciler/pipelinerun/pipelinerun.go @@ -194,43 +194,55 @@ func (c *Reconciler) Reconcile(ctx context.Context, key string) error { return err } +func (c *Reconciler) getPipelineFunc(tr *v1alpha1.PipelineRun) resources.GetPipeline { + var gtFunc resources.GetPipeline + gtFunc = func(name string) (v1alpha1.PipelineInterface, error) { + p, err := c.pipelineLister.Pipelines(tr.Namespace).Get(name) + if err != nil { + return nil, err + } + return p, nil + } + return gtFunc +} + func (c *Reconciler) reconcile(ctx context.Context, pr *v1alpha1.PipelineRun) error { // We may be reading a version of the object that was stored at an older version // and may not have had all of the assumed default specified. pr.SetDefaults(v1alpha1.WithUpgradeViaDefaulting(ctx)) - p, err := c.pipelineLister.Pipelines(pr.Namespace).Get(pr.Spec.PipelineRef.Name) + getPipelineFunc := c.getPipelineFunc(pr) + pipelineMeta, pipelineSpec, err := resources.GetPipelineData(pr, getPipelineFunc) if err != nil { - // This Run has failed, so we need to mark it as failed and stop reconciling it + c.Logger.Errorf("Failed to determine Pipeline spec to use for pipelinerun %s: %v", pr.Name, err) pr.Status.SetCondition(&apis.Condition{ - Type: apis.ConditionSucceeded, - Status: corev1.ConditionFalse, - Reason: ReasonCouldntGetPipeline, + Type: apis.ConditionSucceeded, + Status: corev1.ConditionFalse, + Reason: ReasonCouldntGetPipeline, Message: fmt.Sprintf("Pipeline %s can't be found:%s", - fmt.Sprintf("%s/%s", pr.Namespace, pr.Spec.PipelineRef.Name), err), + fmt.Sprintf("%s/%s", pr.Namespace, pr.Name), err), }) return nil } - p = p.DeepCopy() // Propagate labels from Pipeline to PipelineRun. if pr.ObjectMeta.Labels == nil { - pr.ObjectMeta.Labels = make(map[string]string, len(p.ObjectMeta.Labels)+1) + pr.ObjectMeta.Labels = make(map[string]string, len(pipelineMeta.Labels)+1) } - for key, value := range p.ObjectMeta.Labels { + for key, value := range pipelineMeta.Labels { pr.ObjectMeta.Labels[key] = value } - pr.ObjectMeta.Labels[pipeline.GroupName+pipeline.PipelineLabelKey] = p.Name + pr.ObjectMeta.Labels[pipeline.GroupName+pipeline.PipelineLabelKey] = pipelineMeta.Name // Propagate annotations from Pipeline to PipelineRun. if pr.ObjectMeta.Annotations == nil { - pr.ObjectMeta.Annotations = make(map[string]string, len(p.ObjectMeta.Annotations)) + pr.ObjectMeta.Annotations = make(map[string]string, len(pipelineMeta.Annotations)) } - for key, value := range p.ObjectMeta.Annotations { + for key, value := range pipelineMeta.Annotations { pr.ObjectMeta.Annotations[key] = value } - d, err := v1alpha1.BuildDAG(p.Spec.Tasks) + d, err := v1alpha1.BuildDAG(pipelineSpec.Tasks) if err != nil { // This Run has failed, so we need to mark it as failed and stop reconciling it pr.Status.SetCondition(&apis.Condition{ @@ -242,7 +254,7 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1alpha1.PipelineRun) er }) return nil } - providedResources, err := resources.GetResourcesFromBindings(p, pr) + providedResources, err := resources.GetResourcesFromBindings(pipelineSpec, pr) if err != nil { // This Run has failed, so we need to mark it as failed and stop reconciling it pr.Status.SetCondition(&apis.Condition{ @@ -257,7 +269,7 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1alpha1.PipelineRun) er // Ensure that the parameters from the PipelineRun are overriding Pipeline parameters with the same type. // Weird substitution issues can occur if this is not validated (ApplyParameters() does not verify type). - err = resources.ValidateParamTypesMatching(p, pr) + err = resources.ValidateParamTypesMatching(pipelineSpec, pr) if err != nil { // This Run has failed, so we need to mark it as failed and stop reconciling it pr.Status.SetCondition(&apis.Condition{ @@ -271,7 +283,7 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1alpha1.PipelineRun) er } // Apply parameter substitution from the PipelineRun - p = resources.ApplyParameters(p, pr) + pipelineSpec = resources.ApplyParameters(pipelineSpec, pr) pipelineState, err := resources.ResolvePipelineRun( *pr, @@ -288,8 +300,9 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1alpha1.PipelineRun) er func(name string) (*v1alpha1.Condition, error) { return c.conditionLister.Conditions(pr.Namespace).Get(name) }, - p.Spec.Tasks, providedResources, + pipelineSpec.Tasks, providedResources, ) + if err != nil { // This Run has failed, so we need to mark it as failed and stop reconciling it switch err := err.(type) { @@ -299,7 +312,7 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1alpha1.PipelineRun) er Status: corev1.ConditionFalse, Reason: ReasonCouldntGetTask, Message: fmt.Sprintf("Pipeline %s can't be Run; it contains Tasks that don't exist: %s", - fmt.Sprintf("%s/%s", p.Namespace, p.Name), err), + fmt.Sprintf("%s/%s", pipelineMeta.Namespace, pipelineMeta.Name), err), }) case *resources.ResourceNotFoundError: pr.Status.SetCondition(&apis.Condition{ @@ -307,7 +320,7 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1alpha1.PipelineRun) er Status: corev1.ConditionFalse, Reason: ReasonCouldntGetResource, Message: fmt.Sprintf("PipelineRun %s can't be Run; it tries to bind Resources that don't exist: %s", - fmt.Sprintf("%s/%s", p.Namespace, pr.Name), err), + fmt.Sprintf("%s/%s", pipelineMeta.Namespace, pr.Name), err), }) case *resources.ConditionNotFoundError: pr.Status.SetCondition(&apis.Condition{ @@ -315,7 +328,7 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1alpha1.PipelineRun) er Status: corev1.ConditionFalse, Reason: ReasonCouldntGetCondition, Message: fmt.Sprintf("PipelineRun %s can't be Run; it contains Conditions that don't exist: %s", - fmt.Sprintf("%s/%s", p.Namespace, pr.Name), err), + fmt.Sprintf("%s/%s", pipelineMeta.Namespace, pr.Name), err), }) default: pr.Status.SetCondition(&apis.Condition{ @@ -323,7 +336,7 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1alpha1.PipelineRun) er Status: corev1.ConditionFalse, Reason: ReasonFailedValidation, Message: fmt.Sprintf("PipelineRun %s can't be Run; couldn't resolve all references: %s", - fmt.Sprintf("%s/%s", p.Namespace, pr.Name), err), + fmt.Sprintf("%s/%s", pipelineMeta.Namespace, pr.Name), err), }) } return nil @@ -342,7 +355,7 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1alpha1.PipelineRun) er Status: corev1.ConditionFalse, Reason: ReasonFailedValidation, Message: fmt.Sprintf("Pipeline %s can't be Run; it invalid input/output linkages: %s", - fmt.Sprintf("%s/%s", p.Namespace, pr.Name), err), + fmt.Sprintf("%s/%s", pipelineMeta.Namespace, pr.Name), err), }) return nil } diff --git a/pkg/reconciler/pipelinerun/resources/apply.go b/pkg/reconciler/pipelinerun/resources/apply.go index 389d0fa7248..45e7c853f11 100644 --- a/pkg/reconciler/pipelinerun/resources/apply.go +++ b/pkg/reconciler/pipelinerun/resources/apply.go @@ -23,7 +23,7 @@ import ( ) // ApplyParameters applies the params from a PipelineRun.Params to a PipelineSpec. -func ApplyParameters(p *v1alpha1.Pipeline, pr *v1alpha1.PipelineRun) *v1alpha1.Pipeline { +func ApplyParameters(p *v1alpha1.PipelineSpec, pr *v1alpha1.PipelineRun) *v1alpha1.PipelineSpec { // This assumes that the PipelineRun inputs have been validated against what the Pipeline requests. // stringReplacements is used for standard single-string stringReplacements, while arrayReplacements contains arrays @@ -32,7 +32,7 @@ func ApplyParameters(p *v1alpha1.Pipeline, pr *v1alpha1.PipelineRun) *v1alpha1.P arrayReplacements := map[string][]string{} // Set all the default stringReplacements - for _, p := range p.Spec.Params { + for _, p := range p.Params { if p.Default != nil { if p.Default.Type == v1alpha1.ParamTypeString { stringReplacements[fmt.Sprintf("params.%s", p.Name)] = p.Default.StringVal @@ -54,10 +54,10 @@ func ApplyParameters(p *v1alpha1.Pipeline, pr *v1alpha1.PipelineRun) *v1alpha1.P } // ApplyReplacements replaces placeholders for declared parameters with the specified replacements. -func ApplyReplacements(p *v1alpha1.Pipeline, replacements map[string]string, arrayReplacements map[string][]string) *v1alpha1.Pipeline { +func ApplyReplacements(p *v1alpha1.PipelineSpec, replacements map[string]string, arrayReplacements map[string][]string) *v1alpha1.PipelineSpec { p = p.DeepCopy() - tasks := p.Spec.Tasks + tasks := p.Tasks for i := range tasks { tasks[i].Params = replaceParamValues(tasks[i].Params, replacements, arrayReplacements) diff --git a/pkg/reconciler/pipelinerun/resources/apply_test.go b/pkg/reconciler/pipelinerun/resources/apply_test.go index 03c9af2b458..f26a5b82cfd 100644 --- a/pkg/reconciler/pipelinerun/resources/apply_test.go +++ b/pkg/reconciler/pipelinerun/resources/apply_test.go @@ -136,8 +136,8 @@ func TestApplyParameters(t *testing.T) { }} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := ApplyParameters(tt.original, tt.run) - if d := cmp.Diff(got, tt.expected); d != "" { + got := ApplyParameters(&tt.original.Spec, tt.run) + if d := cmp.Diff(got, &tt.expected.Spec); d != "" { t.Errorf("ApplyParameters() got diff %s", d) } }) diff --git a/pkg/reconciler/pipelinerun/resources/input_output_steps.go b/pkg/reconciler/pipelinerun/resources/input_output_steps.go index ac4747abec8..c5f6ee39b87 100644 --- a/pkg/reconciler/pipelinerun/resources/input_output_steps.go +++ b/pkg/reconciler/pipelinerun/resources/input_output_steps.go @@ -27,14 +27,23 @@ func GetOutputSteps(outputs map[string]*v1alpha1.PipelineResource, taskName, sto var taskOutputResources []v1alpha1.TaskResourceBinding for name, outputResource := range outputs { - taskOutputResources = append(taskOutputResources, v1alpha1.TaskResourceBinding{ - Name: name, - ResourceRef: v1alpha1.PipelineResourceRef{ + taskOutputResource := v1alpha1.TaskResourceBinding{ + Name: name, + Paths: []string{filepath.Join(storageBasePath, taskName, name)}, + } + if outputResource.Name != "" { + taskOutputResource.ResourceRef = v1alpha1.PipelineResourceRef{ Name: outputResource.Name, APIVersion: outputResource.APIVersion, - }, - Paths: []string{filepath.Join(storageBasePath, taskName, name)}, - }) + } + } else { + taskOutputResource.ResourceSpec = &v1alpha1.PipelineResourceSpec{ + Type: outputResource.Spec.Type, + Params: outputResource.Spec.Params, + SecretParams: outputResource.Spec.SecretParams, + } + } + taskOutputResources = append(taskOutputResources, taskOutputResource) } return taskOutputResources } @@ -47,10 +56,18 @@ func GetInputSteps(inputs map[string]*v1alpha1.PipelineResource, pt *v1alpha1.Pi for name, inputResource := range inputs { taskInputResource := v1alpha1.TaskResourceBinding{ Name: name, - ResourceRef: v1alpha1.PipelineResourceRef{ + } + if inputResource.Name != "" { + taskInputResource.ResourceRef = v1alpha1.PipelineResourceRef{ Name: inputResource.Name, APIVersion: inputResource.APIVersion, - }, + } + } else { + taskInputResource.ResourceSpec = &v1alpha1.PipelineResourceSpec{ + Type: inputResource.Spec.Type, + Params: inputResource.Spec.Params, + SecretParams: inputResource.Spec.SecretParams, + } } var stepSourceNames []string diff --git a/pkg/reconciler/pipelinerun/resources/pipelinerunresolution.go b/pkg/reconciler/pipelinerun/resources/pipelinerunresolution.go index ed75ef0b334..c746b2a0c94 100644 --- a/pkg/reconciler/pipelinerun/resources/pipelinerunresolution.go +++ b/pkg/reconciler/pipelinerun/resources/pipelinerunresolution.go @@ -23,6 +23,7 @@ import ( "go.uber.org/zap" "golang.org/x/xerrors" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/api/errors" "knative.dev/pkg/apis" @@ -172,11 +173,11 @@ type GetTaskRun func(name string) (*v1alpha1.TaskRun, error) // GetResourcesFromBindings will validate that all PipelineResources declared in Pipeline p are bound in PipelineRun pr // and if so, will return a map from the declared name of the PipelineResource (which is how the PipelineResource will // be referred to in the PipelineRun) to the ResourceRef. -func GetResourcesFromBindings(p *v1alpha1.Pipeline, pr *v1alpha1.PipelineRun) (map[string]v1alpha1.PipelineResourceRef, error) { - resources := map[string]v1alpha1.PipelineResourceRef{} +func GetResourcesFromBindings(p *v1alpha1.PipelineSpec, pr *v1alpha1.PipelineRun) (map[string]v1alpha1.PipelineResourceBinding, error) { + resources := map[string]v1alpha1.PipelineResourceBinding{} - required := make([]string, 0, len(p.Spec.Resources)) - for _, resource := range p.Spec.Resources { + required := make([]string, 0, len(p.Resources)) + for _, resource := range p.Resources { required = append(required, resource.Name) } provided := make([]string, 0, len(pr.Spec.Resources)) @@ -189,12 +190,12 @@ func GetResourcesFromBindings(p *v1alpha1.Pipeline, pr *v1alpha1.PipelineRun) (m } for _, resource := range pr.Spec.Resources { - resources[resource.Name] = resource.ResourceRef + resources[resource.Name] = resource } return resources, nil } -func getPipelineRunTaskResources(pt v1alpha1.PipelineTask, providedResources map[string]v1alpha1.PipelineResourceRef) ([]v1alpha1.TaskResourceBinding, []v1alpha1.TaskResourceBinding, error) { +func getPipelineRunTaskResources(pt v1alpha1.PipelineTask, providedResources map[string]v1alpha1.PipelineResourceBinding) ([]v1alpha1.TaskResourceBinding, []v1alpha1.TaskResourceBinding, error) { inputs, outputs := []v1alpha1.TaskResourceBinding{}, []v1alpha1.TaskResourceBinding{} if pt.Resources != nil { for _, taskInput := range pt.Resources.Inputs { @@ -204,7 +205,8 @@ func getPipelineRunTaskResources(pt v1alpha1.PipelineTask, providedResources map } inputs = append(inputs, v1alpha1.TaskResourceBinding{ Name: taskInput.Name, - ResourceRef: resource, + ResourceRef: resource.ResourceRef, + ResourceSpec: resource.ResourceSpec, }) } for _, taskOutput := range pt.Resources.Outputs { @@ -214,7 +216,8 @@ func getPipelineRunTaskResources(pt v1alpha1.PipelineTask, providedResources map } outputs = append(outputs, v1alpha1.TaskResourceBinding{ Name: taskOutput.Name, - ResourceRef: resource, + ResourceRef: resource.ResourceRef, + ResourceSpec: resource.ResourceSpec, }) } } @@ -262,7 +265,7 @@ func ResolvePipelineRun( getResource resources.GetResource, getCondition GetCondition, tasks []v1alpha1.PipelineTask, - providedResources map[string]v1alpha1.PipelineResourceRef, + providedResources map[string]v1alpha1.PipelineResourceBinding, ) (PipelineRunState, error) { state := []*ResolvedPipelineRunTask{} @@ -297,9 +300,11 @@ func ResolvePipelineRun( spec := t.TaskSpec() rtr, err := resources.ResolveTaskResources(&spec, t.TaskMetadata().Name, pt.TaskRef.Kind, inputs, outputs, getResource) + if err != nil { return nil, &ResourceNotFoundError{Msg: err.Error()} } + rprt.ResolvedTaskResources = rtr taskRun, err := getTaskRun(rprt.TaskRunName) @@ -492,7 +497,7 @@ func ValidateFrom(state PipelineRunState) error { func resolveConditionChecks(pt *v1alpha1.PipelineTask, taskRunStatus map[string]*v1alpha1.PipelineRunTaskRunStatus, taskRunName string, getTaskRun resources.GetTaskRun, getCondition GetCondition, - getResource resources.GetResource, providedResources map[string]v1alpha1.PipelineResourceRef) ([]*ResolvedConditionCheck, error) { + getResource resources.GetResource, providedResources map[string]v1alpha1.PipelineResourceBinding) ([]*ResolvedConditionCheck, error) { rccs := []*ResolvedConditionCheck{} for _, ptc := range pt.Conditions { cName := ptc.ConditionRef @@ -533,24 +538,33 @@ func resolveConditionChecks(pt *v1alpha1.PipelineTask, func resolveConditionResources(prc []v1alpha1.PipelineConditionResource, getResource resources.GetResource, - providedResources map[string]v1alpha1.PipelineResourceRef, + providedResources map[string]v1alpha1.PipelineResourceBinding, ) (map[string]*v1alpha1.PipelineResource, error) { rr := make(map[string]*v1alpha1.PipelineResource) for _, r := range prc { // First get a ref to actual resource name from its bound name - resourceRef, ok := providedResources[r.Resource] + resourceBinding, ok := providedResources[r.Resource] if !ok { return nil, xerrors.Errorf("resource %s not present in declared resources", r.Resource) } // Next, fetch the actual resource definition - gotResource, err := getResource(resourceRef.Name) - if err != nil { - return nil, xerrors.Errorf("could not retrieve resource %s: %w", r.Name, err) - } + if resourceBinding.ResourceRef.Name != "" { + gotResource, err := getResource(resourceBinding.ResourceRef.Name) + if err != nil { + return nil, xerrors.Errorf("could not retrieve resource %s: %w", r.Name, err) + } - // Finally add it to the resolved resources map - rr[r.Name] = gotResource + // Finally add it to the resolved resources map + rr[r.Name] = gotResource + } else if resourceBinding.ResourceSpec.Type != "" { + rr[r.Name] = &v1alpha1.PipelineResource{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceBinding.Name, + }, + Spec: *resourceBinding.ResourceSpec, + } + } } return rr, nil } diff --git a/pkg/reconciler/pipelinerun/resources/pipelinerunresolution_test.go b/pkg/reconciler/pipelinerun/resources/pipelinerunresolution_test.go index 06a8d6084e8..599923aa145 100644 --- a/pkg/reconciler/pipelinerun/resources/pipelinerunresolution_test.go +++ b/pkg/reconciler/pipelinerun/resources/pipelinerunresolution_test.go @@ -1037,13 +1037,14 @@ func TestGetResourcesFromBindings(t *testing.T) { pr := tb.PipelineRun("pipelinerun", "namespace", tb.PipelineRunSpec("pipeline", tb.PipelineRunResourceBinding("git-resource", tb.PipelineResourceBindingRef("sweet-resource")), )) - m, err := GetResourcesFromBindings(p, pr) + m, err := GetResourcesFromBindings(&p.Spec, pr) if err != nil { t.Fatalf("didn't expect error getting resources from bindings but got: %v", err) } - expectedResources := map[string]v1alpha1.PipelineResourceRef{ + expectedResources := map[string]v1alpha1.PipelineResourceBinding{ "git-resource": { - Name: "sweet-resource", + Name: "git-resource", + ResourceRef:v1alpha1.PipelineResourceRef{Name: "sweet-resource"}, }, } if d := cmp.Diff(expectedResources, m); d != "" { @@ -1059,7 +1060,7 @@ func TestGetResourcesFromBindings_Missing(t *testing.T) { pr := tb.PipelineRun("pipelinerun", "namespace", tb.PipelineRunSpec("pipeline", tb.PipelineRunResourceBinding("git-resource", tb.PipelineResourceBindingRef("sweet-resource")), )) - _, err := GetResourcesFromBindings(p, pr) + _, err := GetResourcesFromBindings(&p.Spec, pr) if err == nil { t.Fatalf("Expected error indicating `image-resource` was missing but got no error") } @@ -1073,7 +1074,7 @@ func TestGetResourcesFromBindings_Extra(t *testing.T) { tb.PipelineRunResourceBinding("git-resource", tb.PipelineResourceBindingRef("sweet-resource")), tb.PipelineRunResourceBinding("image-resource", tb.PipelineResourceBindingRef("sweet-resource2")), )) - _, err := GetResourcesFromBindings(p, pr) + _, err := GetResourcesFromBindings(&p.Spec, pr) if err == nil { t.Fatalf("Expected error indicating `image-resource` was extra but got no error") } @@ -1094,9 +1095,12 @@ func TestResolvePipelineRun(t *testing.T) { tb.PipelineTaskOutputResource("output1", "git-resource"), ), )) - providedResources := map[string]v1alpha1.PipelineResourceRef{ + providedResources := map[string]v1alpha1.PipelineResourceBinding{ "git-resource": { Name: "someresource", + ResourceRef: v1alpha1.PipelineResourceRef{ + Name: "someresource", + }, }, } @@ -1179,7 +1183,7 @@ func TestResolvePipelineRun_PipelineTaskHasNoResources(t *testing.T) { Name: "mytask3", TaskRef: v1alpha1.TaskRef{Name: "task"}, }} - providedResources := map[string]v1alpha1.PipelineResourceRef{} + providedResources := map[string]v1alpha1.PipelineResourceBinding{} getTask := func(name string) (v1alpha1.TaskInterface, error) { return task, nil } getTaskRun := func(name string) (*v1alpha1.TaskRun, error) { return &trs[0], nil } @@ -1219,7 +1223,7 @@ func TestResolvePipelineRun_TaskDoesntExist(t *testing.T) { Name: "mytask1", TaskRef: v1alpha1.TaskRef{Name: "task"}, }} - providedResources := map[string]v1alpha1.PipelineResourceRef{} + providedResources := map[string]v1alpha1.PipelineResourceBinding{} // Return an error when the Task is retrieved, as if it didn't exist getTask := func(name string) (v1alpha1.TaskInterface, error) { @@ -1273,7 +1277,7 @@ func TestResolvePipelineRun_ResourceBindingsDontExist(t *testing.T) { ), )), }} - providedResources := map[string]v1alpha1.PipelineResourceRef{} + providedResources := map[string]v1alpha1.PipelineResourceBinding{} getTask := func(name string) (v1alpha1.TaskInterface, error) { return task, nil } getTaskRun := func(name string) (*v1alpha1.TaskRun, error) { return &trs[0], nil } @@ -1319,7 +1323,7 @@ func TestResolvePipelineRun_ResourcesDontExist(t *testing.T) { ), )), }} - providedResources := map[string]v1alpha1.PipelineResourceRef{ + providedResources := map[string]v1alpha1.PipelineResourceBinding{ "git-resource": { Name: "doesnt-exist", }, @@ -1539,9 +1543,12 @@ func TestResolvePipelineRun_withExistingTaskRuns(t *testing.T) { tb.PipelineTaskInputResource("input1", "git-resource"), ), )) - providedResources := map[string]v1alpha1.PipelineResourceRef{ + providedResources := map[string]v1alpha1.PipelineResourceBinding{ "git-resource": { Name: "someresource", + ResourceRef: v1alpha1.PipelineResourceRef{ + Name: "someresource", + }, }, } @@ -1553,6 +1560,7 @@ func TestResolvePipelineRun_withExistingTaskRuns(t *testing.T) { Type: v1alpha1.PipelineResourceTypeGit, }, } + taskrunStatus := map[string]*v1alpha1.PipelineRunTaskRunStatus{} taskrunStatus["pipelinerun-mytask-with-a-really-long-name-to-trigger-tru-9l9zj"] = &v1alpha1.PipelineRunTaskRunStatus{ PipelineTaskName: "mytask-with-a-really-long-name-to-trigger-truncation", @@ -1617,7 +1625,7 @@ func TestResolveConditionChecks(t *testing.T) { TaskRef: v1alpha1.TaskRef{Name: "task"}, Conditions: []v1alpha1.PipelineTaskCondition{ptc}, }} - providedResources := map[string]v1alpha1.PipelineResourceRef{} + providedResources := map[string]v1alpha1.PipelineResourceBinding{} getTask := func(name string) (v1alpha1.TaskInterface, error) { return task, nil } getClusterTask := func(name string) (v1alpha1.TaskInterface, error) { return nil, xerrors.New("should not get called") } @@ -1697,7 +1705,7 @@ func TestResolveConditionChecks_ConditionDoesNotExist(t *testing.T) { ConditionRef: "does-not-exist", }}, }} - providedResources := map[string]v1alpha1.PipelineResourceRef{} + providedResources := map[string]v1alpha1.PipelineResourceBinding{} getTask := func(name string) (v1alpha1.TaskInterface, error) { return task, nil } getTaskRun := func(name string) (*v1alpha1.TaskRun, error) { @@ -1755,7 +1763,7 @@ func TestResolveConditionCheck_UseExistingConditionCheckName(t *testing.T) { TaskRef: v1alpha1.TaskRef{Name: "task"}, Conditions: []v1alpha1.PipelineTaskCondition{ptc}, }} - providedResources := map[string]v1alpha1.PipelineResourceRef{} + providedResources := map[string]v1alpha1.PipelineResourceBinding{} getTask := func(name string) (v1alpha1.TaskInterface, error) { return task, nil } getTaskRun := func(name string) (*v1alpha1.TaskRun, error) { @@ -1852,12 +1860,12 @@ func TestResolvedConditionCheck_WithResources(t *testing.T) { tcs := []struct { name string - providedResources map[string]v1alpha1.PipelineResourceRef + providedResources map[string]v1alpha1.PipelineResourceBinding wantErr bool expected map[string]*v1alpha1.PipelineResource }{{ name: "resource exists", - providedResources: map[string]v1alpha1.PipelineResourceRef{ + providedResources: map[string]v1alpha1.PipelineResourceBinding{ "blah": { Name: "some-repo", }, @@ -1867,7 +1875,7 @@ func TestResolvedConditionCheck_WithResources(t *testing.T) { }, }, { name: "resource does not exist", - providedResources: map[string]v1alpha1.PipelineResourceRef{ + providedResources: map[string]v1alpha1.PipelineResourceBinding{ "blah": { Name: "some-other-repo", }, @@ -1875,7 +1883,7 @@ func TestResolvedConditionCheck_WithResources(t *testing.T) { wantErr: true, }, { name: "undeclared resource", - providedResources: map[string]v1alpha1.PipelineResourceRef{ + providedResources: map[string]v1alpha1.PipelineResourceBinding{ "foo": { Name: "some-repo", }}, diff --git a/pkg/reconciler/pipelinerun/resources/pipelinespec.go b/pkg/reconciler/pipelinerun/resources/pipelinespec.go new file mode 100644 index 00000000000..78319b7c76b --- /dev/null +++ b/pkg/reconciler/pipelinerun/resources/pipelinespec.go @@ -0,0 +1,52 @@ +/* +Copyright 2019 The Tekton Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package resources + +import ( + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "golang.org/x/xerrors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// GetPipeline is a function used to retrieve Pipelines. +type GetPipeline func(string) (v1alpha1.PipelineInterface, error) +type GetPipelineRun func(string) (*v1alpha1.PipelineRun, error) + +// GetPipelineData will retrieve the Pipeline metadata and Spec associated with the +// provided PipelineRun. This can come from a reference Pipeline or from the PipelineRun's +// metadata and embedded PipelineSpec. +func GetPipelineData(pipelineRun *v1alpha1.PipelineRun, getPipeline GetPipeline) (*metav1.ObjectMeta, *v1alpha1.PipelineSpec, error) { + pipelineMeta := metav1.ObjectMeta{} + pipelineSpec := v1alpha1.PipelineSpec{} + switch { + case pipelineRun.Spec.PipelineRef.Name != "": + // Get related pipeline for pipelinerun + t, err := getPipeline(pipelineRun.Spec.PipelineRef.Name) + if err != nil { + return nil, nil, xerrors.Errorf("error when listing pipelines for pipelineRun %s: %w", pipelineRun.Name, err) + } + pipelineMeta = t.PipelineMetadata() + pipelineSpec = t.PipelineSpec() + case pipelineRun.Spec.PipelineSpec != nil: + pipelineMeta = pipelineRun.ObjectMeta + pipelineSpec = *pipelineRun.Spec.PipelineSpec + default: + return nil, nil, xerrors.Errorf("PipelineRun %s not providing PipelineRef or PipelineSpec", pipelineRun.Name) + } + return &pipelineMeta, &pipelineSpec, nil +} + diff --git a/pkg/reconciler/pipelinerun/resources/pipelinespec_test.go b/pkg/reconciler/pipelinerun/resources/pipelinespec_test.go new file mode 100644 index 00000000000..59b4d6cf4db --- /dev/null +++ b/pkg/reconciler/pipelinerun/resources/pipelinespec_test.go @@ -0,0 +1,128 @@ +/* +Copyright 2019 The Tekton Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package resources + +import ( + "testing" + + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "golang.org/x/xerrors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestGetPipelineSpec_Ref(t *testing.T) { + pipeline := &v1alpha1.Pipeline{ + ObjectMeta: metav1.ObjectMeta{ + Name: "orchestrate", + }, + Spec: v1alpha1.PipelineSpec{ + Tasks: []v1alpha1.PipelineTask{{ + Name: "mytask", + TaskRef: v1alpha1.TaskRef{ + Name: "mytask", + }, + }}, + }, + } + pr := &v1alpha1.PipelineRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mypipelinerun", + }, + Spec: v1alpha1.PipelineRunSpec{ + PipelineRef: v1alpha1.PipelineRef{ + Name: "orchestrate", + }, + }, + } + gt := func(n string) (v1alpha1.PipelineInterface, error) { return pipeline, nil } + pipelineMeta, pipelineSpec, err := GetPipelineData(pr, gt) + + if err != nil { + t.Fatalf("Did not expect error getting pipeline spec but got: %s", err) + } + + if pipelineMeta.Name != "orchestrate" { + t.Errorf("Expected pipeline name to be `orchestrate` but was %q", pipelineMeta.Name) + } + + if len(pipelineSpec.Tasks) != 1 || pipelineSpec.Tasks[0].Name != "mytask" { + t.Errorf("Pipeline Spec not resolved as expected, expected referenced Pipeline spec but got: %v", pipelineSpec) + } +} + +func TestGetPipelineSpec_Embedded(t *testing.T) { + pr := &v1alpha1.PipelineRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mypipelinerun", + }, + Spec: v1alpha1.PipelineRunSpec{ + PipelineSpec: &v1alpha1.PipelineSpec{ + Tasks: []v1alpha1.PipelineTask{{ + Name: "mytask", + TaskRef: v1alpha1.TaskRef{ + Name: "mytask", + }, + }}, + }, + }, + } + gt := func(n string) (v1alpha1.PipelineInterface, error) { return nil, xerrors.New("shouldn't be called") } + pipelineMeta, pipelineSpec, err := GetPipelineData(pr, gt) + + if err != nil { + t.Fatalf("Did not expect error getting pipeline spec but got: %s", err) + } + + if pipelineMeta.Name != "mypipelinerun" { + t.Errorf("Expected pipeline name for embedded pipeline to default to name of pipeline run but was %q", pipelineMeta.Name) + } + + if len(pipelineSpec.Tasks) != 1 || pipelineSpec.Tasks[0].Name != "mytask" { + t.Errorf("Pipeline Spec not resolved as expected, expected embedded Pipeline spec but got: %v", pipelineSpec) + } +} + +func TestGetPipelineSpec_Invalid(t *testing.T) { + tr := &v1alpha1.PipelineRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mypipelinerun", + }, + } + gt := func(n string) (v1alpha1.PipelineInterface, error) { return nil, xerrors.New("shouldn't be called") } + _, _, err := GetPipelineData(tr, gt) + if err == nil { + t.Fatalf("Expected error resolving spec with no embedded or referenced pipeline spec but didn't get error") + } +} + +func TestGetPipelineSpec_Error(t *testing.T) { + tr := &v1alpha1.PipelineRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mypipelinerun", + }, + Spec: v1alpha1.PipelineRunSpec{ + PipelineRef: v1alpha1.PipelineRef{ + Name: "orchestrate", + }, + }, + } + gt := func(n string) (v1alpha1.PipelineInterface, error) { return nil, xerrors.New("something went wrong") } + _, _, err := GetPipelineData(tr, gt) + if err == nil { + t.Fatalf("Expected error when unable to find referenced Pipeline but got none") + } +} diff --git a/pkg/reconciler/pipelinerun/resources/validate_params.go b/pkg/reconciler/pipelinerun/resources/validate_params.go index e50ada40ac4..2055a45d85b 100644 --- a/pkg/reconciler/pipelinerun/resources/validate_params.go +++ b/pkg/reconciler/pipelinerun/resources/validate_params.go @@ -22,10 +22,10 @@ import ( ) // Validate that parameters in PipelineRun override corresponding parameters in Pipeline of the same type. -func ValidateParamTypesMatching(p *v1alpha1.Pipeline, pr *v1alpha1.PipelineRun) error { +func ValidateParamTypesMatching(p *v1alpha1.PipelineSpec, pr *v1alpha1.PipelineRun) error { // Build a map of parameter names/types declared in p. paramTypes := make(map[string]v1alpha1.ParamType) - for _, param := range p.Spec.Params { + for _, param := range p.Params { paramTypes[param.Name] = param.Type } diff --git a/pkg/reconciler/pipelinerun/resources/validate_params_test.go b/pkg/reconciler/pipelinerun/resources/validate_params_test.go index 3a56f6312ae..7d9f7a1d262 100644 --- a/pkg/reconciler/pipelinerun/resources/validate_params_test.go +++ b/pkg/reconciler/pipelinerun/resources/validate_params_test.go @@ -73,7 +73,7 @@ func TestValidateParamTypesMatching_Valid(t *testing.T) { }} for _, tc := range tcs { t.Run(tc.name, func(t *testing.T) { - err := ValidateParamTypesMatching(tc.p, tc.pr) + err := ValidateParamTypesMatching(&tc.p.Spec, tc.pr) if (!tc.errorExpected) && (err != nil) { t.Errorf("Pipeline.Validate() returned error: %v", err) } @@ -115,7 +115,7 @@ func TestValidateParamTypesMatching_Invalid(t *testing.T) { }} for _, tc := range tcs { t.Run(tc.name, func(t *testing.T) { - if err := ValidateParamTypesMatching(tc.p, tc.pr); err == nil { + if err := ValidateParamTypesMatching(&tc.p.Spec, tc.pr); err == nil { t.Errorf("Expected to see error when validating PipelineRun/Pipeline param types but saw none") } }) diff --git a/test/builder/pipeline.go b/test/builder/pipeline.go index 1209ca70723..8d1f148cff9 100644 --- a/test/builder/pipeline.go +++ b/test/builder/pipeline.go @@ -306,9 +306,6 @@ func PipelineRunResourceBinding(name string, ops ...PipelineResourceBindingOp) P return func(prs *v1alpha1.PipelineRunSpec) { r := &v1alpha1.PipelineResourceBinding{ Name: name, - ResourceRef: v1alpha1.PipelineResourceRef{ - Name: name, - }, } for _, op := range ops { op(r) @@ -324,6 +321,13 @@ func PipelineResourceBindingRef(name string) PipelineResourceBindingOp { } } +// PipelineResourceBindingResourceSpec set the PipelineResourceResourceSpec to the PipelineResourceBinding. +func PipelineResourceBindingResourceSpec(spec *v1alpha1.PipelineResourceSpec) PipelineResourceBindingOp { + return func(b *v1alpha1.PipelineResourceBinding) { + b.ResourceSpec = spec + } +} + // PipelineRunServiceAccount sets the service account to the PipelineRunSpec. func PipelineRunServiceAccount(sa string) PipelineRunSpecOp { return func(prs *v1alpha1.PipelineRunSpec) { diff --git a/test/builder/pipeline_test.go b/test/builder/pipeline_test.go index 434487ef107..7acf18e90a2 100644 --- a/test/builder/pipeline_test.go +++ b/test/builder/pipeline_test.go @@ -181,6 +181,79 @@ func TestPipelineRun(t *testing.T) { } } +func TestPipelineRunWithResourceSpec(t *testing.T) { + startTime := time.Now() + completedTime := startTime.Add(5 * time.Minute) + + pipelineRun := tb.PipelineRun("pear", "foo", tb.PipelineRunSpec( + "tomatoes", tb.PipelineRunServiceAccount("sa"), + tb.PipelineRunParam("first-param-string", "first-value"), + tb.PipelineRunParam("second-param-array", "some", "array"), + tb.PipelineRunTimeout(1*time.Hour), + tb.PipelineRunResourceBinding("some-resource", + tb.PipelineResourceBindingResourceSpec(&v1alpha1.PipelineResourceSpec{ + Type: v1alpha1.PipelineResourceTypeGit, + Params:[]v1alpha1.ResourceParam{{ + Name: "url", + Value: "git", + }}})), + tb.PipelineRunServiceAccountTask("foo", "sa-2"), + ), tb.PipelineRunStatus(tb.PipelineRunStatusCondition( + apis.Condition{Type: apis.ConditionSucceeded}), + tb.PipelineRunStartTime(startTime), + tb.PipelineRunCompletionTime(completedTime), + tb.PipelineRunTaskRunsStatus("trname", &v1alpha1.PipelineRunTaskRunStatus{ + PipelineTaskName: "task-1", + }), + ), tb.PipelineRunLabel("label-key", "label-value")) + expectedPipelineRun := &v1alpha1.PipelineRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pear", + Namespace: "foo", + Labels: map[string]string{ + "label-key": "label-value", + }, + }, + Spec: v1alpha1.PipelineRunSpec{ + PipelineRef: v1alpha1.PipelineRef{Name: "tomatoes"}, + ServiceAccount: "sa", + ServiceAccounts: []v1alpha1.PipelineRunSpecServiceAccount{{TaskName: "foo", ServiceAccount: "sa-2"}}, + Params: []v1alpha1.Param{{ + Name: "first-param-string", + Value: *tb.ArrayOrString("first-value"), + }, { + Name: "second-param-array", + Value: *tb.ArrayOrString("some", "array"), + }}, + Timeout: &metav1.Duration{Duration: 1 * time.Hour}, + Resources: []v1alpha1.PipelineResourceBinding{{ + Name: "some-resource", + ResourceSpec: &v1alpha1.PipelineResourceSpec{ + Type: v1alpha1.PipelineResourceType("git"), + Params: []v1alpha1.ResourceParam{{ + Name: "url", + Value: "git", + }}, + SecretParams: nil, + }, + }}, + }, + Status: v1alpha1.PipelineRunStatus{ + Status: duckv1beta1.Status{ + Conditions: []apis.Condition{{Type: apis.ConditionSucceeded}}, + }, + StartTime: &metav1.Time{Time: startTime}, + CompletionTime: &metav1.Time{Time: completedTime}, + TaskRuns: map[string]*v1alpha1.PipelineRunTaskRunStatus{ + "trname": {PipelineTaskName: "task-1"}, + }, + }, + } + if d := cmp.Diff(expectedPipelineRun, pipelineRun); d != "" { + t.Fatalf("PipelineRun diff -want, +got: %v", d) + } +} + func TestPipelineResource(t *testing.T) { pipelineResource := tb.PipelineResource("git-resource", "foo", tb.PipelineResourceSpec( v1alpha1.PipelineResourceTypeGit, tb.PipelineResourceSpecParam("URL", "https://foo.git"),