From e4dd1e28d8a51dbb78b2b73d42f33369a02808d1 Mon Sep 17 00:00:00 2001 From: Lee Bernick Date: Wed, 20 Jul 2022 10:03:44 -0400 Subject: [PATCH] Add conversion for v1 Task This commit adds conversion functions between v1beta1 and v1 Task. It does not handle fields deprecated in v1beta1 that will not be present in v1. It implements ConvertTo and ConvertFrom for v1beta1 Task, and leaves these functions unimplemented for v1 Task, since it is the highest known version. Conversions for all types are basically deep copies, rather than using the same struct definition for both API versions. This will allow us to iterate on v1 without making additional changes to v1beta1, after v1 is the stored version. --- pkg/apis/pipeline/v1/task_conversion.go | 39 +++ pkg/apis/pipeline/v1/task_conversion_test.go | 43 ++++ .../pipeline/v1beta1/container_conversion.go | 160 ++++++++++++ pkg/apis/pipeline/v1beta1/param_conversion.go | 47 ++++ .../pipeline/v1beta1/result_conversion.go | 29 +++ pkg/apis/pipeline/v1beta1/task_conversion.go | 110 +++++++- .../pipeline/v1beta1/task_conversion_test.go | 234 +++++++++++++++++- .../pipeline/v1beta1/workspace_conversion.go | 33 +++ 8 files changed, 686 insertions(+), 9 deletions(-) create mode 100644 pkg/apis/pipeline/v1/task_conversion.go create mode 100644 pkg/apis/pipeline/v1/task_conversion_test.go create mode 100644 pkg/apis/pipeline/v1beta1/container_conversion.go create mode 100644 pkg/apis/pipeline/v1beta1/param_conversion.go create mode 100644 pkg/apis/pipeline/v1beta1/result_conversion.go create mode 100644 pkg/apis/pipeline/v1beta1/workspace_conversion.go diff --git a/pkg/apis/pipeline/v1/task_conversion.go b/pkg/apis/pipeline/v1/task_conversion.go new file mode 100644 index 00000000000..369308e10ac --- /dev/null +++ b/pkg/apis/pipeline/v1/task_conversion.go @@ -0,0 +1,39 @@ +/* +Copyright 2022 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 v1 + +import ( + "context" + "fmt" + + "knative.dev/pkg/apis" +) + +var _ apis.Convertible = (*Task)(nil) + +// ConvertTo implements apis.Convertible +func (t *Task) ConvertTo(ctx context.Context, sink apis.Convertible) error { + if apis.IsInDelete(ctx) { + return nil + } + return fmt.Errorf("v1 is the highest known version, got: %T", sink) +} + +// ConvertFrom implements apis.Convertible +func (t *Task) ConvertFrom(ctx context.Context, source apis.Convertible) error { + if apis.IsInDelete(ctx) { + return nil + } + return fmt.Errorf("v1 is the highest known version, got: %T", source) +} diff --git a/pkg/apis/pipeline/v1/task_conversion_test.go b/pkg/apis/pipeline/v1/task_conversion_test.go new file mode 100644 index 00000000000..2244fd101d2 --- /dev/null +++ b/pkg/apis/pipeline/v1/task_conversion_test.go @@ -0,0 +1,43 @@ +/* +Copyright 2022 The Tetkon 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 v1_test + +import ( + "context" + "testing" + + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + "knative.dev/pkg/apis" +) + +type convertible struct{} + +func (c *convertible) ConvertTo(ctx context.Context, sink apis.Convertible) error { + return nil +} +func (c *convertible) ConvertFrom(ctx context.Context, source apis.Convertible) error { + return nil +} + +func TestTaskConversionBadType(t *testing.T) { + good, bad := &v1.Task{}, &convertible{} + + if err := good.ConvertTo(context.Background(), bad); err == nil { + t.Errorf("ConvertTo() = %#v, wanted error", bad) + } + + if err := good.ConvertFrom(context.Background(), bad); err == nil { + t.Errorf("ConvertFrom() = %#v, wanted error", good) + } +} diff --git a/pkg/apis/pipeline/v1beta1/container_conversion.go b/pkg/apis/pipeline/v1beta1/container_conversion.go new file mode 100644 index 00000000000..d6ed1f60cb4 --- /dev/null +++ b/pkg/apis/pipeline/v1beta1/container_conversion.go @@ -0,0 +1,160 @@ +package v1beta1 + +import ( + "context" + + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" +) + +func (s Step) convertTo(ctx context.Context, sink *v1.Step) { + sink.Name = s.Name + sink.Image = s.Image + sink.Command = s.Command + sink.Args = s.Args + sink.WorkingDir = s.WorkingDir + sink.EnvFrom = s.EnvFrom + sink.Env = s.Env + sink.Resources = s.Resources + sink.VolumeMounts = s.VolumeMounts + sink.VolumeDevices = s.VolumeDevices + sink.ImagePullPolicy = s.ImagePullPolicy + sink.SecurityContext = s.SecurityContext + sink.Script = s.Script + sink.Timeout = s.Timeout + + sink.Workspaces = nil + for _, w := range s.Workspaces { + new := v1.WorkspaceUsage{} + w.convertTo(ctx, &new) + sink.Workspaces = append(sink.Workspaces, new) + } + sink.OnError = s.OnError + sink.StdoutConfig = (*v1.StepOutputConfig)(s.StdoutConfig) + sink.StderrConfig = (*v1.StepOutputConfig)(s.StderrConfig) + + // TODO(#4546): Handle deprecated fields + // Ports, LivenessProbe, ReadinessProbe, StartupProbe, Lifecycle, TerminationMessagePath + // TerminationMessagePolicy, Stdin, StdinOnce, TTY +} + +func (s *Step) convertFrom(ctx context.Context, source v1.Step) { + s.Name = source.Name + s.Image = source.Image + s.Command = source.Command + s.Args = source.Args + s.WorkingDir = source.WorkingDir + s.EnvFrom = source.EnvFrom + s.Env = source.Env + s.Resources = source.Resources + s.VolumeMounts = source.VolumeMounts + s.VolumeDevices = source.VolumeDevices + s.ImagePullPolicy = source.ImagePullPolicy + s.SecurityContext = source.SecurityContext + s.Script = source.Script + s.Timeout = source.Timeout + + s.Workspaces = nil + for _, w := range source.Workspaces { + new := WorkspaceUsage{} + new.convertFrom(ctx, w) + s.Workspaces = append(s.Workspaces, new) + } + s.OnError = source.OnError + s.StdoutConfig = (*StepOutputConfig)(source.StdoutConfig) + s.StderrConfig = (*StepOutputConfig)(source.StderrConfig) +} + +func (s StepTemplate) convertTo(ctx context.Context, sink *v1.StepTemplate) { + sink.Image = s.Image + sink.Command = s.Command + sink.Args = s.Args + sink.WorkingDir = s.WorkingDir + sink.EnvFrom = s.EnvFrom + sink.Env = s.Env + sink.Resources = s.Resources + sink.VolumeMounts = s.VolumeMounts + sink.VolumeDevices = s.VolumeDevices + sink.ImagePullPolicy = s.ImagePullPolicy + sink.SecurityContext = s.SecurityContext + // TODO(#4546): Handle deprecated fields + // Name, Ports, LivenessProbe, ReadinessProbe, StartupProbe, Lifecycle, TerminationMessagePath + // TerminationMessagePolicy, Stdin, StdinOnce, TTY +} + +func (s *StepTemplate) convertFrom(ctx context.Context, source *v1.StepTemplate) { + s.Image = source.Image + s.Command = source.Command + s.Args = source.Args + s.WorkingDir = source.WorkingDir + s.EnvFrom = source.EnvFrom + s.Env = source.Env + s.Resources = source.Resources + s.VolumeMounts = source.VolumeMounts + s.VolumeDevices = source.VolumeDevices + s.ImagePullPolicy = source.ImagePullPolicy + s.SecurityContext = source.SecurityContext +} + +func (s Sidecar) convertTo(ctx context.Context, sink *v1.Sidecar) { + sink.Name = s.Name + sink.Image = s.Image + sink.Command = s.Command + sink.Args = s.Args + sink.WorkingDir = s.WorkingDir + sink.Ports = s.Ports + sink.EnvFrom = s.EnvFrom + sink.Env = s.Env + sink.Resources = s.Resources + sink.VolumeMounts = s.VolumeMounts + sink.VolumeDevices = s.VolumeDevices + sink.LivenessProbe = s.LivenessProbe + sink.ReadinessProbe = s.ReadinessProbe + sink.StartupProbe = s.StartupProbe + sink.Lifecycle = s.Lifecycle + sink.TerminationMessagePath = s.TerminationMessagePath + sink.TerminationMessagePolicy = s.TerminationMessagePolicy + sink.ImagePullPolicy = s.ImagePullPolicy + sink.SecurityContext = s.SecurityContext + sink.Stdin = s.Stdin + sink.StdinOnce = s.StdinOnce + sink.TTY = s.TTY + sink.Script = s.Script + sink.Workspaces = nil + for _, w := range s.Workspaces { + new := v1.WorkspaceUsage{} + w.convertTo(ctx, &new) + sink.Workspaces = append(sink.Workspaces, new) + } +} + +func (s *Sidecar) convertFrom(ctx context.Context, source v1.Sidecar) { + s.Name = source.Name + s.Image = source.Image + s.Command = source.Command + s.Args = source.Args + s.WorkingDir = source.WorkingDir + s.Ports = source.Ports + s.EnvFrom = source.EnvFrom + s.Env = source.Env + s.Resources = source.Resources + s.VolumeMounts = source.VolumeMounts + s.VolumeDevices = source.VolumeDevices + s.LivenessProbe = source.LivenessProbe + s.ReadinessProbe = source.ReadinessProbe + s.StartupProbe = source.StartupProbe + s.Lifecycle = source.Lifecycle + s.TerminationMessagePath = source.TerminationMessagePath + s.TerminationMessagePolicy = source.TerminationMessagePolicy + s.ImagePullPolicy = source.ImagePullPolicy + s.SecurityContext = source.SecurityContext + s.Stdin = source.Stdin + s.StdinOnce = source.StdinOnce + s.TTY = source.TTY + s.Script = source.Script + s.Workspaces = nil + for _, w := range source.Workspaces { + new := WorkspaceUsage{} + new.convertFrom(ctx, w) + s.Workspaces = append(s.Workspaces, new) + } +} diff --git a/pkg/apis/pipeline/v1beta1/param_conversion.go b/pkg/apis/pipeline/v1beta1/param_conversion.go new file mode 100644 index 00000000000..7c6f33fe1ce --- /dev/null +++ b/pkg/apis/pipeline/v1beta1/param_conversion.go @@ -0,0 +1,47 @@ +package v1beta1 + +import ( + "context" + + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" +) + +func (p ParamSpec) convertTo(ctx context.Context, sink *v1.ParamSpec) { + sink.Name = p.Name + sink.Type = v1.ParamType(p.Type) + sink.Description = p.Description + var properties map[string]v1.PropertySpec + if p.Properties != nil { + properties = make(map[string]v1.PropertySpec) + } + for k, v := range p.Properties { + properties[k] = v1.PropertySpec{Type: v1.ParamType(v.Type)} + } + sink.Properties = properties + if p.Default != nil { + sink.Default = &v1.ArrayOrString{ + Type: v1.ParamType(p.Default.Type), StringVal: p.Default.StringVal, + ArrayVal: p.Default.ArrayVal, ObjectVal: p.Default.ObjectVal, + } + } +} + +func (p *ParamSpec) convertFrom(ctx context.Context, source v1.ParamSpec) { + p.Name = source.Name + p.Type = ParamType(source.Type) + p.Description = source.Description + var properties map[string]PropertySpec + if source.Properties != nil { + properties = make(map[string]PropertySpec) + } + for k, v := range source.Properties { + properties[k] = PropertySpec{Type: ParamType(v.Type)} + } + p.Properties = properties + if source.Default != nil { + p.Default = &ArrayOrString{ + Type: ParamType(source.Default.Type), StringVal: source.Default.StringVal, + ArrayVal: source.Default.ArrayVal, ObjectVal: source.Default.ObjectVal, + } + } +} diff --git a/pkg/apis/pipeline/v1beta1/result_conversion.go b/pkg/apis/pipeline/v1beta1/result_conversion.go new file mode 100644 index 00000000000..70197bed164 --- /dev/null +++ b/pkg/apis/pipeline/v1beta1/result_conversion.go @@ -0,0 +1,29 @@ +package v1beta1 + +import ( + "context" + + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" +) + +func (r TaskResult) convertTo(ctx context.Context, sink *v1.TaskResult) { + sink.Name = r.Name + sink.Type = v1.ResultsType(r.Type) + sink.Description = r.Description + properties := make(map[string]v1.PropertySpec) + for k, v := range r.Properties { + properties[k] = v1.PropertySpec{Type: v1.ParamType(v.Type)} + } + sink.Properties = properties +} + +func (r *TaskResult) convertFrom(ctx context.Context, source v1.TaskResult) { + r.Name = source.Name + r.Type = ResultsType(source.Type) + r.Description = source.Description + properties := make(map[string]PropertySpec) + for k, v := range source.Properties { + properties[k] = PropertySpec{Type: ParamType(v.Type)} + } + r.Properties = properties +} diff --git a/pkg/apis/pipeline/v1beta1/task_conversion.go b/pkg/apis/pipeline/v1beta1/task_conversion.go index f8155cd97c0..7c0a65010ea 100644 --- a/pkg/apis/pipeline/v1beta1/task_conversion.go +++ b/pkg/apis/pipeline/v1beta1/task_conversion.go @@ -20,23 +20,121 @@ import ( "context" "fmt" + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" "knative.dev/pkg/apis" ) var _ apis.Convertible = (*Task)(nil) -// ConvertTo implements api.Convertible -func (t *Task) ConvertTo(ctx context.Context, sink apis.Convertible) error { +// ConvertTo implements apis.Convertible +func (t *Task) ConvertTo(ctx context.Context, to apis.Convertible) error { if apis.IsInDelete(ctx) { return nil } - return fmt.Errorf("v1beta1 is the highest known version, got: %T", sink) + switch sink := to.(type) { + case *v1.Task: + sink.ObjectMeta = t.ObjectMeta + return t.Spec.ConvertTo(ctx, &sink.Spec) + default: + return fmt.Errorf("unknown version, got: %T", sink) + } +} + +// ConvertTo implements apis.Convertible +func (ts *TaskSpec) ConvertTo(ctx context.Context, sink *v1.TaskSpec) error { + sink.Steps = nil + for _, s := range ts.Steps { + new := v1.Step{} + s.convertTo(ctx, &new) + sink.Steps = append(sink.Steps, new) + } + sink.Volumes = ts.Volumes + if ts.StepTemplate != nil { + new := v1.StepTemplate{} + ts.StepTemplate.convertTo(ctx, &new) + sink.StepTemplate = &new + } + sink.Sidecars = nil + for _, s := range ts.Sidecars { + new := v1.Sidecar{} + s.convertTo(ctx, &new) + sink.Sidecars = append(sink.Sidecars, new) + } + sink.Workspaces = nil + for _, w := range ts.Workspaces { + new := v1.WorkspaceDeclaration{} + w.convertTo(ctx, &new) + sink.Workspaces = append(sink.Workspaces, new) + } + sink.Results = nil + for _, r := range ts.Results { + new := v1.TaskResult{} + r.convertTo(ctx, &new) + sink.Results = append(sink.Results, new) + } + sink.Params = nil + for _, p := range ts.Params { + new := v1.ParamSpec{} + p.convertTo(ctx, &new) + sink.Params = append(sink.Params, new) + } + sink.Description = ts.Description + // TODO(#4546): Handle Resources + return nil } -// ConvertFrom implements api.Convertible -func (t *Task) ConvertFrom(ctx context.Context, source apis.Convertible) error { +// ConvertFrom implements apis.Convertible +func (t *Task) ConvertFrom(ctx context.Context, from apis.Convertible) error { if apis.IsInDelete(ctx) { return nil } - return fmt.Errorf("v1beta1 is the highest know version, got: %T", source) + switch source := from.(type) { + case *v1.Task: + t.ObjectMeta = source.ObjectMeta + return t.Spec.ConvertFrom(ctx, &source.Spec) + default: + return fmt.Errorf("unknown version, got: %T", t) + } +} + +// ConvertFrom implements apis.Convertible +func (ts *TaskSpec) ConvertFrom(ctx context.Context, source *v1.TaskSpec) error { + ts.Steps = nil + for _, s := range source.Steps { + new := Step{} + new.convertFrom(ctx, s) + ts.Steps = append(ts.Steps, new) + } + ts.Volumes = source.Volumes + if source.StepTemplate != nil { + new := StepTemplate{} + new.convertFrom(ctx, source.StepTemplate) + ts.StepTemplate = &new + } + ts.Sidecars = nil + for _, s := range source.Sidecars { + new := Sidecar{} + new.convertFrom(ctx, s) + ts.Sidecars = append(ts.Sidecars, new) + } + ts.Workspaces = nil + for _, w := range source.Workspaces { + new := WorkspaceDeclaration{} + new.convertFrom(ctx, w) + ts.Workspaces = append(ts.Workspaces, new) + } + ts.Results = nil + for _, r := range source.Results { + new := TaskResult{} + new.convertFrom(ctx, r) + ts.Results = append(ts.Results, new) + } + ts.Params = nil + for _, p := range source.Params { + new := ParamSpec{} + new.convertFrom(ctx, p) + ts.Params = append(ts.Params, new) + } + ts.Description = source.Description + return nil } diff --git a/pkg/apis/pipeline/v1beta1/task_conversion_test.go b/pkg/apis/pipeline/v1beta1/task_conversion_test.go index 3ee57a2c3b0..7c99582191f 100644 --- a/pkg/apis/pipeline/v1beta1/task_conversion_test.go +++ b/pkg/apis/pipeline/v1beta1/task_conversion_test.go @@ -14,21 +14,249 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1beta1 +package v1beta1_test import ( "context" "testing" + "time" + + "github.com/google/go-cmp/cmp" + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "github.com/tektoncd/pipeline/test/diff" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" ) func TestTaskConversionBadType(t *testing.T) { - good, bad := &Task{}, &Pipeline{} + good, bad := &v1beta1.Task{}, &v1beta1.Pipeline{} if err := good.ConvertTo(context.Background(), bad); err == nil { t.Errorf("ConvertTo() = %#v, wanted error", bad) } if err := good.ConvertFrom(context.Background(), bad); err == nil { - t.Errorf("ConvertFrom() = %#v, wanted error", good) + t.Errorf("ConvertTo() = %#v, wanted error", bad) + } +} + +func TestTaskConversion(t *testing.T) { + versions := []apis.Convertible{&v1.Task{}} + + tests := []struct { + name string + in *v1beta1.Task + }{{ + name: "simple task", + in: &v1beta1.Task{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "bar", + Generation: 1, + }, + Spec: v1beta1.TaskSpec{ + Description: "test", + Steps: []v1beta1.Step{{ + Image: "foo", + }}, + Volumes: []corev1.Volume{{}}, + Params: []v1beta1.ParamSpec{{ + Name: "param-1", + Type: v1beta1.ParamTypeString, + Description: "My first param", + }}, + }, + }, + }, { + name: "task conversion all non deprecated fields", + in: &v1beta1.Task{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "bar", + Generation: 1, + }, + Spec: v1beta1.TaskSpec{ + Description: "test", + Steps: []v1beta1.Step{{ + Name: "step", + Image: "foo", + Command: []string{"hello"}, + Args: []string{"world"}, + WorkingDir: "/dir", + EnvFrom: []corev1.EnvFromSource{{Prefix: "prefix"}}, + Env: []corev1.EnvVar{{Name: "var"}}, + Resources: corev1.ResourceRequirements{}, + VolumeMounts: []corev1.VolumeMount{}, + VolumeDevices: []corev1.VolumeDevice{}, + ImagePullPolicy: corev1.PullIfNotPresent, + SecurityContext: &corev1.SecurityContext{}, + Script: "echo hello", + Timeout: &metav1.Duration{Duration: time.Hour}, + Workspaces: []v1beta1.WorkspaceUsage{{Name: "workspace"}}, + OnError: "continue", + StdoutConfig: &v1beta1.StepOutputConfig{Path: "/path"}, + StderrConfig: &v1beta1.StepOutputConfig{Path: "/another-path"}, + }}, + StepTemplate: &v1beta1.StepTemplate{ + Image: "foo", + Command: []string{"hello"}, + Args: []string{"world"}, + WorkingDir: "/dir", + EnvFrom: []corev1.EnvFromSource{{Prefix: "prefix"}}, + Env: []corev1.EnvVar{{Name: "var"}}, + Resources: corev1.ResourceRequirements{}, + VolumeMounts: []corev1.VolumeMount{}, + VolumeDevices: []corev1.VolumeDevice{}, + ImagePullPolicy: corev1.PullIfNotPresent, + SecurityContext: &corev1.SecurityContext{}, + }, + Sidecars: []v1beta1.Sidecar{{ + Name: "step", + Image: "foo", + Command: []string{"hello"}, + Args: []string{"world"}, + WorkingDir: "/dir", + Ports: []corev1.ContainerPort{}, + EnvFrom: []corev1.EnvFromSource{{Prefix: "prefix"}}, + Env: []corev1.EnvVar{{Name: "var"}}, + Resources: corev1.ResourceRequirements{}, + VolumeMounts: []corev1.VolumeMount{}, + VolumeDevices: []corev1.VolumeDevice{}, + LivenessProbe: &corev1.Probe{}, + ReadinessProbe: &corev1.Probe{}, + StartupProbe: &corev1.Probe{}, + Lifecycle: &corev1.Lifecycle{}, + TerminationMessagePath: "/path", + TerminationMessagePolicy: corev1.TerminationMessageReadFile, + ImagePullPolicy: corev1.PullIfNotPresent, + SecurityContext: &corev1.SecurityContext{}, + Stdin: true, + StdinOnce: true, + TTY: true, + Script: "echo hello", + Workspaces: []v1beta1.WorkspaceUsage{{Name: "workspace"}}, + }}, + Volumes: []corev1.Volume{{Name: "volume"}}, + Params: []v1beta1.ParamSpec{{ + Name: "param-1", + Type: v1beta1.ParamTypeString, + Description: "My first param", + Properties: map[string]v1beta1.PropertySpec{"foo": {Type: v1beta1.ParamTypeString}}, + Default: v1beta1.NewArrayOrString("bar"), + }}, + Workspaces: []v1beta1.WorkspaceDeclaration{{ + Name: "workspace", + Description: "description", + MountPath: "/foo", + ReadOnly: true, + Optional: true, + }}, + + Results: []v1beta1.TaskResult{{ + Name: "result", + Type: v1beta1.ResultsTypeObject, + Properties: map[string]v1beta1.PropertySpec{"property": {Type: v1beta1.ParamTypeString}}, + Description: "description", + }}, + }, + }, + }} + + for _, test := range tests { + for _, version := range versions { + t.Run(test.name, func(t *testing.T) { + ver := version + if err := test.in.ConvertTo(context.Background(), ver); err != nil { + t.Errorf("ConvertTo() = %v", err) + return + } + t.Logf("ConvertTo() = %#v", ver) + got := &v1beta1.Task{} + if err := got.ConvertFrom(context.Background(), ver); err != nil { + t.Errorf("ConvertFrom() = %v", err) + } + t.Logf("ConvertFrom() = %#v", got) + if d := cmp.Diff(test.in, got); d != "" { + t.Errorf("roundtrip %s", diff.PrintWantGot(d)) + } + }) + } + } +} + +func TestTaskConversionFromDeprecated(t *testing.T) { + // TODO(#4546): We're just dropping Resources when converting from + // v1beta1 to v1. Before moving the stored version to v1, we should + // come up with a better strategy + versions := []apis.Convertible{&v1.Task{}} + tests := []struct { + name string + in *v1beta1.Task + want *v1beta1.Task + }{{ + name: "input resources", + in: &v1beta1.Task{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "bar", + Generation: 1, + }, + Spec: v1beta1.TaskSpec{ + Resources: &v1beta1.TaskResources{ + Inputs: []v1beta1.TaskResource{}, + }, + }, + }, + want: &v1beta1.Task{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "bar", + Generation: 1, + }, + Spec: v1beta1.TaskSpec{}, + }, + }, { + name: "output resources", + in: &v1beta1.Task{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "bar", + Generation: 1, + }, + Spec: v1beta1.TaskSpec{ + Resources: &v1beta1.TaskResources{ + Outputs: []v1beta1.TaskResource{}, + }, + }, + }, + want: &v1beta1.Task{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "bar", + Generation: 1, + }, + Spec: v1beta1.TaskSpec{}, + }, + }} + for _, test := range tests { + for _, version := range versions { + t.Run(test.name, func(t *testing.T) { + ver := version + if err := test.in.ConvertTo(context.Background(), ver); err != nil { + t.Errorf("ConvertTo() = %v", err) + } + t.Logf("ConvertTo() = %#v", ver) + got := &v1beta1.Task{} + if err := got.ConvertFrom(context.Background(), ver); err != nil { + t.Errorf("ConvertFrom() = %v", err) + } + t.Logf("ConvertFrom() = %#v", got) + if d := cmp.Diff(test.want, got); d != "" { + t.Errorf("roundtrip %s", diff.PrintWantGot(d)) + } + }) + } } } diff --git a/pkg/apis/pipeline/v1beta1/workspace_conversion.go b/pkg/apis/pipeline/v1beta1/workspace_conversion.go new file mode 100644 index 00000000000..c5920e53430 --- /dev/null +++ b/pkg/apis/pipeline/v1beta1/workspace_conversion.go @@ -0,0 +1,33 @@ +package v1beta1 + +import ( + "context" + + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" +) + +func (w WorkspaceDeclaration) convertTo(ctx context.Context, sink *v1.WorkspaceDeclaration) { + sink.Name = w.Name + sink.Description = w.Description + sink.MountPath = w.MountPath + sink.ReadOnly = w.ReadOnly + sink.Optional = w.Optional +} + +func (w *WorkspaceDeclaration) convertFrom(ctx context.Context, source v1.WorkspaceDeclaration) { + w.Name = source.Name + w.Description = source.Description + w.MountPath = source.MountPath + w.ReadOnly = source.ReadOnly + w.Optional = source.Optional +} + +func (w WorkspaceUsage) convertTo(ctx context.Context, sink *v1.WorkspaceUsage) { + sink.Name = w.Name + sink.MountPath = w.MountPath +} + +func (w *WorkspaceUsage) convertFrom(ctx context.Context, source v1.WorkspaceUsage) { + w.Name = source.Name + w.MountPath = source.MountPath +}