Skip to content

Commit 93d6349

Browse files
committed
chore: added benchmark test cases for GetChildObjectsV2
1 parent f1702bc commit 93d6349

12 files changed

Lines changed: 720 additions & 607 deletions
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
/*
2+
* Copyright (c) 2024. Devtron Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package commonHelmService
18+
19+
import (
20+
"context"
21+
"flag"
22+
"fmt"
23+
"github.com/devtron-labs/common-lib/utils/k8s"
24+
"github.com/devtron-labs/kubelink/config"
25+
"github.com/devtron-labs/kubelink/internals/logger"
26+
log "github.com/sirupsen/logrus"
27+
"go.uber.org/zap"
28+
"io"
29+
k8sApiV1 "k8s.io/api/core/v1"
30+
k8sApiMetaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31+
"k8s.io/apimachinery/pkg/runtime/schema"
32+
"k8s.io/apimachinery/pkg/types"
33+
"k8s.io/apimachinery/pkg/util/rand"
34+
k8sClinetV1 "k8s.io/client-go/kubernetes/typed/core/v1"
35+
"k8s.io/client-go/rest"
36+
"k8s.io/utils/ptr"
37+
"os"
38+
"os/exec"
39+
"strings"
40+
"testing"
41+
"time"
42+
)
43+
44+
var sugaredLogger *zap.SugaredLogger
45+
46+
const multiplyFactor = 10
47+
48+
func init() {
49+
sugaredLogger = logger.NewSugaredLogger()
50+
}
51+
52+
func dependencyInit() (k8sUtil *k8s.K8sServiceImpl, restConfig *rest.Config, client *k8sClinetV1.CoreV1Client) {
53+
// Mock K8sService implementation
54+
runtimeCfg, err := k8s.GetRuntimeConfig()
55+
if err != nil {
56+
log.Errorf("Failed to get runtime config: %v", err)
57+
return
58+
}
59+
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
60+
flag.CommandLine.SetOutput(io.Discard)
61+
flag.CommandLine.Usage = flag.Usage
62+
runtimeCfg.LocalDevMode = true
63+
k8sUtil, err = k8s.NewK8sUtil(sugaredLogger, runtimeCfg)
64+
if err != nil {
65+
log.Errorf("Failed to create K8sUtil: %v", err)
66+
return
67+
}
68+
restConfig, err = k8sUtil.GetK8sInClusterRestConfig()
69+
if err != nil {
70+
log.Errorf("Failed to get rest config: %v", err)
71+
return
72+
}
73+
client, err = k8sUtil.GetCoreV1ClientByRestConfig(restConfig)
74+
if err != nil {
75+
log.Errorf("Failed to get core v1 client: %v", err)
76+
return
77+
}
78+
return
79+
}
80+
81+
func BenchmarkGetChildObjectsV2_ResourceByLimit_1_1(b *testing.B) {
82+
b.SkipNow()
83+
var pageLimit int64 = 100 * multiplyFactor
84+
resourceCount := 100 * multiplyFactor
85+
benchmarkGetChildObjectsV2(b, resourceCount, pageLimit)
86+
}
87+
88+
func BenchmarkGetChildObjectsV2_ResourceByLimit_3_4(b *testing.B) {
89+
var pageLimit int64 = 75 * multiplyFactor
90+
resourceCount := 100 * multiplyFactor
91+
benchmarkGetChildObjectsV2(b, resourceCount, pageLimit)
92+
}
93+
94+
func BenchmarkGetChildObjectsV2_ResourceByLimit_1_2(b *testing.B) {
95+
var pageLimit int64 = 50 * multiplyFactor
96+
resourceCount := 100 * multiplyFactor
97+
benchmarkGetChildObjectsV2(b, resourceCount, pageLimit)
98+
}
99+
100+
func BenchmarkGetChildObjectsV2_ResourceByLimit_1_4(b *testing.B) {
101+
var pageLimit int64 = 25 * multiplyFactor
102+
resourceCount := 100 * multiplyFactor
103+
benchmarkGetChildObjectsV2(b, resourceCount, pageLimit)
104+
}
105+
106+
func BenchmarkGetChildObjectsV2_ResourceByLimit_1_5(b *testing.B) {
107+
var pageLimit int64 = 20 * multiplyFactor
108+
resourceCount := 100 * multiplyFactor
109+
benchmarkGetChildObjectsV2(b, resourceCount, pageLimit)
110+
}
111+
112+
func benchmarkGetChildObjectsV2(b *testing.B, resourceCount int, pageLimit int64) {
113+
b.Helper()
114+
b.SetParallelism(1)
115+
testName := fmt.Sprintf("test-listing-page-limit-%d", pageLimit)
116+
k8sUtil, restConfig, client := dependencyInit()
117+
helmReleaseConfig, err := config.GetHelmReleaseConfig()
118+
if err != nil {
119+
b.Fatalf("Failed to get helm release config: %v", err)
120+
return
121+
}
122+
helmReleaseConfig.ParentChildGvkMapping = getParentChildGvkMapForBenchmarkTest()
123+
helmReleaseConfig.ChildObjectListingPageSize = pageLimit
124+
k8sService, err := NewK8sServiceImpl(sugaredLogger, helmReleaseConfig)
125+
if err != nil {
126+
b.Fatalf("Failed to create K8sService: %v", err)
127+
return
128+
}
129+
highlightedLog("\n\n\n==========================================================================")
130+
highlightedLog(fmt.Sprintf("=== Running benchmark for %s with resource count: %d\n", testName, resourceCount))
131+
ctx, cancel := context.WithCancel(context.Background())
132+
// setup logic
133+
releaseName := fmt.Sprintf("test-deployment-limit-%d", pageLimit)
134+
err = createDeploymentWithCMs(ctx, k8sUtil, client, releaseName, resourceCount)
135+
if err != nil {
136+
b.Fatalf("Failed to setup test case: %v", err)
137+
return
138+
}
139+
b.Cleanup(func() {
140+
// cleanup logic
141+
cancel()
142+
_ = tearDownDeploymentWithCMs(releaseName)
143+
highlightedLog("==========================================================================\n\n\n")
144+
})
145+
b.ReportAllocs()
146+
b.ResetTimer()
147+
for range b.N {
148+
startTime := time.Now()
149+
_, err = k8sService.GetChildObjectsV2(restConfig, "ent-8-env-1", schema.GroupVersionKind{
150+
Kind: "Deployment",
151+
Group: "apps",
152+
Version: "v1",
153+
}, fmt.Sprintf("%s-memcached", releaseName))
154+
highlightedLog("==========================================================================")
155+
highlightedLog(fmt.Sprintf("=== Time taken to get child objects: %v", time.Since(startTime).Seconds()))
156+
highlightedLog("==========================================================================")
157+
if err != nil {
158+
b.Fatalf("Failed to get child objects: %v", err)
159+
return
160+
}
161+
}
162+
}
163+
164+
func createDeploymentWithCMs(ctx context.Context, k8sUtil k8s.K8sService,
165+
client *k8sClinetV1.CoreV1Client, releaseName string, cmCount int) error {
166+
highlightedLog("=== Creating helm release...")
167+
// Create helm release
168+
err := executeCommand(ctx, "helm", "install", releaseName, "oci://registry-1.docker.io/bitnamicharts/memcached", "--namespace", "ent-8-env-1", "--create-namespace")
169+
if err != nil {
170+
return err
171+
}
172+
// Get the UID of the deployment
173+
ucidBytes, err := executeCommandWithOutput(ctx, "kubectl", "get", "deployment", fmt.Sprintf("%s-memcached", releaseName), "--namespace", "ent-8-env-1", "-o", "jsonpath={.metadata.uid}")
174+
if err != nil {
175+
return err
176+
}
177+
deploymentUid := types.UID(ucidBytes)
178+
// validate the UID
179+
if len(deploymentUid) == 0 || strings.ContainsAny(string(deploymentUid), " ") {
180+
return fmt.Errorf("no deployment uid found")
181+
}
182+
// Create configmap with owner reference
183+
for i := 0; i < cmCount; i++ {
184+
newConfigMap := &k8sApiV1.ConfigMap{ObjectMeta: k8sApiMetaV1.ObjectMeta{Name: fmt.Sprintf("benchmark-test-%d-configmap-%s", i, rand.SafeEncodeString(rand.String(10)))}}
185+
newConfigMap.Data = map[string]string{
186+
"createdBy": "integration-testing",
187+
"createdAt": time.Now().Format(time.RFC3339),
188+
"fileName": "test-file",
189+
"filePath": "/tmp/test-file",
190+
"fileContent": rand.SafeEncodeString(rand.String(90)),
191+
}
192+
newConfigMap.OwnerReferences = []k8sApiMetaV1.OwnerReference{
193+
{
194+
APIVersion: "apps/v1",
195+
Kind: "Deployment",
196+
Name: fmt.Sprintf("%s-memcached", releaseName),
197+
UID: deploymentUid,
198+
Controller: ptr.To(true),
199+
},
200+
}
201+
_, err := k8sUtil.CreateConfigMap("ent-8-env-1", newConfigMap, client)
202+
if err != nil {
203+
return err
204+
}
205+
}
206+
highlightedLog("=== Created configmaps with owner reference ✔")
207+
return nil
208+
}
209+
210+
func tearDownDeploymentWithCMs(releaseName string) error {
211+
highlightedLog("=== Deleting helm release...")
212+
// Delete helm release
213+
_ = executeCommand(context.Background(), "helm", "uninstall", releaseName, "--namespace", "ent-8-env-1")
214+
return nil
215+
}
216+
217+
// executeCommandWithOutput executes a command with the given arguments and returns the output as a byte slice.
218+
func executeCommandWithOutput(ctx context.Context, name string, arg ...string) ([]byte, error) {
219+
cmd := exec.CommandContext(ctx, name, arg...)
220+
return cmd.Output()
221+
}
222+
223+
// getParentChildGvkMapForBenchmarkTest
224+
// DAG Graph Representation of Parent-Child Objects:
225+
//
226+
// ReplicaSet (v1, apps)
227+
// └─── Pods (v1, namespace)
228+
//
229+
// Deployment (v1, apps)
230+
// ├─── ReplicaSets (v1, apps, namespace)
231+
// │ └─── Pods (v1, namespace)
232+
// └─── ConfigMaps (v1, namespace)
233+
func getParentChildGvkMapForBenchmarkTest() string {
234+
return `[{"childObjects":[{"Group":"","Resource":"pods","Scope":"namespace","Version":"v1"}],"group":"apps","kind":"ReplicaSet","version":"v1"},{"childObjects":[{"Group":"apps","Resource":"replicasets","Scope":"namespace","Version":"v1"},{"Group":"","Resource":"configmaps","Scope":"namespace","Version":"v1"}],"group":"apps","kind":"Deployment","version":"v1"}]`
235+
}

0 commit comments

Comments
 (0)