-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
add nodeselector and annotation build pod overrides and defaulters #11380
Conversation
[test] |
[testextended][extended:core(builds)] |
dcba9cd
to
6a9b95e
Compare
7cad91e
to
be5eab3
Compare
@openshift/api-review this is ready for review. @deads2k it could also use your review of how i'm initializing the config+controller now with this change since it's not actually an admission controller anymore, but it's still reading/using the admission plugin config. |
) | ||
|
||
func init() { | ||
admission.RegisterPlugin("BuildDefaults", func(c clientset.Interface, config io.Reader) (admission.Interface, error) { |
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.
to continue allowing old configs to run, do we need to register a no-op plugin with this name that logs a warning that the admission plugin is deprecated and returns nil? same question for BuildOverrides?
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.
I don't think so? we're still honoring the admission plugin config, we're just using it in a different codepath instead of as an admission controller.
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.
@deads2k, did we want to remove direct control of the admission chain in 1.4? if so, then no, I don't think we need to maintain the names as dummy plugins
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.
@deads2k, did we want to remove direct control of the admission chain in 1.4? if so, then no, I don't think we need to maintain the names as dummy plugins
I don't strictly object to doing it 1.4, but I hadn't set a timeframe. I'm not entirely sure what the validator will do on admission config for a mismatched name.
Also, it's pretty weird to drive config for a controller from the admission config. If we do end up with split processes, this is going to be a problem. This would have instantly broken.
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.
it's the only way I can see to make this change and maintain backwards compatibility unless you've got another idea, and @smarterclayton insisted the admission controllers should go away.
i'm going to need you guys to elaborate on this admission chain issue, i don't know what you're referring to.
} | ||
if !buildadmission.IsBuildPod(attributes) { | ||
// ApplyDefaults applies configured build defaults to a build pod | ||
func (b BuildDefaults) ApplyDefaults(pod *kapi.Pod) error { |
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 we take the build as an arg instead of parsing from the annotation? looks like all callers have the build handy (same question applies to ApplyOverrides, I guess)
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.
are the possible mutations to the build in applyBuildDefaults()
things we want persisted? if not, we could pass in buildCopy
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.
Right now we don't mutuate the actual build object in the admission controller, only the build as it is defined in the pod, so no, the mutations are not things we want persisted.
we can't just take the build as an arg because we also need the version info that is part of the serialized form of the build in the annotation. we have to keep the serialized version consistent before/after mutating the value.
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.
(also taking the build as an arg would mean warning callers that the object they pass in is being mutated but should not be persisted after that mutation, to retain the existing behavior, or we'd have to make a copy within this method)
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.
mm, ok.
@@ -17,4 +19,10 @@ type BuildOverridesConfig struct { | |||
// If user provided a label in their Build/BuildConfig with the same name as one in this | |||
// list, the user's label will be overwritten. | |||
ImageLabels []buildapi.ImageLabel | |||
|
|||
// nodeSelector is a selector which must be true for the build pod to fit on a node | |||
NodeSelector map[string]string |
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.
it looks like these defaults are applied key-by-key? so if I have a build with "nodeSelector": {"foo":"bar"}
and defaults of "nodeSelector":{"baz":"qux"}
, I'll get a pod with "nodeSelector":{"foo":"bar","baz":"qux"}
. Is that what we want for the defaulting?
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.
yes and yes. the intent is that an admin might set a default nodeSelector of "targetNode:defaultBuildNode" but the user might want to say "targetNode:highMemoryBuildNode" instead.
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.
That works as long as a build never needs a selector that omits one of the default keys. I'm not sure what level of granularity is needed.
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.
my view of the default nodeselector was "must be at least this specific".
there are probably competing use cases. but my assumption is most admins are going to set their system up with node labels like "buildNode=foo", and a defaulter that assigns the buildNode nodeselector key, which ensures that all builds will land on a node with at least the "buildNode" key, of some value.
if build.Spec.Strategy.SourceStrategy != nil { | ||
glog.V(5).Infof("Setting source strategy ForcePull to true in build %s/%s", build.Namespace, build.Name) | ||
build.Spec.Strategy.SourceStrategy.ForcePull = true | ||
for k, v := range b.config.NodeSelector { |
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.
do we want to do the same thing as the project nodeSelector and error if the build already specifies a conflicting key/value, or just stomp it?
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.
well obviously i want to stomp it :)
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.
if they create a build with a nodeSelector, is it more confusing to fail build pod creation with errors about disallowed elements in the nodeSelector, or to create build pods that apparently ignore the nodeSelector they set? I'm really not sure...
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.
i'm not sure either. i kind of prefer the path where the build succeeds for them, but i agree they aren't necessarily getting what they expected nor will they necessarily understand why (assuming they even notice that it didn't run on the node they expected)
return nil | ||
for k, v := range b.config.Annotations { | ||
glog.V(5).Infof("Adding override annotation %s=%s to build pod %s/%s", k, v, pod.Namespace, pod.Name) | ||
pod.Annotations[k] = v |
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.
same question about erroring or stomping on conflicting key/value
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.
same answer :)
It was probably a mistake to assume that policy config related to
things that happened in admission config would continue to be
implemented the same way. Admission is an implementation detail.
When we change our process model we will already have to deal with
existing config continuing working for some period of time.
In any case we should not break config on disk just because
implementation changed.
|
d4163ac
to
b121646
Compare
flake #11240 |
@smarterclayton @liggitt @deads2k The open questions on this PR appear to be:
And these questions may have different answers for nodselectors vs annotations (it's hard to imagine we'd want to fail all user builds that have an annotation set, when a non-conflicting override annotation is defined. Ditto for ignoring default annotations just because the user set some random annotation on their build). We need this questions resolved asap so this can land for 3.4 feature cut. Especially if the answer is that the current choices are not acceptable and the behavior needs to be changed. |
flake #11240 |
@openshift/api-review bump for questions in #11380 (comment). I'd prefer the route that does not fail the build, especially if we can provide a message that their selection was overridden by cluster settings. |
Suggestions:
|
so I think the only change from the current state is making the nodeSelector defaulting all-or-nothing follow up issue to try to surface stompage to the user would be nice but non-blocking |
Yeah definitely non-blocking. |
yeah, working on that change right now. |
default selector is now all or nothing. @csrwng ptal. |
(and per @liggitt's suggestion, map{} results in no defaults being applied. nil results in defaults being applied. so users can avoid the defaults if they want) |
flake #11452 |
@jupierce ptal |
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.
LGTM.
// build if the specified variables do not exist on the build | ||
Env []kapi.EnvVar `json:"env,omitempty"` | ||
|
||
// SourceStrategyDefaults are default values that apply to builds using the | ||
// sourceStrategyDefaults are default values that apply to builds using the | ||
// source strategy. | ||
SourceStrategyDefaults *SourceStrategyDefaultsConfig `json:"sourceStrategyDefaults,omitempty"` | ||
|
||
// ImageLabels is a list of docker labels that are applied to the resulting image. |
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.
You lowercased every attribute name in doc except this one.
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.
result of rebase/merge. but yeah i'll lowercase it.
defer configData.Close() | ||
|
||
configBytes, err := ioutil.ReadAll(configData) | ||
|
||
err = configlatest.ReadYAMLInto(configBytes, config) |
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.
ReadYAMLFileInto instead?
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.
thanks. i just moved this code from somewhere else but it makes sense to update it to use that instead.
} | ||
return pod, nil | ||
} | ||
|
||
// SetBuild encodes a build object and sets it in a pod's BUILD environment variable |
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.
SetBuildInPod instead of SetBuild
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.
yup
} | ||
return hasBuildAnnotation(pod) && hasBuildEnvVar(pod) | ||
} | ||
|
||
// GetBuild returns a build object encoded in a pod's BUILD environment variable along with |
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.
GetBuildFromPod instead of GetBuild
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.
yup thanks
if err != nil { | ||
return nil, unversioned.GroupVersion{}, err | ||
} | ||
func GetBuildFromPod(pod *kapi.Pod) (*buildapi.Build, unversioned.GroupVersion, error) { | ||
build, version, err := getBuildFromPod(pod) |
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.
Could we not collapse the public/private methods into one?
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.
yeah, will do.
|
||
err = setBuildInPod(build, pod, groupVersion) | ||
func SetBuildInPod(pod *kapi.Pod, build *buildapi.Build, groupVersion unversioned.GroupVersion) error { | ||
err := setBuildInPod(build, pod, groupVersion) |
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.
Could we not collapse the public/private methods into one?
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.
yup
return err | ||
} | ||
|
||
func SetPodLogLevelFromBuild(pod *kapi.Pod, build *buildapi.Build) error { |
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.
Method name in documentation needs update.
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.
yup
having some trouble getting nodeSelector to respect empty map values instead of nil values. otherwise i think this is done (addressed justin's comments) will take another crack at getting nodeSelector to stop turning map{} into nil, tomorrow. |
111e2e3
to
1fad6cf
Compare
ok got the nil vs {} behavior working thanks to @smarterclayton's tip about protobuf logic. @smarterclayton not sure if you want to do a general review of this (or double check the api changes for the optional map at least). still hoping to merge this before monday morning. |
Evaluated for origin testextended up to c1e03fe |
Evaluated for origin test up to c1e03fe |
continuous-integration/openshift-jenkins/test SUCCESS (https://ci.openshift.redhat.com/jenkins/job/test_pr_origin/10498/) (Base Commit: 2047a73) |
continuous-integration/openshift-jenkins/testextended SUCCESS (https://ci.openshift.redhat.com/jenkins/job/test_pr_origin_extended/663/) (Base Commit: 2047a73) (Extended Tests: core(builds)) |
[merge] |
continuous-integration/openshift-jenkins/merge SUCCESS (https://ci.openshift.redhat.com/jenkins/job/test_pr_origin/10498/) (Image: devenv-rhel7_5236) |
Evaluated for origin merge up to c1e03fe |
You're tagged. On Thu, Oct 20, 2016 at 6:13 PM, OpenShift Bot [email protected]
|
@smarterclayton also an already merged PR.... |
This PR allows cluster admins to set override nodeselectors and annotations(primarily intended to be used for tolerations) that will be applied to build pods.
Admins can also set default nodeselectors/annotations which will be applied to the build pod only if the pod does not contain those selector/annotation keys already.
Lastly it also allows users to set nodeselectors on their buildconfig, those nodeselectors will be applied to the build pod (but be overridden by the admin-configured override values)
fixes #9971
fixes bug 1277432