-
Notifications
You must be signed in to change notification settings - Fork 159
Use pkg/webhook along with build webhook implementation #379
Changes from 15 commits
97bea78
2280b2f
7b7ecf3
4a462d7
432b2a4
a89dc49
817a39c
cba4a11
496374f
2c47a6c
58c05fe
dddca9c
0c21d5d
e328233
64f2949
80c7adc
d2381d0
864523c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package v1alpha1 | ||
|
||
import ( | ||
"time" | ||
|
||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
// DefaultTime is 10min | ||
const DefaultTimeout = 10 * time.Minute | ||
|
||
// SetDefaults for build | ||
func (b *Build) SetDefaults() { | ||
shashwathi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if b.Spec.ServiceAccountName == "" { | ||
b.Spec.ServiceAccountName = "default" | ||
} | ||
if b.Spec.Timeout.Duration == 0 { | ||
b.Spec.Timeout = metav1.Duration{Duration: DefaultTimeout} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package v1alpha1 | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
func TestSetDefault(t *testing.T) { | ||
shashwathi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
emptyBuild := &Build{} | ||
emptyBuild.SetDefaults() | ||
if emptyBuild.Spec.ServiceAccountName != "default" { | ||
t.Errorf("Expect default to be the serviceaccount name but got %s", emptyBuild.Spec.ServiceAccountName) | ||
} | ||
if emptyBuild.Spec.Timeout.Duration != DefaultTimeout { | ||
t.Errorf("Expect build timeout to be set") | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package v1alpha1 | ||
|
||
// SetDefaults for build template | ||
func (b *BuildTemplate) SetDefaults() { | ||
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. Can we just put this into |
||
return | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package v1alpha1 | ||
|
||
import ( | ||
"strings" | ||
|
||
"github.com/knative/pkg/apis" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
|
||
corev1 "k8s.io/api/core/v1" | ||
) | ||
|
||
// Validate build template | ||
func (b *BuildTemplate) Validate() *apis.FieldError { | ||
return validateObjectMetadata(b.GetObjectMeta()).ViaField("metadata").Also(b.Spec.Validate().ViaField("spec")) | ||
} | ||
|
||
// Validate Build Template | ||
func (b *BuildTemplateSpec) Validate() *apis.FieldError { | ||
if err := validateSteps(b.Steps); err != nil { | ||
return err | ||
} | ||
if err := validateVolumes(b.Volumes); err != nil { | ||
return err | ||
} | ||
if err := validateParameters(b.Parameters); err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
func validateObjectMetadata(meta metav1.Object) *apis.FieldError { | ||
name := meta.GetName() | ||
|
||
if strings.Contains(name, ".") { | ||
return &apis.FieldError{ | ||
Message: "Invalid resource name: special character . must not be present", | ||
Paths: []string{"name"}, | ||
} | ||
} | ||
|
||
if len(name) > 63 { | ||
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. Can we extract this to a const? |
||
return &apis.FieldError{ | ||
Message: "Invalid resource name: length must be no more than 63 characters", | ||
Paths: []string{"name"}, | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func validateParameters(params []ParameterSpec) *apis.FieldError { | ||
// Template must not duplicate parameter names. | ||
seen := map[string]struct{}{} | ||
for _, p := range params { | ||
if _, ok := seen[p.Name]; ok { | ||
return apis.ErrMultipleOneOf("ParamName") | ||
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 don't think https://godoc.org/github.com/knative/pkg/apis#ErrMultipleOneOf ( Maybe 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. Yeah pick the error with closest match I guess. I am okay with |
||
} | ||
seen[p.Name] = struct{}{} | ||
} | ||
return nil | ||
} | ||
|
||
func validateVolumes(volumes []corev1.Volume) *apis.FieldError { | ||
// Build must not duplicate volume names. | ||
vols := map[string]struct{}{} | ||
for _, v := range volumes { | ||
if _, ok := vols[v.Name]; ok { | ||
return apis.ErrMultipleOneOf("volumeName") | ||
} | ||
vols[v.Name] = struct{}{} | ||
} | ||
return nil | ||
} | ||
|
||
func validateSteps(steps []corev1.Container) *apis.FieldError { | ||
// Build must not duplicate step names. | ||
names := map[string]struct{}{} | ||
for _, s := range steps { | ||
if s.Image == "" { | ||
return apis.ErrMissingField("Image") | ||
} | ||
|
||
if s.Name == "" { | ||
continue | ||
} | ||
if _, ok := names[s.Name]; ok { | ||
return apis.ErrMultipleOneOf("stepName") | ||
} | ||
names[s.Name] = struct{}{} | ||
} | ||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
package v1alpha1 | ||
|
||
import ( | ||
"testing" | ||
|
||
corev1 "k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
func TestValidateTemplate(t *testing.T) { | ||
hasDefault := "has-default" | ||
for _, c := range []struct { | ||
desc string | ||
tmpl *BuildTemplate | ||
reason string // if "", expect success. | ||
}{{ | ||
desc: "Single named step", | ||
tmpl: &BuildTemplate{ | ||
Spec: BuildTemplateSpec{ | ||
Steps: []corev1.Container{{ | ||
Name: "foo", | ||
Image: "gcr.io/foo-bar/baz:latest", | ||
}}, | ||
}, | ||
}, | ||
}, { | ||
desc: "long name", | ||
reason: "long name", | ||
tmpl: &BuildTemplate{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "*********************************************************************************************", | ||
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. I missed my TODO there. Good catch |
||
}, | ||
}, | ||
}, { | ||
desc: "invalid name", | ||
reason: "invalid name", | ||
tmpl: &BuildTemplate{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "b.c", | ||
}, | ||
}, | ||
}, { | ||
desc: "Multiple unnamed steps", | ||
tmpl: &BuildTemplate{ | ||
Spec: BuildTemplateSpec{ | ||
Steps: []corev1.Container{{ | ||
Image: "gcr.io/foo-bar/baz:latest", | ||
}, { | ||
Image: "gcr.io/foo-bar/baz:latest", | ||
}, { | ||
Image: "gcr.io/foo-bar/baz:latest", | ||
}}, | ||
}, | ||
}, | ||
}, { | ||
tmpl: &BuildTemplate{ | ||
Spec: BuildTemplateSpec{ | ||
Steps: []corev1.Container{{ | ||
Name: "foo", | ||
Image: "gcr.io/foo-bar/baz:latest", | ||
}, { | ||
Name: "foo", | ||
Image: "gcr.io/foo-bar/baz:oops", | ||
}}, | ||
}, | ||
}, | ||
reason: "DuplicateStepName", | ||
}, { | ||
tmpl: &BuildTemplate{ | ||
Spec: BuildTemplateSpec{ | ||
Steps: []corev1.Container{{ | ||
Name: "foo", | ||
Image: "gcr.io/foo-bar/baz:latest", | ||
}}, | ||
Volumes: []corev1.Volume{{ | ||
Name: "foo", | ||
VolumeSource: corev1.VolumeSource{ | ||
EmptyDir: &corev1.EmptyDirVolumeSource{}, | ||
}, | ||
}, { | ||
Name: "foo", | ||
VolumeSource: corev1.VolumeSource{ | ||
EmptyDir: &corev1.EmptyDirVolumeSource{}, | ||
}, | ||
}}, | ||
}, | ||
}, | ||
reason: "DuplicateVolumeName", | ||
}, { | ||
tmpl: &BuildTemplate{ | ||
Spec: BuildTemplateSpec{ | ||
Parameters: []ParameterSpec{{ | ||
Name: "foo", | ||
}, { | ||
Name: "foo", | ||
}}, | ||
}, | ||
}, | ||
reason: "DuplicateParamName", | ||
}, { | ||
tmpl: &BuildTemplate{ | ||
Spec: BuildTemplateSpec{ | ||
Steps: []corev1.Container{{ | ||
Name: "step-name-${FOO${BAR}}", | ||
}}, | ||
Parameters: []ParameterSpec{{ | ||
Name: "FOO", | ||
}, { | ||
Name: "BAR", | ||
Default: &hasDefault, | ||
}}, | ||
}, | ||
}, | ||
reason: "NestedPlaceholder", | ||
}} { | ||
name := c.desc | ||
if c.reason != "" { | ||
name = "invalid-" + c.reason | ||
} | ||
t.Run(name, func(t *testing.T) { | ||
verr := c.tmpl.Validate() | ||
if gotErr, wantErr := verr != nil, c.reason != ""; gotErr != wantErr { | ||
t.Errorf("validateBuildTemplate(%s); got %v, want %q", name, verr, c.reason) | ||
} | ||
}) | ||
} | ||
} |
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.
Can you confirm that the common infrastructure works properly with cluster-scoped resources, since AFAIK this is the first one we're trying :)
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.
Cool 👍
I have added e2e test for build with template and build with cluster template as well. Going forward we will have better coverage for these features