Skip to content

Commit 7f5cdc0

Browse files
add pprof test but remove upload artefact
1 parent ff39cf2 commit 7f5cdc0

File tree

6 files changed

+89
-10
lines changed

6 files changed

+89
-10
lines changed

.github/workflows/e2e.yaml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,12 @@ jobs:
3434

3535
- name: Run e2e tests
3636
run: ARTIFACT_PATH=/tmp/artifacts make test-e2e
37-
38-
- uses: actions/upload-artifact@v4
39-
with:
40-
name: upgrade-e2e-artifacts
41-
path: test/e2e/results/**
37+
38+
# To not store pprof
39+
# - uses: actions/upload-artifact@v4
40+
# with:
41+
# name: upgrade-e2e-artifacts
42+
# path: test/e2e/results/**
4243

4344
- uses: actions/upload-artifact@v4
4445
if: failure()

cmd/operator-controller/main.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ var (
8383

8484
type config struct {
8585
metricsAddr string
86+
pprofAddr string
8687
certFile string
8788
keyFile string
8889
enableLeaderElection bool
@@ -131,6 +132,7 @@ func init() {
131132
//create flagset, the collection of flags for this command
132133
flags := operatorControllerCmd.Flags()
133134
flags.StringVar(&cfg.metricsAddr, "metrics-bind-address", "", "The address for the metrics endpoint. Requires tls-cert and tls-key. (Default: ':8443')")
135+
flags.StringVar(&cfg.pprofAddr, "pprof-bind-address", "0", "The address the pprof endpoint binds to. an empty string or 0 disables pprof")
134136
flags.StringVar(&cfg.probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
135137
flags.StringVar(&cfg.catalogdCasDir, "catalogd-cas-dir", "", "The directory of TLS certificate authorities to use for verifying HTTPS connections to the Catalogd web service.")
136138
flags.StringVar(&cfg.pullCasDir, "pull-cas-dir", "", "The directory of TLS certificate authorities to use for verifying HTTPS connections to image registries.")
@@ -265,6 +267,7 @@ func run() error {
265267
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
266268
Scheme: scheme.Scheme,
267269
Metrics: metricsServerOptions,
270+
PprofBindAddress: cfg.pprofAddr,
268271
HealthProbeBindAddress: cfg.probeAddr,
269272
LeaderElection: cfg.enableLeaderElection,
270273
LeaderElectionID: "9c4404e7.operatorframework.io",

config/base/catalogd/manager/catalogd_service.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ spec:
1414
protocol: TCP
1515
port: 80
1616
targetPort: 8443
17+
- name: pprof
18+
port: 8083
19+
targetPort: 8083
1720
- name: webhook
1821
protocol: TCP
1922
port: 9443

config/base/catalogd/manager/manager.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ spec:
4545
- ./catalogd
4646
args:
4747
- --leader-elect
48+
- --pprof-bind-address=:8083
4849
- --metrics-bind-address=:7443
4950
- --external-address=catalogd-service.olmv1-system.svc
5051
image: controller:latest

config/base/operator-controller/manager/manager.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ spec:
4444
- /operator-controller
4545
args:
4646
- "--health-probe-bind-address=:8081"
47+
- "--pprof-bind-address=:8082"
4748
- "--metrics-bind-address=:8443"
4849
- "--leader-elect"
4950
image: controller:latest

test/e2e/metrics_test.go

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"errors"
2121
"fmt"
2222
"os"
23+
"os/exec"
2324
"regexp"
2425
"strconv"
2526
"strings"
@@ -153,23 +154,91 @@ func parseMetrics(metricsText string) map[string]float64 {
153154
return mapMetrics
154155
}
155156

157+
func (c *MetricsTestConfig) fetchPprofAndStore(ctx context.Context, token string, profileType string) {
158+
c.t.Logf("fetching pprof profiling data for profile: %s", profileType)
159+
160+
var url string
161+
if strings.Contains(c.metricsURL, "catalogd-service") {
162+
url = fmt.Sprintf("http://catalogd-service.olmv1-system.svc.cluster.local:8083/debug/pprof/%s", profileType)
163+
} else if strings.Contains(c.metricsURL, "operator-controller-service") {
164+
url = fmt.Sprintf("http://operator-controller-service.olmv1-system.svc.cluster.local:8082/debug/pprof/%s", profileType)
165+
} else {
166+
c.t.Fatalf("unknown service in metricsURL: %s", c.metricsURL)
167+
}
168+
169+
savePath := fmt.Sprintf("/tmp/%s.pprof", profileType)
170+
cmd := []string{
171+
"sh", "-c",
172+
fmt.Sprintf(`curl -s -k -H "Authorization: Bearer %s" %s > %s`, token, url, savePath),
173+
}
174+
175+
req := c.kubeClient.CoreV1().RESTClient().
176+
Post().
177+
Resource("pods").
178+
Namespace(c.namespace).
179+
Name(c.curlPodName).
180+
SubResource("exec").
181+
VersionedParams(&corev1.PodExecOptions{
182+
Container: "curl",
183+
Command: cmd,
184+
Stdin: false,
185+
Stdout: true,
186+
Stderr: true,
187+
TTY: false,
188+
}, scheme.ParameterCodec)
189+
190+
executor, err := remotecommand.NewSPDYExecutor(c.restConfig, "POST", req.URL())
191+
require.NoError(c.t, err, "rrror creating SPDY executor for pod exec")
192+
193+
var stdout, stderr bytes.Buffer
194+
streamOpts := remotecommand.StreamOptions{
195+
Stdout: &stdout,
196+
Stderr: &stderr,
197+
}
198+
199+
err = executor.StreamWithContext(ctx, streamOpts)
200+
require.NoError(c.t, err, "rrror executing curl in pod: %v", stderr.String())
201+
202+
tt := strings.ReplaceAll(c.t.Name(), "/", "_")
203+
localP := fmt.Sprintf("results/%s_%s_%s.pprof", tt, profileType, time.Now().Format("20060102_150405"))
204+
copyCmd := fmt.Sprintf("kubectl cp %s/%s:%s %s", c.namespace, c.curlPodName, savePath, localP)
205+
206+
output, copyErr := exec.Command("sh", "-c", copyCmd).CombinedOutput()
207+
require.NoError(c.t, copyErr, "failed copy pprof: %s", string(output))
208+
c.t.Logf("Pprof data successfully saved to: %s", localP)
209+
210+
textP := strings.Replace(localP, ".pprof", ".txt", 1)
211+
textCmd := fmt.Sprintf("go tool pprof -text %s > %s", localP, textP)
212+
textOutput, textErr := exec.Command("sh", "-c", textCmd).CombinedOutput()
213+
require.NoError(c.t, textErr, "failed to convert pprof: %s", string(textOutput))
214+
215+
c.t.Logf("Pprof text saved to: %s", textP)
216+
}
217+
218+
func (c *MetricsTestConfig) fetchAndStorePprof(ctx context.Context, token string) {
219+
profiles := []string{"profile", "heap", "goroutine"}
220+
for _, profileType := range profiles {
221+
c.fetchPprofAndStore(ctx, token, profileType)
222+
}
223+
}
224+
156225
// storeMetricsResult writes only relevant metrics to a JSON file
157226
func (c *MetricsTestConfig) storeMetricsResult(metrics map[string]float64) {
158227
dir := "results"
159228
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
160229
c.t.Fatalf("Failed to create directory %s: %v", dir, err)
161230
}
162-
231+
163232
filePath := fmt.Sprintf("%s/metrics_%s_%s_%s.txt", dir, c.name, c.t.Name(), time.Now().Format("20060102_150405"))
164233
jsonData, err := json.MarshalIndent(metrics, "", " ")
165234
if err != nil {
166235
c.t.Fatalf("Failed to encode metrics as JSON: %v", err)
167236
}
168237

169238
err = os.WriteFile(filePath, jsonData, 0644)
170-
require.NoError(c.t, err, "Failed to save metrics to file")
239+
require.NoError(c.t, err, "failed to save metrics")
171240

172-
c.t.Logf("Filtered metrics saved to: %s", filePath)
241+
c.t.Logf("filtered metrics saved to: %s", filePath)
173242
}
174243

175244
func findK8sClient(t *testing.T) (kubernetes.Interface, *rest.Config) {
@@ -233,17 +302,18 @@ func NewMetricsTestConfig(
233302
// run executes the entire test flow
234303
func (c *MetricsTestConfig) run() {
235304
ctx := context.Background()
236-
defer c.cleanup(ctx)
305+
//defer c.cleanup(ctx)
237306
c.createMetricsClusterRoleBinding(ctx)
238307
token := c.getServiceAccountToken(ctx)
239308
c.createCurlMetricsPod(ctx)
240309
c.waitForPodReady(ctx)
241310
// Exec `curl` in the Pod to validate the metrics
242311
c.validateMetricsEndpoint(ctx, token)
243312

244-
// Fetch and save Prometheus metrics after test execution
245313
metrics := c.fetchMetrics(ctx, token)
246314
c.storeMetricsResult(metrics)
315+
316+
c.fetchAndStorePprof(ctx, token)
247317
}
248318

249319
// createMetricsClusterRoleBinding to bind the cluster role so metrics are accessible

0 commit comments

Comments
 (0)