-
Notifications
You must be signed in to change notification settings - Fork 2.2k
/
build_benchmark_test.go
196 lines (170 loc) · 4.93 KB
/
build_benchmark_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
// Copyright 2023 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package build_test
import (
"bytes"
"fmt"
"path/filepath"
"testing"
. "sigs.k8s.io/kustomize/kustomize/v5/commands/build"
"sigs.k8s.io/kustomize/kyaml/filesys"
)
// GenConfig configures the generation of a kustomization tree for benchmarking purposes
type GenConfig struct {
// The number of plain file resources to generate
fileResources int
// The number of subdirectories to generate
resources int
// The number of patches to generate
patches int
// Whether to generate a namespace field
namespaced bool
// The name prefix to use (if any)
namePrefix string
// The name suffix to use (if any)
nameSuffix string
// Common labels to use (if any)
commonLabels map[string]string
// Common annotations to use (if any)
commonAnnotations map[string]string
}
func makeKustomization(configs []GenConfig, fSys filesys.FileSystem, path, id string, depth int) error {
cfg := configs[depth]
if err := fSys.MkdirAll(path); err != nil {
return fmt.Errorf("failed to make directory %v: %w", path, err)
}
var buf bytes.Buffer
if cfg.namespaced {
fmt.Fprintf(&buf, "namespace: %s\n", id)
}
if cfg.namePrefix != "" {
fmt.Fprintf(&buf, "namePrefix: %s\n", cfg.namePrefix)
}
if cfg.nameSuffix != "" {
fmt.Fprintf(&buf, "nameSuffix: %s\n", cfg.nameSuffix)
}
if len(cfg.commonLabels) > 0 {
fmt.Fprintf(&buf, "commonLabels:\n")
for k, v := range cfg.commonLabels {
fmt.Fprintf(&buf, " %s: %s\n", k, v)
}
}
if len(cfg.commonAnnotations) > 0 {
fmt.Fprintf(&buf, "commonAnnotations:\n")
for k, v := range cfg.commonAnnotations {
fmt.Fprintf(&buf, " %s: %s\n", k, v)
}
}
if cfg.fileResources > 0 || cfg.resources > 0 {
fmt.Fprintf(&buf, "resources:\n")
for res := 0; res < cfg.fileResources; res++ {
fn := fmt.Sprintf("res%d.yaml", res)
fmt.Fprintf(&buf, " - %v\n", fn)
cm := fmt.Sprintf(`kind: ConfigMap
apiVersion: v1
metadata:
name: %s-%d
labels:
foo: bar
annotations:
baz: blatti
data:
k: v
`, id, res)
if err := fSys.WriteFile(filepath.Join(path, fn), []byte(cm)); err != nil {
return fmt.Errorf("failed to write file resource: %w", err)
}
}
for res := 0; res < cfg.resources; res++ {
fn := fmt.Sprintf("res%d", res)
fmt.Fprintf(&buf, " - %v\n", fn)
if err := makeKustomization(configs, fSys, path+"/"+fn, fmt.Sprintf("%s-%d", id, res), depth+1); err != nil {
return fmt.Errorf("failed to make kustomization: %w", err)
}
}
}
for res := 0; res < cfg.patches; res++ {
if res == 0 {
fmt.Fprintf(&buf, "patches:\n")
}
// alternate between json and yaml patches to test both kinds
if res%2 == 0 {
fn := fmt.Sprintf("patch%d.yaml", res)
fmt.Fprintf(&buf, " - path: %v\n", fn)
cmPatch := fmt.Sprintf(`kind: ConfigMap
apiVersion: v1
metadata:
name: %s-%d
data:
k: v2
`, id, res)
if err := fSys.WriteFile(filepath.Join(path, fn), []byte(cmPatch)); err != nil {
return fmt.Errorf("failed to write patch: %w", err)
}
} else {
fn := fmt.Sprintf("patch%d.json", res)
fmt.Fprintf(&buf, ` - path: %v
target:
version: v1
kind: ConfigMap
name: %s-%d
`, fn, id, res-1)
patch := `[{"op": "add", "path": "/data/k2", "value": "3"} ]`
if err := fSys.WriteFile(filepath.Join(path, fn), []byte(patch)); err != nil {
return fmt.Errorf("failed to write patch: %w", err)
}
}
}
if err := fSys.WriteFile(filepath.Join(path, "kustomization.yaml"), buf.Bytes()); err != nil {
return fmt.Errorf("failed to write kustomization.yaml: %w", err)
}
return nil
}
func BenchmarkBuild(b *testing.B) {
// This benchmark generates a kustomization tree with the following structure:
genConfig := []GenConfig{
{
resources: 4, // four nested resources
// these operations should be very fast, so lets perform them on a *lot* of resources
namePrefix: "foo-",
nameSuffix: "-bar",
commonLabels: map[string]string{
"foo": "bar",
},
commonAnnotations: map[string]string{
"baz": "blatti",
},
},
{
// test some more nesting (this could be `apps/` or `components/` directory with 100 apps or components)
resources: 100,
},
{
// this should be almost the same as using 300 above and skipping it, but it is currently not, so lets have some more nesting
resources: 3,
},
{
// here we have an actual component/app with lots or resources. Typically here we set namespace and have some patches
resources: 1,
namespaced: true,
fileResources: 30,
patches: 10,
},
{
// we can also have a base/ or shared resources included into the namespace
fileResources: 2,
},
}
fSys := filesys.MakeFsInMemory()
if err := makeKustomization(genConfig, fSys, "testdata", "res", 0); err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
buffy := new(bytes.Buffer)
cmd := NewCmdBuild(fSys, MakeHelp("foo", "bar"), buffy)
if err := cmd.RunE(cmd, []string{"./testdata"}); err != nil {
b.Fatal(err)
}
}
}