Add Tolerations to Build and BuildRun objects#1711
Conversation
872db31 to
462d9bb
Compare
4ecfc21 to
dfe25d5
Compare
e449fbd to
03b3b21
Compare
3e66b55 to
43382a6
Compare
090570c to
b8e487a
Compare
SaschaSchwarze0
left a comment
There was a problem hiding this comment.
Nice that tests now succeed. I still have one more homework for you. Sorry that I missed that earlier.
| // In this case, fields set only on the BuildRun object do not get validated as they are not copied to the transient Build resource. | ||
| // explicitly setting them here is required for validation to happen. | ||
| build.Spec.NodeSelector = buildRun.Spec.NodeSelector | ||
| build.Spec.Tolerations = buildRun.Spec.Tolerations |
There was a problem hiding this comment.
This is not correct and reminds me of something that I missed earlier (also for nodeSelector). Users can define these things both in the inline build spec as well as the overrides on the buildrun spec.
Two things:
- We should validate the buildrun spec fields somewhere near here (https://github.com/shipwright-io/build/blob/v0.14.0/pkg/reconciler/buildrun/buildrun.go#L276-L292) and not copy it over to a build spec to perform a validation.
- When an inline build spec is used, we validate that certain fields are not set on the buildrun spec because it does not make sense to have them specified in two places. That would be suitable for both nodeSelector and tolerations as well. The code that validates this is here: https://github.com/shipwright-io/build/blob/v0.14.0/pkg/validate/validate.go#L106-L136. Basically, when somebody creates a standalone BuildRun (= with an inline Build spec), we expect that BuildRun to not also set those fields (params, volumes, timeout etc) on the BuildRun spec itself.
Can you adopt this please.
There was a problem hiding this comment.
Ah, so if I understand it seems that specifically in the inline build spec case we expect that certain fields are only specified on that build spec instead of having, for example, the "BuildRun Tolerations take preference over Build Tolerations" behavior (overriding). I'll implement these.
There was a problem hiding this comment.
@SaschaSchwarze0 Ok, let me know how that looks. Tests are good except for an unrelated flaky e2e test.
Signed-off-by: Dylan Orzel <[email protected]>
fa80d86 to
b0fee7d
Compare
Signed-off-by: Dylan Orzel <[email protected]>
b0fee7d to
e889d0a
Compare
Signed-off-by: Dylan Orzel <[email protected]>
e889d0a to
f0139f1
Compare
|
/retest |
|
@dorzel: Cannot trigger testing until a trusted user reviews the PR and leaves an DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
adambkaplan
left a comment
There was a problem hiding this comment.
/approve
In general this looks really good! I found a few opportunities for improvement - either through refactoring or adding additional tests to improve code coverage. That said, I would not block merge/lgtm on these items, they can all be addressed in a follow-up pull request.
| for _, toleration := range b.Build.Spec.Tolerations { | ||
| // validate Key | ||
| if errs := validation.IsQualifiedName(toleration.Key); errs != nil { | ||
| b.Build.Status.Reason = ptr.To(build.TolerationNotValid) | ||
| b.Build.Status.Message = ptr.To(fmt.Sprintf("Toleration key not valid: %v", strings.Join(errs, ", "))) | ||
| } | ||
| // validate Operator | ||
| if !((toleration.Operator == v1.TolerationOpExists) || (toleration.Operator == v1.TolerationOpEqual)) { | ||
| b.Build.Status.Reason = ptr.To(build.TolerationNotValid) | ||
| b.Build.Status.Message = ptr.To(fmt.Sprintf("Toleration operator not valid. Must be one of: '%v', '%v'", v1.TolerationOpExists, v1.TolerationOpEqual)) | ||
| } | ||
| // validate Value | ||
| if errs := validation.IsValidLabelValue(toleration.Value); errs != nil { | ||
| b.Build.Status.Reason = ptr.To(build.TolerationNotValid) | ||
| b.Build.Status.Message = ptr.To(fmt.Sprintf("Toleration value not valid: %v", strings.Join(errs, ", "))) | ||
| } | ||
| // validate Taint Effect, of which only "NoSchedule" is supported | ||
| if !((toleration.Effect) == "" || (toleration.Effect == v1.TaintEffectNoSchedule)) { | ||
| b.Build.Status.Reason = ptr.To(build.TolerationNotValid) | ||
| b.Build.Status.Message = ptr.To(fmt.Sprintf("Only the '%v' toleration effect is supported.", v1.TaintEffectNoSchedule)) | ||
| } | ||
| // validate TolerationSeconds, which should not be specified | ||
| if toleration.TolerationSeconds != nil { | ||
| b.Build.Status.Reason = ptr.To(build.TolerationNotValid) | ||
| b.Build.Status.Message = ptr.To("Specifying TolerationSeconds is not supported.") | ||
| } | ||
| } |
There was a problem hiding this comment.
Can't we simplify this a bit and re-use the logic in BuildRunTolerations?
| for _, toleration := range b.Build.Spec.Tolerations { | |
| // validate Key | |
| if errs := validation.IsQualifiedName(toleration.Key); errs != nil { | |
| b.Build.Status.Reason = ptr.To(build.TolerationNotValid) | |
| b.Build.Status.Message = ptr.To(fmt.Sprintf("Toleration key not valid: %v", strings.Join(errs, ", "))) | |
| } | |
| // validate Operator | |
| if !((toleration.Operator == v1.TolerationOpExists) || (toleration.Operator == v1.TolerationOpEqual)) { | |
| b.Build.Status.Reason = ptr.To(build.TolerationNotValid) | |
| b.Build.Status.Message = ptr.To(fmt.Sprintf("Toleration operator not valid. Must be one of: '%v', '%v'", v1.TolerationOpExists, v1.TolerationOpEqual)) | |
| } | |
| // validate Value | |
| if errs := validation.IsValidLabelValue(toleration.Value); errs != nil { | |
| b.Build.Status.Reason = ptr.To(build.TolerationNotValid) | |
| b.Build.Status.Message = ptr.To(fmt.Sprintf("Toleration value not valid: %v", strings.Join(errs, ", "))) | |
| } | |
| // validate Taint Effect, of which only "NoSchedule" is supported | |
| if !((toleration.Effect) == "" || (toleration.Effect == v1.TaintEffectNoSchedule)) { | |
| b.Build.Status.Reason = ptr.To(build.TolerationNotValid) | |
| b.Build.Status.Message = ptr.To(fmt.Sprintf("Only the '%v' toleration effect is supported.", v1.TaintEffectNoSchedule)) | |
| } | |
| // validate TolerationSeconds, which should not be specified | |
| if toleration.TolerationSeconds != nil { | |
| b.Build.Status.Reason = ptr.To(build.TolerationNotValid) | |
| b.Build.Status.Message = ptr.To("Specifying TolerationSeconds is not supported.") | |
| } | |
| } | |
| ok, reason, msg := BuildRunTolerations(b.Build.Spec.Tolerations) | |
| if !ok { | |
| b.Build.Status.Reason = reason | |
| b.Build.Status.Message = msg | |
| } | |
| return nil |
| shpgit "github.com/shipwright-io/build/pkg/git" | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
|
|
||
| corev1 "k8s.io/api/core/v1" |
There was a problem hiding this comment.
nit (not blocking): keep k8s.io imports grouped together above.
| - `spec.env` - Specifies additional environment variables that should be passed to the build container. Overrides any environment variables that are specified in the `Build` resource. The available variables depend on the tool used by the chosen build strategy. | ||
| - `spec.nodeSelector` - Specifies a selector which must match a node's labels for the build pod to be scheduled on that node. | ||
| - `spec.nodeSelector` - Specifies a selector which must match a node's labels for the build pod to be scheduled on that node. If nodeSelectors are specified in both a `Build` and `BuildRun`, `BuildRun` values take precedence. | ||
| - `spec.tolerations` - Specifies the tolerations for the build pod. Only `key`, `value`, and `operator` are supported. Only `NoSchedule` taint `effect` is supported. If tolerations are specified in both a `Build` and `BuildRun`, `BuildRun` values take precedence. |
| } | ||
|
|
||
| // BuildRunNodeSelector is used to validate nodeSelectors in the BuildRun object | ||
| func BuildRunNodeSelector(nodeSelector map[string]string) (bool, string, string) { |
There was a problem hiding this comment.
Ditto here - there is a refactoring opportunity to consolidate the logic into a single function.
There was a problem hiding this comment.
Not blocking - if possible, I'd love to have unit-style Ginkgo tests that cover all the possible validation exceptions.
This can be done in a follow-up pull request, as we currently don't have similar levels of unit test coverage for the node selector validations. We have test coverage elsewhere in this PR.
| if len(buildRun.Spec.NodeSelector) > 0 { | ||
| return resources.BuildRunBuildFieldOverrideForbidden, | ||
| "cannot use 'nodeSelector' override and 'buildSpec' simultaneously" | ||
| } | ||
|
|
||
| if len(buildRun.Spec.Tolerations) > 0 { | ||
| return resources.BuildRunBuildFieldOverrideForbidden, | ||
| "cannot use 'tolerations' override and 'buildSpec' simultaneously" | ||
| } |
There was a problem hiding this comment.
(not-blocking): would like to have unit test coverage here.
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: adambkaplan The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
|
@adambkaplan @SaschaSchwarze0 Is this ok for merge? I'll plan on addressing the comments above in #1770 |
Changes
Fixes #1636
Submitter Checklist
See the contributor guide
for details on coding conventions, github and prow interactions, and the code review process.
Release Notes