Skip to content

Commit

Permalink
Adding support to enable pipelineSpec
Browse files Browse the repository at this point in the history
Its now possible to embed the whole pipeline specification into Pipeline Run
using pipelineSpec, for example:

apiVersion: tekton.dev/v1alpha1
kind: PipelineRun
metadata:
  name: pipelinerun-echo-greetings
spec:
  pipelineRef:
    name: pipeline-echo-greetings

Can be specified as:

apiVersion: tekton.dev/v1alpha1
kind: PipelineRun
metadata:
  name: pipelinerun-echo-greetings
spec:
  pipelineSpec:
    tasks:
    - name: echo-good-morning
    ...
    params:
    ...
  • Loading branch information
pritidesai committed Sep 20, 2019
1 parent 454ddec commit 67f4ba4
Show file tree
Hide file tree
Showing 17 changed files with 459 additions and 44 deletions.
44 changes: 43 additions & 1 deletion docs/pipelineruns.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -55,6 +55,48 @@ 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
```
[Here](../examples/pipelineruns/pipelinerun-with-pipelinespec.yaml) is a sample `PipelineRun` to display different
greetings while embedding the spec of the `Pipeline` directly in the `PipelineRun`.


After creating such `PipelineRun`, 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!
```

And 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
Expand Down
54 changes: 54 additions & 0 deletions examples/pipelineruns/pipelinerun-with-pipelinespec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
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!"
26 changes: 26 additions & 0 deletions pkg/apis/pipeline/v1alpha1/pipeline_interface.go
Original file line number Diff line number Diff line change
@@ -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
}
12 changes: 12 additions & 0 deletions pkg/apis/pipeline/v1alpha1/pipeline_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
5 changes: 4 additions & 1 deletion pkg/apis/pipeline/v1alpha1/pipelinerun_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ var _ apis.Defaultable = (*PipelineRun)(nil)

// PipelineRunSpec defines the desired state of PipelineRun
type PipelineRunSpec struct {
PipelineRef PipelineRef `json:"pipelineRef"`
// +optional
PipelineRef PipelineRef `json:"pipelineRef,omitempty"`
// +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.
Expand Down
19 changes: 16 additions & 3 deletions pkg/apis/pipeline/v1alpha1/pipelinerun_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
69 changes: 68 additions & 1 deletion pkg/apis/pipeline/v1alpha1/pipelinerun_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -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)
}
})
}
}
5 changes: 5 additions & 0 deletions pkg/apis/pipeline/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 67f4ba4

Please sign in to comment.