-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
adding finally type (without implementation) at the pipeline level #2650
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,8 @@ const ( | |
// resources when they cannot be converted to warn of a forthcoming | ||
// breakage. | ||
ConditionTypeConvertible apis.ConditionType = v1beta1.ConditionTypeConvertible | ||
// Conversion Error message for a field not available in v1alpha1 | ||
ConversionErrorFieldNotAvailableMsg = "the specified field/section is not available in v1alpha1" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice! |
||
) | ||
|
||
// CannotConvertError is returned when a field cannot be converted. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,13 +42,12 @@ func TestPipelineConversionBadType(t *testing.T) { | |
} | ||
} | ||
|
||
func TestPipelineConversion(t *testing.T) { | ||
func TestPipelineConversion_Success(t *testing.T) { | ||
versions := []apis.Convertible{&v1beta1.Pipeline{}} | ||
|
||
tests := []struct { | ||
name string | ||
in *Pipeline | ||
wantErr bool | ||
pritidesai marked this conversation as resolved.
Show resolved
Hide resolved
|
||
name string | ||
in *Pipeline | ||
}{{ | ||
name: "simple conversion", | ||
in: &Pipeline{ | ||
|
@@ -114,7 +113,38 @@ func TestPipelineConversion(t *testing.T) { | |
}}, | ||
}, | ||
}, | ||
}, { | ||
}} | ||
|
||
for _, test := range tests { | ||
for _, version := range versions { | ||
t.Run(test.name, func(t *testing.T) { | ||
ver := version | ||
// convert v1alpha1 Pipeline to v1beta1 Pipeline | ||
if err := test.in.ConvertTo(context.Background(), ver); err != nil { | ||
t.Errorf("ConvertTo() = %v", err) | ||
} | ||
got := &Pipeline{} | ||
// converting it back to v1alpha1 pipeline and storing it in got variable to compare with original input | ||
if err := got.ConvertFrom(context.Background(), ver); err != nil { | ||
t.Errorf("ConvertFrom() = %v", err) | ||
} | ||
// compare origin input and roundtrip Pipeline i.e. v1alpha1 pipeline converted to v1beta1 and then converted back to v1alpha1 | ||
// this check is making sure that we do not end up with different object than what we started with | ||
if d := cmp.Diff(test.in, got); d != "" { | ||
t.Errorf("roundtrip %s", diff.PrintWantGot(d)) | ||
pritidesai marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
}) | ||
} | ||
} | ||
} | ||
|
||
func TestPipelineConversion_Failure(t *testing.T) { | ||
versions := []apis.Convertible{&v1beta1.Pipeline{}} | ||
|
||
tests := []struct { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NIT: this is just a single test so maybe we don't need the loop |
||
name string | ||
in *Pipeline | ||
}{{ | ||
name: "simple conversion with task spec error", | ||
in: &Pipeline{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
|
@@ -152,29 +182,76 @@ func TestPipelineConversion(t *testing.T) { | |
}}, | ||
}, | ||
}, | ||
wantErr: true, | ||
}} | ||
|
||
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 { | ||
if !test.wantErr { | ||
t.Errorf("ConvertTo() = %v", err) | ||
} | ||
return | ||
} | ||
t.Logf("ConvertTo() = %#v", ver) | ||
got := &Pipeline{} | ||
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)) | ||
if err := test.in.ConvertTo(context.Background(), ver); err == nil { | ||
t.Errorf("Expected ConvertTo to fail but did not produce any error") | ||
} | ||
return | ||
}) | ||
} | ||
} | ||
} | ||
|
||
func TestPipelineConversionFromWithFinally(t *testing.T) { | ||
versions := []apis.Convertible{&v1beta1.Pipeline{}} | ||
p := &Pipeline{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "foo", | ||
Namespace: "bar", | ||
Generation: 1, | ||
}, | ||
Spec: PipelineSpec{ | ||
Tasks: []PipelineTask{{Name: "mytask", TaskRef: &TaskRef{Name: "task"}}}, | ||
}, | ||
} | ||
for _, version := range versions { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. im a bit confused about why there is a loop here when it seems like we only have one thing in versions? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yup its iterating over a list of versions which has only one version today, being a list, it would be easier to add new versions in future if we had to, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. personally id still prefer to avoid the loop when we don't need it (YAGNI!) but if that's the only piece of feedback I have then lets just go with it as is :D There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer to skip the loop to for now. |
||
t.Run("finally not available in v1alpha1", func(t *testing.T) { | ||
ver := version | ||
// convert v1alpha1 to v1beta1 | ||
if err := p.ConvertTo(context.Background(), ver); err != nil { | ||
t.Errorf("ConvertTo() = %v", err) | ||
} | ||
// modify ver to introduce new field which causes failure to convert v1beta1 to v1alpha1 | ||
source := ver | ||
source.(*v1beta1.Pipeline).Spec.Finally = []v1beta1.PipelineTask{{Name: "finaltask", TaskRef: &TaskRef{Name: "task"}}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i feel like it might be worth including an end to end test for this, e.g. one that creates a v1alpha1 object with this field and then gets an error. since we're manipulating a v1beta1 object here its a bit hard to feel completely certain that this is an accurate representation of what would happen to a user There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added one test here to convert |
||
got := &Pipeline{} | ||
if err := got.ConvertFrom(context.Background(), source); err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. when would the v1alpha1 ConvertFrom get called and why? I would guess we are always converting from v1alpha1 to v1beta1 and never the other way? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. im still a bit confused but ill go with it! i just dont understand why we go back to v1alpha1 (via ConvertFrom) at all - maybe that's after the controller is done reconciling, before we store the state back into etcd? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See my question above to @vdemeester and his reply. |
||
cce, ok := err.(*CannotConvertError) | ||
// conversion error contains the field name which resulted in the failure and should be equal to "Finally" here | ||
if ok && cce.Field == FinallyFieldName { | ||
return | ||
} | ||
t.Errorf("ConvertFrom() should have failed") | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestPipelineConversionFromBetaToAlphaWithFinally_Failure(t *testing.T) { | ||
p := &v1beta1.Pipeline{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "foo", | ||
Namespace: "bar", | ||
Generation: 1, | ||
}, | ||
Spec: v1beta1.PipelineSpec{ | ||
Tasks: []v1beta1.PipelineTask{{Name: "mytask", TaskRef: &TaskRef{Name: "task"}}}, | ||
Finally: []v1beta1.PipelineTask{{Name: "mytask", TaskRef: &TaskRef{Name: "task"}}}, | ||
}, | ||
} | ||
t.Run("finally not available in v1alpha1", func(t *testing.T) { | ||
got := &Pipeline{} | ||
if err := got.ConvertFrom(context.Background(), p); err != nil { | ||
cce, ok := err.(*CannotConvertError) | ||
// conversion error (cce) contains the field name which resulted in the failure and should be equal to "finally" here | ||
if ok && cce.Field == FinallyFieldName { | ||
return | ||
} | ||
t.Errorf("ConvertFrom() should have failed") | ||
} | ||
}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
/* | ||
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 v1beta1_test | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/tektoncd/pipeline/test/diff" | ||
|
||
"github.com/google/go-cmp/cmp" | ||
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" | ||
) | ||
|
||
func TestPipeline_SetDefaults(t *testing.T) { | ||
p := &v1beta1.Pipeline{} | ||
want := &v1beta1.Pipeline{} | ||
ctx := context.Background() | ||
p.SetDefaults(ctx) | ||
if d := cmp.Diff(want, p); d != "" { | ||
t.Errorf("Mismatch of Pipeline: empty pipeline must not change after setting defaults: %s", diff.PrintWantGot(d)) | ||
} | ||
} | ||
|
||
func TestPipelineSpec_SetDefaults(t *testing.T) { | ||
cases := []struct { | ||
desc string | ||
ps *v1beta1.PipelineSpec | ||
want *v1beta1.PipelineSpec | ||
}{{ | ||
desc: "empty pipelineSpec must not change after setting defaults", | ||
ps: &v1beta1.PipelineSpec{}, | ||
want: &v1beta1.PipelineSpec{}, | ||
}, { | ||
desc: "pipeline task - default task kind must be " + string(v1beta1.NamespacedTaskKind), | ||
ps: &v1beta1.PipelineSpec{ | ||
Tasks: []v1beta1.PipelineTask{{ | ||
Name: "foo", TaskRef: &v1beta1.TaskRef{Name: "foo-task"}, | ||
}}, | ||
}, | ||
want: &v1beta1.PipelineSpec{ | ||
Tasks: []v1beta1.PipelineTask{{ | ||
Name: "foo", TaskRef: &v1beta1.TaskRef{Name: "foo-task", Kind: v1beta1.NamespacedTaskKind}, | ||
}}, | ||
}, | ||
}, { | ||
desc: "final pipeline task - default task kind must be " + string(v1beta1.NamespacedTaskKind), | ||
ps: &v1beta1.PipelineSpec{ | ||
Finally: []v1beta1.PipelineTask{{ | ||
Name: "final-task", TaskRef: &v1beta1.TaskRef{Name: "foo-task"}, | ||
}}, | ||
}, | ||
want: &v1beta1.PipelineSpec{ | ||
Finally: []v1beta1.PipelineTask{{ | ||
Name: "final-task", TaskRef: &v1beta1.TaskRef{Name: "foo-task", Kind: v1beta1.NamespacedTaskKind}, | ||
}}, | ||
}, | ||
}, { | ||
desc: "param type - default param type must be " + string(v1beta1.ParamTypeString), | ||
ps: &v1beta1.PipelineSpec{ | ||
Params: []v1beta1.ParamSpec{{ | ||
Name: "string-param", | ||
}}, | ||
}, | ||
want: &v1beta1.PipelineSpec{ | ||
Params: []v1beta1.ParamSpec{{ | ||
Name: "string-param", Type: v1beta1.ParamTypeString, | ||
}}, | ||
}, | ||
}, { | ||
desc: "pipeline task with taskSpec - default param type must be " + string(v1beta1.ParamTypeString), | ||
ps: &v1beta1.PipelineSpec{ | ||
Tasks: []v1beta1.PipelineTask{{ | ||
Name: "foo", TaskSpec: &v1beta1.TaskSpec{ | ||
Params: []v1beta1.ParamSpec{{ | ||
Name: "string-param", | ||
}}, | ||
}, | ||
}}, | ||
}, | ||
want: &v1beta1.PipelineSpec{ | ||
Tasks: []v1beta1.PipelineTask{{ | ||
Name: "foo", TaskSpec: &v1beta1.TaskSpec{ | ||
Params: []v1beta1.ParamSpec{{ | ||
Name: "string-param", | ||
Type: v1beta1.ParamTypeString, | ||
}}, | ||
}, | ||
}}, | ||
}, | ||
}, { | ||
desc: "final pipeline task with taskSpec - default param type must be " + string(v1beta1.ParamTypeString), | ||
ps: &v1beta1.PipelineSpec{ | ||
Finally: []v1beta1.PipelineTask{{ | ||
Name: "foo", TaskSpec: &v1beta1.TaskSpec{ | ||
Params: []v1beta1.ParamSpec{{ | ||
Name: "string-param", | ||
}}, | ||
}, | ||
}}, | ||
}, | ||
want: &v1beta1.PipelineSpec{ | ||
Finally: []v1beta1.PipelineTask{{ | ||
Name: "foo", TaskSpec: &v1beta1.TaskSpec{ | ||
Params: []v1beta1.ParamSpec{{ | ||
Name: "string-param", | ||
Type: v1beta1.ParamTypeString, | ||
}}, | ||
}, | ||
}}, | ||
}, | ||
}} | ||
for _, tc := range cases { | ||
t.Run(tc.desc, func(t *testing.T) { | ||
ctx := context.Background() | ||
tc.ps.SetDefaults(ctx) | ||
if d := cmp.Diff(tc.want, tc.ps); d != "" { | ||
t.Errorf("Mismatch of pipelineSpec after setting defaults: %s: %s", tc.desc, diff.PrintWantGot(d)) | ||
} | ||
}) | ||
} | ||
} | ||
pritidesai marked this conversation as resolved.
Show resolved
Hide resolved
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ooo i like calling it a "preview" good call :D