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

Use NodeWrapper to directly initialize nodes with labels #92514

Merged

Conversation

nodo
Copy link
Contributor

@nodo nodo commented Jun 25, 2020

What type of PR is this?
/kind cleanup

What this PR does / why we need it:
In #92074 (comment), we discussed the possibility to use NodeWrapper in the scheduler integration tests so that we have more flexibility when creating the nodes. For instance we can create nodes directly with a label.

Does this PR introduce a user-facing change?:

NONE

@k8s-ci-robot k8s-ci-robot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. release-note-none Denotes a PR that doesn't merit a release note. kind/cleanup Categorizes issue or PR as related to cleaning up code, process, or technical debt. size/M Denotes a PR that changes 30-99 lines, ignoring generated files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. needs-sig Indicates an issue or PR lacks a `sig/foo` label and requires one. needs-priority Indicates a PR lacks a `priority/foo` label and requires one. needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Jun 25, 2020
@k8s-ci-robot
Copy link
Contributor

Hi @nodo. Thanks for your PR.

I'm waiting for a kubernetes member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

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.

@k8s-ci-robot k8s-ci-robot added area/test sig/scheduling Categorizes an issue or PR as relevant to SIG Scheduling. sig/testing Categorizes an issue or PR as relevant to SIG Testing. and removed needs-sig Indicates an issue or PR lacks a `sig/foo` label and requires one. labels Jun 25, 2020
@nodo
Copy link
Contributor Author

nodo commented Jun 25, 2020

@alculquicondor @Huang-Wei I have opened this PR as WIP to get your feedback about the cleanup.

In particular:

  • What do you think about keeping initNode and use it in the integration tests, so that we can easily initialize nodes with certain default?
  • I deliberately created createNodeFromWrapper to keep this PR small in scope, however, do you think we should change createNode to use NodeWrapper as well?
  • Also, if you agree I think we should also change createNodes to use createNodeFromWrapper, but I wanted to hear from you first.

@Huang-Wei
Copy link
Member

  • What do you think about keeping initNode and use it in the integration tests, so that we can easily initialize nodes with certain default?

There are just 2 callers using initNode():

⇒  ag initNode test/integration/scheduler
test/integration/scheduler/util.go
151:// initNode returns a node with the given resource list and images. If 'res' is nil, a predefined amount of
153:func initNode(name string, res *v1.ResourceList, images []v1.ContainerImage) *v1.Node {
174:	return cs.CoreV1().Nodes().Create(context.TODO(), initNode(name, res, nil), metav1.CreateOptions{})
179:	return cs.CoreV1().Nodes().Create(context.TODO(), initNode(name, res, images), metav1.CreateOptions{})

I think it makes more sense to eliminate it by migrating to NodeWrapper.

  • I deliberately created createNodeFromWrapper to keep this PR small in scope, however, do you think we should change createNode to use NodeWrapper as well?
  • Also, if you agree I think we should also change createNodes to use createNodeFromWrapper, but I wanted to hear from you first.

My 2 cents is to use NodeWrapper inside createNode() & createNodes() to craft the Node(s) object first, and then call API server to create them.

return n
}

func (n *NodeWrapper) Capacity(capacity v1.ResourceList) *NodeWrapper {
Copy link
Member

Choose a reason for hiding this comment

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

I'd suggest using paired key-value string (or just values) instead of v1.ResourceList here. v1.ResourceList is too low-level API, which is friendly for new-users to build, and if you look at the usage of createNode(), there is only one caller specifying a non-nil resourceList.

Option 1:

func (n *NodeWrapper) Capacity(pairs ...string) *NodeWrapper {
	res := v1.ResourceList{}
	for i := 0; i < len(pairs); i += 2 {
		k, v := pairs[i], pairs[i+1]
		res[v1.ResourceName(k)] = resource.MustParse(v)
	}
	n.Status.Capacity, n.Status.Allocatable = res, res
	return n
}

Option 2:

func (n *NodeWrapper) Capacity(cpu, mem string, pods int64) *NodeWrapper {
	res := v1.ResourceList{
		v1.ResourcePods: *resource.NewQuantity(pods, resource.DecimalSI),
	}
	if len(cpu) != 0 {
		res[v1.ResourceCPU] = resource.MustParse(cpu)
	}
	if len(mem) != 0 {
		res[v1.ResourceMemory] = resource.MustParse(mem)
	}
	n.Status.Capacity, n.Status.Allocatable = res, res
	return n
}

Copy link
Member

Choose a reason for hiding this comment

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

I would prefer Option 1, but accepting map[string]string, instead of the pairs of lists

Copy link
Member

Choose a reason for hiding this comment

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

yup, option 1 is also my preference.

Copy link
Member

Choose a reason for hiding this comment

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

nit: map[v1.ResourceName]string should be less error-prone.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea, I will make the change!

return n
}

func (n *NodeWrapper) Images(images []v1.ContainerImage) *NodeWrapper {
Copy link
Member

Choose a reason for hiding this comment

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

Similar as above, this function should take the burden of building []v1.ContainerImage inside, so users just pass in image names, etc.

Copy link
Member

Choose a reason for hiding this comment

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

+1

@Huang-Wei
Copy link
Member

/ok-to-test

@k8s-ci-robot k8s-ci-robot added ok-to-test Indicates a non-member PR verified by an org member that is safe to test. and removed needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Jun 25, 2020
test/integration/scheduler/util.go Outdated Show resolved Hide resolved
test/integration/scheduler/util.go Show resolved Hide resolved
test/integration/scheduler/predicates_test.go Outdated Show resolved Hide resolved
return n
}

func (n *NodeWrapper) Images(images []v1.ContainerImage) *NodeWrapper {
Copy link
Member

Choose a reason for hiding this comment

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

+1

return n
}

func (n *NodeWrapper) Capacity(capacity v1.ResourceList) *NodeWrapper {
Copy link
Member

Choose a reason for hiding this comment

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

I would prefer Option 1, but accepting map[string]string, instead of the pairs of lists

@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Jun 28, 2020
@nodo nodo changed the title WIP: Use NodeWrapper to directly initialize node with labels Use NodeWrapper to directly initialize nodes with labels Jun 29, 2020
@k8s-ci-robot k8s-ci-robot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jun 29, 2020
@nodo
Copy link
Contributor Author

nodo commented Jun 29, 2020

@alculquicondor @Huang-Wei Thanks a lot for the feedback. I have done some changes and cleaned up the rest of the integration tests. Feel free to review it when you have time.

// Capacity sets the capacity and the allocatable resources of the inner node.
// Each entry in `resources` corresponds to a resource name and its quantity.
func (n *NodeWrapper) Capacity(resources map[v1.ResourceName]string) *NodeWrapper {
res := v1.ResourceList{}
Copy link
Member

Choose a reason for hiding this comment

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

nit: res := make(v1.ResourceList)

@@ -194,6 +167,10 @@ func createNodes(cs clientset.Interface, prefix string, res *v1.ResourceList, nu
return nodes[:], nil
}

func defaultNodeWrapper() *st.NodeWrapper {
Copy link
Member

Choose a reason for hiding this comment

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

I prefer we don't hide the details in this function.
Each caller should define the resources they need in their tests.
I know this goes against having repeated code, but this is actually a good practice when writing tests.

Copy link
Member

@Huang-Wei Huang-Wei Jun 29, 2020

Choose a reason for hiding this comment

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

IMO v1.ResourcePods is a bit special, unlike zero cpu/mem, a zero Pods number would simply fail a NodeFit plugin in any case. Which is usually not what we want. So having a default v1.ResourcePods looks good to me. However, I'd prefer to set the default value on NodesWrapper side - which would also benefit UT (I did that way in another PR: https://github.com/kubernetes/kubernetes/pull/92571/files?file-filters%5B%5D=.go#diff-1cea28cd0be3cdbab57f5dc287dc98c0R427-R438)

If you do so, this function is not needed anymore - you can just replace callers of defaultNodeWrapper() directly to st.MakeNode().

Copy link
Member

Choose a reason for hiding this comment

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

Sg, we can just do res[pods] = 32 at the beginning of the function, and the loop would override it.

Copy link
Member

Choose a reason for hiding this comment

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

Yup, that would save one if block.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good, I have made the change.

Copy link
Member

@Huang-Wei Huang-Wei left a comment

Choose a reason for hiding this comment

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

Just some nits.

for name, value := range resources {
res[name] = resource.MustParse(value)
}
n.Status.Capacity, n.Status.Allocatable = res, res
Copy link
Member

@Huang-Wei Huang-Wei Jun 29, 2020

Choose a reason for hiding this comment

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

would be good to ensure v1.ResourcePods is always present, otherwise some pod scheduling may fail due to "no Pods quota".

FYI: I put it in https://github.com/kubernetes/kubernetes/pull/92571/files?file-filters%5B%5D=.go#diff-1cea28cd0be3cdbab57f5dc287dc98c0R427-R438

// Images sets the images of the inner node. Each entry in `images` corresponds
// to an image name and its size in bytes.
func (n *NodeWrapper) Images(images map[string]int64) *NodeWrapper {
containerImages := []v1.ContainerImage{}
Copy link
Member

Choose a reason for hiding this comment

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

nit: var containerImages []v1.ContainerImage.

@@ -194,6 +167,10 @@ func createNodes(cs clientset.Interface, prefix string, res *v1.ResourceList, nu
return nodes[:], nil
}

func defaultNodeWrapper() *st.NodeWrapper {
Copy link
Member

@Huang-Wei Huang-Wei Jun 29, 2020

Choose a reason for hiding this comment

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

IMO v1.ResourcePods is a bit special, unlike zero cpu/mem, a zero Pods number would simply fail a NodeFit plugin in any case. Which is usually not what we want. So having a default v1.ResourcePods looks good to me. However, I'd prefer to set the default value on NodesWrapper side - which would also benefit UT (I did that way in another PR: https://github.com/kubernetes/kubernetes/pull/92571/files?file-filters%5B%5D=.go#diff-1cea28cd0be3cdbab57f5dc287dc98c0R427-R438)

If you do so, this function is not needed anymore - you can just replace callers of defaultNodeWrapper() directly to st.MakeNode().

@nodo nodo force-pushed the scheduler-integration-test-cleanup branch from df00fed to b9a36c3 Compare June 30, 2020 14:56
@@ -390,3 +391,27 @@ func (n *NodeWrapper) Label(k, v string) *NodeWrapper {
n.Labels[k] = v
return n
}

// Capacity sets the capacity and the allocatable resources of the inner node.
// Each entry in `resources` corresponds to a resource name and its quantity.
Copy link
Member

Choose a reason for hiding this comment

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

Indicate that sets 32 pods limit by default

@@ -1841,7 +1842,7 @@ func initTestSchedulerForFrameworkTest(t *testing.T, testCtx *testutils.TestCont
go testCtx.Scheduler.Run(testCtx.Ctx)

if nodeCount > 0 {
_, err := createNodes(testCtx.ClientSet, "test-node", nil, nodeCount)
_, err := createNodes(testCtx.ClientSet, "test-node", st.MakeNode(), nodeCount)
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't MakeNode call Capacity(nil) or something like that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Totally! My bad, let me fix it.

Using NodeWrapper in the integration tests gives more flexibility when
creating nodes. For instance, tests can create nodes with labels or
with a specific sets of resources.

Also, NodeWrapper initialises a node with a capacity of 32 pods, which
can be overridden by the caller. This makes sure that a node is usable
as soon as it is created.
@nodo nodo force-pushed the scheduler-integration-test-cleanup branch from b9a36c3 to 2e1042f Compare June 30, 2020 15:18
Copy link
Member

@alculquicondor alculquicondor left a comment

Choose a reason for hiding this comment

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

/lgtm

Thanks for the cleanup!

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Jun 30, 2020
@k8s-ci-robot
Copy link
Contributor

k8s-ci-robot commented Jun 30, 2020

@nodo: The following test failed, say /retest to rerun all failed tests:

Test name Commit Details Rerun command
pull-kubernetes-e2e-kind-ipv6 2e1042f link /test pull-kubernetes-e2e-kind-ipv6

Full PR test history. Your PR dashboard. Please help us cut down on flakes by linking to an open issue when you hit one in your PR.

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. I understand the commands that are listed here.

@Huang-Wei
Copy link
Member

/approve
/retest

Thanks @nodo .

@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: Huang-Wei, nodo

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

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jun 30, 2020
@k8s-ci-robot k8s-ci-robot merged commit ff1d6b3 into kubernetes:master Jun 30, 2020
@k8s-ci-robot k8s-ci-robot added this to the v1.19 milestone Jun 30, 2020
@nodo nodo deleted the scheduler-integration-test-cleanup branch July 1, 2020 06:43
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. area/test cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/cleanup Categorizes issue or PR as related to cleaning up code, process, or technical debt. lgtm "Looks good to me", indicates that a PR is ready to be merged. needs-priority Indicates a PR lacks a `priority/foo` label and requires one. ok-to-test Indicates a non-member PR verified by an org member that is safe to test. release-note-none Denotes a PR that doesn't merit a release note. sig/scheduling Categorizes an issue or PR as relevant to SIG Scheduling. sig/testing Categorizes an issue or PR as relevant to SIG Testing. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants