Skip to content

Commit 9872b17

Browse files
authored
Merge pull request kubernetes#127828 from macsko/add_template_parameters_to_createnodesop_in_scheduler_perf
Add template parameters to createNodesOp in scheduler_perf
2 parents 3694a02 + 6dbb5d8 commit 9872b17

File tree

4 files changed

+93
-81
lines changed

4 files changed

+93
-81
lines changed

test/integration/framework/util.go

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,14 @@ import (
2222
"context"
2323
"fmt"
2424
"testing"
25-
"time"
2625

2726
v1 "k8s.io/api/core/v1"
2827
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29-
"k8s.io/apimachinery/pkg/util/wait"
3028
clientset "k8s.io/client-go/kubernetes"
3129
"k8s.io/klog/v2"
3230
nodectlr "k8s.io/kubernetes/pkg/controller/nodelifecycle"
3331
)
3432

35-
const (
36-
// poll is how often to Poll pods, nodes and claims.
37-
poll = 2 * time.Second
38-
39-
// singleCallTimeout is how long to try single API calls (like 'get' or 'list'). Used to prevent
40-
// transient failures from failing tests.
41-
singleCallTimeout = 5 * time.Minute
42-
)
43-
4433
// CreateNamespaceOrDie creates a namespace.
4534
func CreateNamespaceOrDie(c clientset.Interface, baseName string, t testing.TB) *v1.Namespace {
4635
ns := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: baseName}}
@@ -59,22 +48,6 @@ func DeleteNamespaceOrDie(c clientset.Interface, ns *v1.Namespace, t testing.TB)
5948
}
6049
}
6150

62-
// waitListAllNodes is a wrapper around listing nodes supporting retries.
63-
func waitListAllNodes(c clientset.Interface) (*v1.NodeList, error) {
64-
var nodes *v1.NodeList
65-
var err error
66-
if wait.PollImmediate(poll, singleCallTimeout, func() (bool, error) {
67-
nodes, err = c.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
68-
if err != nil {
69-
return false, err
70-
}
71-
return true, nil
72-
}) != nil {
73-
return nodes, err
74-
}
75-
return nodes, nil
76-
}
77-
7851
// Filter filters nodes in NodeList in place, removing nodes that do not
7952
// satisfy the given condition
8053
func Filter(nodeList *v1.NodeList, fn func(node v1.Node) bool) {

test/integration/framework/perf_utils.go renamed to test/integration/scheduler_perf/node_util.go

Lines changed: 62 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -14,48 +14,70 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package framework
17+
package benchmark
1818

1919
import (
2020
"context"
2121
"fmt"
22+
"time"
2223

2324
v1 "k8s.io/api/core/v1"
24-
"k8s.io/apimachinery/pkg/api/resource"
2525
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2626
"k8s.io/apimachinery/pkg/util/rand"
27+
"k8s.io/apimachinery/pkg/util/wait"
2728
clientset "k8s.io/client-go/kubernetes"
2829
"k8s.io/klog/v2"
2930
testutils "k8s.io/kubernetes/test/utils"
3031
)
3132

3233
const (
33-
retries = 5
34+
// createNodeRetries defines number of retries when creating nodes.
35+
createNodeRetries = 5
36+
37+
// pollingInterval defines how often to poll when waiting for nodes to be created.
38+
pollingInterval = 2 * time.Second
39+
40+
// singleCallTimeout is how long to try single API calls (like 'get' or 'list'). Used to prevent
41+
// transient failures from failing tests.
42+
singleCallTimeout = 5 * time.Minute
3443
)
3544

45+
// NodeTemplate is responsible for creating a v1.Node instance that is ready
46+
// to be sent to the API server.
47+
type NodeTemplate interface {
48+
// GetNodeTemplate returns a node template for one out of many different nodes.
49+
// It gets called multiple times with an increasing index and a fixed count parameters.
50+
// This number can, but doesn't have to be, used to modify parts of the node spec like
51+
// for example a named reference to some other object.
52+
GetNodeTemplate(index, count int) (*v1.Node, error)
53+
}
54+
55+
// StaticNodeTemplate returns an implementation of NodeTemplate for a fixed node that is the same regardless of the index.
56+
func StaticNodeTemplate(node *v1.Node) NodeTemplate {
57+
return (*staticNodeTemplate)(node)
58+
}
59+
60+
type staticNodeTemplate v1.Node
61+
62+
// GetNodeTemplate implements [NodeTemplate.GetNodeTemplate] by returning the same node
63+
// for each call.
64+
func (s *staticNodeTemplate) GetNodeTemplate(index, count int) (*v1.Node, error) {
65+
return (*v1.Node)(s), nil
66+
}
67+
3668
// IntegrationTestNodePreparer holds configuration information for the test node preparer.
3769
type IntegrationTestNodePreparer struct {
3870
client clientset.Interface
3971
countToStrategy []testutils.CountToStrategy
40-
nodeNamePrefix string
41-
nodeSpec *v1.Node
72+
nodeTemplate NodeTemplate
4273
}
4374

44-
// NewIntegrationTestNodePreparer creates an IntegrationTestNodePreparer configured with defaults.
45-
func NewIntegrationTestNodePreparer(client clientset.Interface, countToStrategy []testutils.CountToStrategy, nodeNamePrefix string) testutils.TestNodePreparer {
75+
// NewIntegrationTestNodePreparer creates an IntegrationTestNodePreparer with a given nodeTemplate.
76+
func NewIntegrationTestNodePreparer(client clientset.Interface, countToStrategy []testutils.CountToStrategy, nodeTemplate NodeTemplate) testutils.TestNodePreparer {
4677
return &IntegrationTestNodePreparer{
4778
client: client,
4879
countToStrategy: countToStrategy,
49-
nodeNamePrefix: nodeNamePrefix,
50-
}
51-
}
52-
53-
// NewIntegrationTestNodePreparerWithNodeSpec creates an IntegrationTestNodePreparer configured with nodespec.
54-
func NewIntegrationTestNodePreparerWithNodeSpec(client clientset.Interface, countToStrategy []testutils.CountToStrategy, nodeSpec *v1.Node) testutils.TestNodePreparer {
55-
return &IntegrationTestNodePreparer{
56-
client: client,
57-
countToStrategy: countToStrategy,
58-
nodeSpec: nodeSpec,
80+
nodeTemplate: nodeTemplate,
5981
}
6082
}
6183

@@ -67,30 +89,13 @@ func (p *IntegrationTestNodePreparer) PrepareNodes(ctx context.Context, nextNode
6789
}
6890

6991
klog.Infof("Making %d nodes", numNodes)
70-
baseNode := &v1.Node{
71-
ObjectMeta: metav1.ObjectMeta{
72-
GenerateName: p.nodeNamePrefix,
73-
},
74-
Status: v1.NodeStatus{
75-
Capacity: v1.ResourceList{
76-
v1.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
77-
v1.ResourceCPU: resource.MustParse("4"),
78-
v1.ResourceMemory: resource.MustParse("32Gi"),
79-
},
80-
Phase: v1.NodeRunning,
81-
Conditions: []v1.NodeCondition{
82-
{Type: v1.NodeReady, Status: v1.ConditionTrue},
83-
},
84-
},
85-
}
86-
87-
if p.nodeSpec != nil {
88-
baseNode = p.nodeSpec
89-
}
9092

9193
for i := 0; i < numNodes; i++ {
92-
var err error
93-
for retry := 0; retry < retries; retry++ {
94+
baseNode, err := p.nodeTemplate.GetNodeTemplate(i, numNodes)
95+
if err != nil {
96+
return fmt.Errorf("failed to get node template: %w", err)
97+
}
98+
for retry := 0; retry < createNodeRetries; retry++ {
9499
// Create nodes with the usual kubernetes.io/hostname label.
95100
// For that we need to know the name in advance, if we want to
96101
// do it in one request.
@@ -114,7 +119,7 @@ func (p *IntegrationTestNodePreparer) PrepareNodes(ctx context.Context, nextNode
114119
}
115120
}
116121

117-
nodes, err := waitListAllNodes(p.client)
122+
nodes, err := waitListAllNodes(ctx, p.client)
118123
if err != nil {
119124
return fmt.Errorf("listing nodes: %w", err)
120125
}
@@ -133,7 +138,7 @@ func (p *IntegrationTestNodePreparer) PrepareNodes(ctx context.Context, nextNode
133138
func (p *IntegrationTestNodePreparer) CleanupNodes(ctx context.Context) error {
134139
// TODO(#93794): make CleanupNodes only clean up the nodes created by this
135140
// IntegrationTestNodePreparer to make this more intuitive.
136-
nodes, err := waitListAllNodes(p.client)
141+
nodes, err := waitListAllNodes(ctx, p.client)
137142
if err != nil {
138143
klog.Fatalf("Error listing nodes: %v", err)
139144
}
@@ -146,3 +151,19 @@ func (p *IntegrationTestNodePreparer) CleanupNodes(ctx context.Context) error {
146151
}
147152
return errRet
148153
}
154+
155+
// waitListAllNodes is a wrapper around listing nodes supporting retries.
156+
func waitListAllNodes(ctx context.Context, c clientset.Interface) (*v1.NodeList, error) {
157+
var nodes *v1.NodeList
158+
var err error
159+
if wait.PollUntilContextTimeout(ctx, pollingInterval, singleCallTimeout, true, func(ctx context.Context) (bool, error) {
160+
nodes, err = c.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
161+
if err != nil {
162+
return false, err
163+
}
164+
return true, nil
165+
}) != nil {
166+
return nodes, err
167+
}
168+
return nodes, nil
169+
}

test/integration/scheduler_perf/scheduler_perf.go

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1702,21 +1702,15 @@ func getNodePreparer(prefix string, cno *createNodesOp, clientset clientset.Inte
17021702
nodeStrategy = cno.UniqueNodeLabelStrategy
17031703
}
17041704

1705+
nodeTemplate := StaticNodeTemplate(makeBaseNode(prefix))
17051706
if cno.NodeTemplatePath != nil {
1706-
node, err := getNodeSpecFromFile(cno.NodeTemplatePath)
1707-
if err != nil {
1708-
return nil, err
1709-
}
1710-
return framework.NewIntegrationTestNodePreparerWithNodeSpec(
1711-
clientset,
1712-
[]testutils.CountToStrategy{{Count: cno.Count, Strategy: nodeStrategy}},
1713-
node,
1714-
), nil
1707+
nodeTemplate = nodeTemplateFromFile(*cno.NodeTemplatePath)
17151708
}
1716-
return framework.NewIntegrationTestNodePreparer(
1709+
1710+
return NewIntegrationTestNodePreparer(
17171711
clientset,
17181712
[]testutils.CountToStrategy{{Count: cno.Count, Strategy: nodeStrategy}},
1719-
prefix,
1713+
nodeTemplate,
17201714
), nil
17211715
}
17221716

@@ -2103,9 +2097,11 @@ func getPodStrategy(cpo *createPodsOp) (testutils.TestPodCreateStrategy, error)
21032097
return testutils.NewCreatePodWithPersistentVolumeStrategy(pvcTemplate, getCustomVolumeFactory(pvTemplate), podTemplate), nil
21042098
}
21052099

2106-
func getNodeSpecFromFile(path *string) (*v1.Node, error) {
2100+
type nodeTemplateFromFile string
2101+
2102+
func (f nodeTemplateFromFile) GetNodeTemplate(index, count int) (*v1.Node, error) {
21072103
nodeSpec := &v1.Node{}
2108-
if err := getSpecFromFile(path, nodeSpec); err != nil {
2104+
if err := getSpecFromTextTemplateFile(string(f), map[string]any{"Index": index, "Count": count}, nodeSpec); err != nil {
21092105
return nil, fmt.Errorf("parsing Node: %w", err)
21102106
}
21112107
return nodeSpec, nil

test/integration/scheduler_perf/util.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"time"
3131

3232
v1 "k8s.io/api/core/v1"
33+
"k8s.io/apimachinery/pkg/api/resource"
3334
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3435
"k8s.io/apimachinery/pkg/labels"
3536
"k8s.io/apimachinery/pkg/util/sets"
@@ -227,6 +228,27 @@ func makeBasePod() *v1.Pod {
227228
return basePod
228229
}
229230

231+
// makeBaseNode creates a Node object with given nodeNamePrefix to be used as a template.
232+
func makeBaseNode(nodeNamePrefix string) *v1.Node {
233+
return &v1.Node{
234+
ObjectMeta: metav1.ObjectMeta{
235+
GenerateName: nodeNamePrefix,
236+
},
237+
Status: v1.NodeStatus{
238+
Capacity: v1.ResourceList{
239+
v1.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
240+
v1.ResourceCPU: resource.MustParse("4"),
241+
v1.ResourceMemory: resource.MustParse("32Gi"),
242+
},
243+
Phase: v1.NodeRunning,
244+
Conditions: []v1.NodeCondition{
245+
{Type: v1.NodeReady, Status: v1.ConditionTrue},
246+
},
247+
},
248+
}
249+
250+
}
251+
230252
func dataItems2JSONFile(dataItems DataItems, namePrefix string) error {
231253
// perfdash expects all data items to have the same set of labels. It
232254
// then renders drop-down buttons for each label with all values found

0 commit comments

Comments
 (0)