Skip to content

Commit

Permalink
PipelineRun auto-conversion from v1alha1 to v1alpha2 🎋
Browse files Browse the repository at this point in the history
This adds auto-conversion methods and tests for PipelineRun types in
v1alpha1 and v1alpha2.

Signed-off-by: Vincent Demeester <vdemeest@redhat.com>
  • Loading branch information
vdemeester committed Feb 12, 2020
1 parent 9eef4d5 commit 4ce45d0
Show file tree
Hide file tree
Showing 7 changed files with 359 additions and 191 deletions.
98 changes: 98 additions & 0 deletions pkg/apis/pipeline/v1alpha1/pipelinerun_conversion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
Copyright 2020 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.
*/

// nolint: golint
package v1alpha1

import (
"context"
"fmt"

"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha2"
"knative.dev/pkg/apis"
)

var _ apis.Convertible = (*PipelineRun)(nil)

// ConvertUp implements api.Convertible
func (source *PipelineRun) ConvertUp(ctx context.Context, obj apis.Convertible) error {
switch sink := obj.(type) {
case *v1alpha2.PipelineRun:
sink.ObjectMeta = source.ObjectMeta
if err := source.Spec.ConvertUp(ctx, &sink.Spec); err != nil {
return err
}
sink.Status = source.Status
return nil
default:
return fmt.Errorf("unknown version, got: %T", sink)
}
}

func (source *PipelineRunSpec) ConvertUp(ctx context.Context, sink *v1alpha2.PipelineRunSpec) error {
sink.PipelineRef = source.PipelineRef
if source.PipelineSpec != nil {
sink.PipelineSpec = &v1alpha2.PipelineSpec{}
if err := source.PipelineSpec.ConvertUp(ctx, sink.PipelineSpec); err != nil {
return err
}
}
sink.Resources = source.Resources
sink.Params = source.Params
sink.ServiceAccountName = source.ServiceAccountName
sink.ServiceAccountNames = source.ServiceAccountNames
sink.Status = source.Status
sink.Timeout = source.Timeout
sink.PodTemplate = source.PodTemplate
sink.Workspaces = source.Workspaces
sink.LimitRangeName = source.LimitRangeName
return nil
}

// ConvertDown implements api.Convertible
func (sink *PipelineRun) ConvertDown(ctx context.Context, obj apis.Convertible) error {
switch source := obj.(type) {
case *v1alpha2.PipelineRun:
sink.ObjectMeta = source.ObjectMeta
if err := sink.Spec.ConvertDown(ctx, &source.Spec); err != nil {
return err
}
sink.Status = source.Status
return nil
default:
return fmt.Errorf("unknown version, got: %T", sink)
}
}

func (sink *PipelineRunSpec) ConvertDown(ctx context.Context, source *v1alpha2.PipelineRunSpec) error {
sink.PipelineRef = source.PipelineRef
if source.PipelineSpec != nil {
sink.PipelineSpec = &PipelineSpec{}
if err := sink.PipelineSpec.ConvertDown(ctx, *source.PipelineSpec); err != nil {
return err
}
}
sink.Resources = source.Resources
sink.Params = source.Params
sink.ServiceAccountName = source.ServiceAccountName
sink.ServiceAccountNames = source.ServiceAccountNames
sink.Status = source.Status
sink.Timeout = source.Timeout
sink.PodTemplate = source.PodTemplate
sink.Workspaces = source.Workspaces
sink.LimitRangeName = source.LimitRangeName
return nil
}
183 changes: 183 additions & 0 deletions pkg/apis/pipeline/v1alpha1/pipelinerun_conversion_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/*
Copyright 2020 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 (
"context"
"testing"
"time"

"github.com/google/go-cmp/cmp"
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha2"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"knative.dev/pkg/apis"
)

func TestPipelineRunConversionBadType(t *testing.T) {
good, bad := &PipelineRun{}, &Pipeline{}

if err := good.ConvertUp(context.Background(), bad); err == nil {
t.Errorf("ConvertUp() = %#v, wanted error", bad)
}

if err := good.ConvertDown(context.Background(), bad); err == nil {
t.Errorf("ConvertUp() = %#v, wanted error", bad)
}
}

func TestPipelineRunConversion(t *testing.T) {
versions := []apis.Convertible{&v1alpha2.PipelineRun{}}

tests := []struct {
name string
in *PipelineRun
wantErr bool
}{{
name: "simple conversion pipelineref",
in: &PipelineRun{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "bar",
Generation: 1,
},
Spec: PipelineRunSpec{
PipelineRef: &PipelineRef{
Name: "pipeline",
},
ServiceAccountName: "sa",
ServiceAccountNames: []PipelineRunSpecServiceAccountName{{
TaskName: "t1",
ServiceAccountName: "sa1",
}},
Timeout: &metav1.Duration{Duration: 1 * time.Minute},
PodTemplate: &PodTemplate{
NodeSelector: map[string]string{"foo": "bar"},
},
Workspaces: []WorkspaceBinding{{
Name: "w1",
SubPath: "foo",
EmptyDir: &corev1.EmptyDirVolumeSource{},
}},
LimitRangeName: "foo",
Params: []Param{{
Name: "p1",
Value: v1alpha2.ArrayOrString{StringVal: "baz"},
}},
Resources: []PipelineResourceBinding{{
Name: "i1",
ResourceRef: &v1alpha2.PipelineResourceRef{Name: "r1"},
}},
},
Status: PipelineRunStatus{
PipelineRunStatusFields: PipelineRunStatusFields{
StartTime: &metav1.Time{Time: time.Now().Add(-4 * time.Minute)},
CompletionTime: &metav1.Time{Time: time.Now().Add(-1 * time.Minute)},
TaskRuns: map[string]*PipelineRunTaskRunStatus{
"foo-run": {
PipelineTaskName: "foo",
},
},
},
},
},
}, {
name: "simple conversion pipelinespec",
in: &PipelineRun{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "bar",
Generation: 1,
},
Spec: PipelineRunSpec{
PipelineSpec: &PipelineSpec{
Tasks: []PipelineTask{{
Name: "task1",
TaskRef: &TaskRef{
Name: "taskref",
},
}, {
Name: "task2",
TaskSpec: &TaskSpec{TaskSpec: v1alpha2.TaskSpec{
Steps: []v1alpha2.Step{{Container: corev1.Container{
Image: "foo",
}}},
}},
RunAfter: []string{"task1"},
}},
},
ServiceAccountName: "sa",
ServiceAccountNames: []PipelineRunSpecServiceAccountName{{
TaskName: "t1",
ServiceAccountName: "sa1",
}},
Timeout: &metav1.Duration{Duration: 1 * time.Minute},
PodTemplate: &PodTemplate{
NodeSelector: map[string]string{"foo": "bar"},
},
Workspaces: []WorkspaceBinding{{
Name: "w1",
SubPath: "foo",
EmptyDir: &corev1.EmptyDirVolumeSource{},
}},
LimitRangeName: "foo",
Params: []Param{{
Name: "p1",
Value: v1alpha2.ArrayOrString{StringVal: "baz"},
}},
Resources: []PipelineResourceBinding{{
Name: "i1",
ResourceRef: &v1alpha2.PipelineResourceRef{Name: "r1"},
}},
},
Status: PipelineRunStatus{
PipelineRunStatusFields: PipelineRunStatusFields{
StartTime: &metav1.Time{Time: time.Now().Add(-4 * time.Minute)},
CompletionTime: &metav1.Time{Time: time.Now().Add(-1 * time.Minute)},
TaskRuns: map[string]*PipelineRunTaskRunStatus{
"foo-run": {
PipelineTaskName: "foo",
},
},
},
},
},
}}

for _, test := range tests {
for _, version := range versions {
t.Run(test.name, func(t *testing.T) {
ver := version
if err := test.in.ConvertUp(context.Background(), ver); err != nil {
if !test.wantErr {
t.Errorf("ConvertUp() = %v", err)
}
return
}
t.Logf("ConvertUp() = %#v", ver)
got := &PipelineRun{}
if err := got.ConvertDown(context.Background(), ver); err != nil {
t.Errorf("ConvertDown() = %v", err)
}
t.Logf("ConvertDown() = %#v", got)
if diff := cmp.Diff(test.in, got); diff != "" {
t.Errorf("roundtrip (-want, +got) = %v", diff)
}
})
}
}
}
64 changes: 5 additions & 59 deletions pkg/apis/pipeline/v1alpha1/pipelinerun_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"knative.dev/pkg/apis"
duckv1beta1 "knative.dev/pkg/apis/duck/v1beta1"
)

var (
Expand Down Expand Up @@ -97,12 +96,12 @@ type PipelineRunSpec struct {
}

// PipelineRunSpecStatus defines the pipelinerun spec status the user can provide
type PipelineRunSpecStatus string
type PipelineRunSpecStatus = v1alpha2.PipelineRunSpecStatus

const (
// PipelineRunSpecStatusCancelled indicates that the user wants to cancel the task,
// if not already cancelled or terminated
PipelineRunSpecStatusCancelled = "PipelineRunCancelled"
PipelineRunSpecStatusCancelled = v1alpha2.PipelineRunSpecStatusCancelled
)

// PipelineResourceRef can be used to refer to a specific instance of a Resource
Expand All @@ -113,68 +112,15 @@ type PipelineResourceRef = v1alpha2.PipelineResourceRef
type PipelineRef = v1alpha2.PipelineRef

// PipelineRunStatus defines the observed state of PipelineRun
type PipelineRunStatus struct {
duckv1beta1.Status `json:",inline"`

// PipelineRunStatusFields inlines the status fields.
PipelineRunStatusFields `json:",inline"`
}

var pipelineRunCondSet = apis.NewBatchConditionSet()

// GetCondition returns the Condition matching the given type.
func (pr *PipelineRunStatus) GetCondition(t apis.ConditionType) *apis.Condition {
return pipelineRunCondSet.Manage(pr).GetCondition(t)
}

// InitializeConditions will set all conditions in pipelineRunCondSet to unknown for the PipelineRun
// and set the started time to the current time
func (pr *PipelineRunStatus) InitializeConditions() {
if pr.TaskRuns == nil {
pr.TaskRuns = make(map[string]*PipelineRunTaskRunStatus)
}
if pr.StartTime.IsZero() {
pr.StartTime = &metav1.Time{Time: time.Now()}
}
pipelineRunCondSet.Manage(pr).InitializeConditions()
}

// SetCondition sets the condition, unsetting previous conditions with the same
// type as necessary.
func (pr *PipelineRunStatus) SetCondition(newCond *apis.Condition) {
if newCond != nil {
pipelineRunCondSet.Manage(pr).SetCondition(*newCond)
}
}
type PipelineRunStatus = v1alpha2.PipelineRunStatus

// PipelineRunStatusFields holds the fields of PipelineRunStatus' status.
// This is defined separately and inlined so that other types can readily
// consume these fields via duck typing.
type PipelineRunStatusFields struct {
// StartTime is the time the PipelineRun is actually started.
// +optional
StartTime *metav1.Time `json:"startTime,omitempty"`

// CompletionTime is the time the PipelineRun completed.
// +optional
CompletionTime *metav1.Time `json:"completionTime,omitempty"`

// map of PipelineRunTaskRunStatus with the taskRun name as the key
// +optional
TaskRuns map[string]*PipelineRunTaskRunStatus `json:"taskRuns,omitempty"`
}
type PipelineRunStatusFields = v1alpha2.PipelineRunStatusFields

// PipelineRunTaskRunStatus contains the name of the PipelineTask for this TaskRun and the TaskRun's Status
type PipelineRunTaskRunStatus struct {
// PipelineTaskName is the name of the PipelineTask.
PipelineTaskName string `json:"pipelineTaskName,omitempty"`
// Status is the TaskRunStatus for the corresponding TaskRun
// +optional
Status *TaskRunStatus `json:"status,omitempty"`
// ConditionChecks maps the name of a condition check to its Status
// +optional
ConditionChecks map[string]*PipelineRunConditionCheckStatus `json:"conditionChecks,omitempty"`
}
type PipelineRunTaskRunStatus = v1alpha2.PipelineRunTaskRunStatus

type PipelineRunConditionCheckStatus = v1alpha2.PipelineRunConditionCheckStatus

Expand Down
13 changes: 1 addition & 12 deletions pkg/apis/pipeline/v1alpha1/resource_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,18 +101,7 @@ func ApplyTaskModifier(ts *TaskSpec, tm TaskModifier) error {

// PipelineResourceBinding connects a reference to an instance of a PipelineResource
// with a PipelineResource dependency that the Pipeline has declared
type PipelineResourceBinding struct {
// Name is the name of the PipelineResource in the Pipeline's declaration
Name string `json:"name,omitempty"`
// ResourceRef is a reference to the instance of the actual PipelineResource
// that should be used
// +optional
ResourceRef *PipelineResourceRef `json:"resourceRef,omitempty"`
// ResourceSpec is specification of a resource that should be created and
// consumed by the task
// +optional
ResourceSpec *PipelineResourceSpec `json:"resourceSpec,omitempty"`
}
type PipelineResourceBinding = v1alpha2.PipelineResourceBinding

// PipelineResourceResult used to export the image name and digest as json
type PipelineResourceResult = v1alpha2.PipelineResourceResult
Expand Down
Loading

0 comments on commit 4ce45d0

Please sign in to comment.