Skip to content
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 default podAntiAffinity to PodTemplateSpec #319

Merged
merged 1 commit into from Mar 7, 2022

Conversation

fossedihelm
Copy link
Contributor

What this PR does / why we need it:
Default PodTemplateSpec now has a podAntiAffinity that makes pod replicas to be scheduled on different nodes.
This affinity will be merged with the requested one.
The merge affect nodeAffinity, podAffinity, podAntiaffinity, tolerations, and nodeSelector
Which issue(s) this PR fixes:

Fixes #

Special notes for your reviewer:

Release note:

TemplateValidator has a podAntiAffinity that makes pod replicas to be scheduled on different nodes.

@kubevirt-bot kubevirt-bot added dco-signoff: yes Indicates the PR's author has DCO signed all their commits. release-note Denotes a PR that will be considered when it comes time to generate release notes. labels Mar 2, 2022
Copy link

@iholder101 iholder101 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @fossedihelm! Great job! 👍

Overall looks great.


Small note about my perspective on comments:
IMO comments should be used to explain complicated code that cannot be simplified easily. In such cases comments need to expose as less implementation details as possible, i.e. explain what the codes does and not how it does it. Also, comments should never point out the obvious (explain something that is immediately obvious from looking at the code).

The reason I'm even mentioning this is because comments can easily do more harm than good. The danger is that the code changes but the comments stay outdated. This can lead to a very confusing code.

Example for what I'm referring to:

// Iterating over Pod's node selector
for i := Pod.NodeSelector { ... }

What do you think? :)

internal/operands/template-validator/reconcile.go Outdated Show resolved Hide resolved
internal/operands/template-validator/reconcile.go Outdated Show resolved Hide resolved
internal/operands/template-validator/reconcile.go Outdated Show resolved Hide resolved
@fossedihelm
Copy link
Contributor Author

@iholder-redhat Thanks for your review. Agree with your perspective about the comments. In this case, they aren't necessary. They are leftovers because, in the first implementation, I wrote all the merge stuff inside the injectPlacementMetadata function and they help reading the different blocks, due to its cyclomatic complexity. Having created specific function for each block comments are not necessary anymore, as you said. I will remove them. 🙂

@fossedihelm
Copy link
Contributor Author

@iholder-redhat I pushed the requested changes. I also push a change on validator_test file: it is the fix of the failing tests. Thanks

@fossedihelm fossedihelm force-pushed the template_anti_affinity branch 4 times, most recently from 7d82980 to 6dee28f Compare March 3, 2022 07:29
@fossedihelm
Copy link
Contributor Author

/test ci/prow/e2e-functests

@openshift-ci
Copy link

openshift-ci bot commented Mar 3, 2022

@fossedihelm: The specified target(s) for /test were not found.
The following commands are available to trigger required jobs:

  • /test e2e-functests
  • /test e2e-single-node-functests
  • /test e2e-upgrade-functests
  • /test images
  • /test unittests

Use /test all to run all jobs.

In response to this:

/test ci/prow/e2e-functests

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/test-infra repository.

@kubevirt-bot
Copy link
Contributor

@fossedihelm: No presubmit jobs available for kubevirt/ssp-operator@master

In response to this:

/test ci/prow/e2e-functests

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/test-infra repository.

@fossedihelm
Copy link
Contributor Author

/test e2e-functests

@kubevirt-bot
Copy link
Contributor

@fossedihelm: No presubmit jobs available for kubevirt/ssp-operator@master

In response to this:

/test e2e-functests

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/test-infra repository.

@fossedihelm
Copy link
Contributor Author

/retest

@fossedihelm
Copy link
Contributor Author

/test e2e-functests

@kubevirt-bot
Copy link
Contributor

@fossedihelm: No presubmit jobs available for kubevirt/ssp-operator@master

In response to this:

/test e2e-functests

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/test-infra repository.

Copy link
Collaborator

@akrejcir akrejcir left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR.
My comments are mostly about code style. You can ignore them, if you want to. Except the last one about using client in reconcile_test.go, that would be better.

return
}
if podSpec == nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check can be removed, because the calling function never passes nil as podSpec. But if you want to keep this check, it can return on nil, because the function will be noop.

nodePlacement := componentConfig.Placement
if nodePlacement.NodeSelector == nil {
nodePlacement.NodeSelector = make(map[string]string)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This if block can be safely removed. Iterating over nil map is the same as iterating over an empty map.

if podSpec.Affinity == nil {
podSpec.Affinity = nodePlacement.Affinity.DeepCopy()
} else {
if nodePlacement.Affinity.NodeAffinity != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move these != nil checks inside the respective functions?

}
}

if len(nodePlacement.Tolerations) != 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole if block can be replaced with one line:

podSpec.Tolerations = append(podSpec.Tolerations, nodePlacement.Tolerations...)

Appending to a nil slice allocates a new slice, so you don't need to check if it is nil, and appending an empty slice is a noop.

func mergeNodeAffinity(currentAffinity *v1.Affinity, configNodeAffinity *v1.NodeAffinity) {
if currentAffinity.NodeAffinity == nil {
currentAffinity.NodeAffinity = configNodeAffinity.DeepCopy()
} else {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add early return instead of else block?

},
},
},
TopologyKey: "kubernetes.io/hostname",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This string is used in many places, can you extract it to a constant?


var setPodAntiAffinity = func(request *common.Request) {
request.Instance.Spec.TemplateValidator.Placement.Affinity.PodAntiAffinity = antiAffinity
return
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This return can be removed.

}
var setPodAffinity = func(request *common.Request) {
request.Instance.Spec.TemplateValidator.Placement.Affinity.PodAffinity = podAffinity
return
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This return can be removed.

}
var setNodeAffinity = func(request *common.Request) {
request.Instance.Spec.TemplateValidator.Placement.Affinity.NodeAffinity = nodeAffinity
return
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This return can be removed.

}
res, err := operand.Reconcile(&request)
Expect(err).ToNot(HaveOccurred())
deploymentResult := res[4]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use the client to get the deployment instead of accessing the result of the call? The resource in result is only used for getting names and logging.

You can use:

deployment := &apps.Deployment{}
key := client.ObjectKeyFromObject(newDeployment(namespace, replicas, "test-img"))
Expect(request.Client.Get(request.Context, key, deployment)).To(Succeed())

@fossedihelm
Copy link
Contributor Author

@akrejcir Thanks for your review. It was constructive and instructive. I'm going to address your suggestions. Thanks

Default PodTemplateSpec now has a podAntiAffinity that makes replicas to be scheduled on different node.
This affinity will be merged with the requested one.
The merge affect nodeAffinity, podAffinity, podAntiaffinity, tolerations and nodeSelector

Signed-off-by: fossedihelm <ffossemo@redhat.com>
@sonarcloud
Copy link

sonarcloud bot commented Mar 4, 2022

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 1 Code Smell

No Coverage information No Coverage information
0.0% 0.0% Duplication

@fossedihelm
Copy link
Contributor Author

/retest

@akrejcir
Copy link
Collaborator

akrejcir commented Mar 4, 2022

/lgtm

@kubevirt-bot kubevirt-bot added the lgtm Indicates that a PR is ready to be merged. label Mar 4, 2022
@akrejcir
Copy link
Collaborator

akrejcir commented Mar 4, 2022

I've noticed that the commit message is not split into a title and body. When I run git branch -vv it shows the whole commit message as title.
But you don't need to change it now.

@fossedihelm
Copy link
Contributor Author

I've noticed that the commit message is not split into a title and body. When I run git branch -vv it shows the whole commit message as title. But you don't need to change it now.

Thanks. I think I forgot a \n. 🙁

@akrejcir
Copy link
Collaborator

akrejcir commented Mar 7, 2022

/approve

@kubevirt-bot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: akrejcir

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@kubevirt-bot kubevirt-bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Mar 7, 2022
@kubevirt-bot kubevirt-bot merged commit 0e5bfec into kubevirt:master Mar 7, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. dco-signoff: yes Indicates the PR's author has DCO signed all their commits. lgtm Indicates that a PR is ready to be merged. release-note Denotes a PR that will be considered when it comes time to generate release notes. size/L
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants