-
Notifications
You must be signed in to change notification settings - Fork 111
Add support for "bench warp" command #1702
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
base: master
Are you sure you want to change the base?
Add support for "bench warp" command #1702
Conversation
WalkthroughAdds Kubernetes manifests and bundled assets for running MinIO Warp, introduces a new bench warp orchestration CLI workflow, integrates it into the main CLI, adds generic Map and OnSignal utilities, and refactors admission server signal handling to use the new utility. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant CLI as noobaa CLI
participant Bench as bench.warp
participant K8s as Kubernetes API
participant SS as Warp StatefulSet/Service
participant Job as Warp Job
participant Target as Object Store
User->>CLI: noobaa bench warp [flags]
CLI->>Bench: RunBenchWarp()
Bench->>K8s: Create Service + StatefulSet
K8s-->>Bench: Pods ready
Bench->>K8s: Create Warp Job (env keys, args)
Bench->>K8s: Watch Job / Pods
K8s-->>Bench: Job status updates
Job->>Target: Run warp client against endpoints
Note over Job,Target: Data/benchmark operations
Bench->>K8s: Stream logs from Job pods
alt Job Succeeds
K8s-->>Bench: Completion (Succeeded)
else Job Fails
K8s-->>Bench: Completion (Failed)
end
Bench->>K8s: Cleanup Job/SS/Service
Bench-->>CLI: Exit with status/logs
CLI-->>User: Output results
sequenceDiagram
autonumber
participant Adm as Admission Server
participant Util as util.OnSignal
participant OS as OS Signals
Adm->>Util: OnSignal(cb, SIGINT, SIGTERM)
OS-->>Util: Deliver signal
Util-->>Adm: Invoke callback
Adm->>Adm: server.Shutdown(ctx)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes ✨ Finishing touches
🧪 Generate unit tests
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal). Please share your feedback with us on this Discord post. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment Pre-merge checks❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 10
🧹 Nitpick comments (17)
deploy/warp/warp-job.yaml (1)
3-15
: Provide labels and wire credentials via Secret refs (not empty envs)Empty env entries are brittle. Add labels for lifecycle ops and source keys from a Secret.
metadata: name: warp-job + labels: + app: warp spec: template: spec: containers: - name: warp-job - env: - - name: WARP_ACCESS_KEY - - name: WARP_SECRET_KEY + env: + - name: WARP_ACCESS_KEY + valueFrom: + secretKeyRef: + name: REPLACE_ME_WARP_CREDS_SECRET + key: access_key + - name: WARP_SECRET_KEY + valueFrom: + secretKeyRef: + name: REPLACE_ME_WARP_CREDS_SECRET + key: secret_keyConfirm the bench workflow patches args/creds before apply; otherwise this Job will run with defaults.
pkg/util/util.go (1)
2422-2428
: Stop signal notifications to avoid leaks; consider a context‑based variantCall signal.Stop to unregister and prevent goroutine/channel leaks. Optional: offer OnSignalCtx that cancels a context.
func OnSignal(cb func(), signals ...os.Signal) { - signalChan := make(chan os.Signal, 1) - signal.Notify(signalChan, signals...) - <-signalChan - - cb() + signalChan := make(chan os.Signal, 1) + signal.Notify(signalChan, signals...) + defer signal.Stop(signalChan) + <-signalChan + cb() }If useful, I can add
OnSignalCtx(ctx context.Context, cb func(), signals ...os.Signal)
that returns when either ctx is done or a signal arrives.pkg/admission/server.go (1)
69-77
: Graceful shutdown: use timeout context; fix minor typos/severityUse a bounded context for Shutdown and warn on real errors. Also “singal” → “signal”.
- // listening shutdown singal - util.OnSignal(func() { - log.Info("Got shutdown signal, shutting down webhook server gracefully...") - err = server.Shutdown(context.Background()) - if err != nil { - log.Info("Failed to Shutdown admission server") - } - }, syscall.SIGINT, syscall.SIGTERM) + // listening shutdown signal + util.OnSignal(func() { + log.Info("Got shutdown signal, shutting down webhook server gracefully...") + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + if err := server.Shutdown(ctx); err != nil && err != http.ErrServerClosed { + log.Warnf("Failed to shutdown admission server gracefully: %v", err) + } + }, syscall.SIGINT, syscall.SIGTERM)Additional import needed:
import ( "context" "crypto/tls" "fmt" "net/http" "os" "syscall" + "time"
deploy/warp/warp-svc.yaml (2)
11-12
: Align port names with the StatefulSetThe container port is named “http”; align Service port name for consistency and discovery.
ports: - - port: 7761 - name: warp + - port: 7761 + name: http
14-14
: Add newline at EOFMinor YAML lint nit.
deploy/warp/warp.yaml (2)
15-18
: Drop template.metadata.name (ignored in Pod templates)Name in a Pod template is ignored and can confuse reviewers.
template: metadata: - name: warp labels: app: warp
20-29
: Prefer soft anti‑affinity to avoid unschedulable replicasRequired anti‑affinity can block scaling when node count < replicas. Prefer preferredDuringScheduling.
- affinity: - podAntiAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - - labelSelector: - matchExpressions: - - key: app - operator: In - values: - - warp - topologyKey: "kubernetes.io/hostname" + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + topologyKey: "kubernetes.io/hostname" + labelSelector: + matchExpressions: + - key: app + operator: In + values: ["warp"]pkg/bundle/deploy.go (1)
6950-6964
: Nit: Port name consistency and labels.Service port is named "warp" while the container exposes "http". Prefer consistent names to simplify targeting by name and add app labels for discoverability.
Apply this diff (pairs with the StatefulSet port rename above):
spec: publishNotReadyAddresses: true clusterIP: None ports: - - port: 7761 - name: warp + - port: 7761 + name: warp selector: app: warpOptional: consider adding standard labels (app.kubernetes.io/name, app.kubernetes.io/part-of: bench-warp).
pkg/bench/warp.go (9)
241-254
: Polling loop can run forever — bound it or make it user-configurableConsider a timeout (e.g., flag) and stop with a warning when exceeded.
- for true { + deadline := time.Now().Add(24 * time.Hour) + for time.Now().Before(deadline) { ... - time.Sleep(5 * time.Second) + time.Sleep(5 * time.Second) } + if time.Now().After(deadline) { + log.Warn("Benchmark timed out after 24h; proceeding to cleanup") + }
272-275
: Close log streams to avoid leaking readersReaders returned by GetPodLogs must be closed after use.
- for _, container := range logs { - io.Copy(os.Stdout, container) - } + for _, rc := range logs { + _, _ = io.Copy(os.Stdout, rc) + _ = rc.Close() + }
216-218
: Robust arg parsingstrings.Split breaks on multiple spaces and can’t handle quoted args. At minimum use Fields; ideally use a shellwords parser.
Minimal fix:
- args = append(args, strings.Split(warpArgs, " ")...) + args = append(args, strings.Fields(warpArgs)...)Optional (better): parse with shellwords (github.com/mattn/go-shellwords) and append parsed tokens.
98-105
: Validate endpoint-type earlyReject invalid values to avoid producing an empty --host list.
endpointType := util.GetFlagStringOrPrompt(cmd, "endpoint-type") + switch endpointType { + case EndpointInternal, EndpointPodIP, EndpointNodeport, EndpointLoadbalancer: + default: + log.Fatalf("❌ Invalid --endpoint-type %q. Valid: %s,%s,%s,%s", + endpointType, EndpointInternal, EndpointPodIP, EndpointNodeport, EndpointLoadbalancer) + }
128-131
: Deduplicate cleanup on signal and normal exitcleanupWarp can run twice concurrently. Guard with sync.Once.
@@ -import ( +import ( "context" "fmt" "io" "os" "strings" "syscall" "time" + "sync" @@ -go util.OnSignal(func() { - cleanupWarp() -}, syscall.SIGINT, syscall.SIGTERM) +var cleanupOnce sync.Once +go util.OnSignal(func() { + cleanupOnce.Do(cleanupWarp) +}, syscall.SIGINT, syscall.SIGTERM) @@ pollWarp() -cleanupWarp() +cleanupOnce.Do(cleanupWarp) @@ -func cleanupWarp() { +func cleanupWarp() { util.Logger().Info("Cleaning up Warp") @@ util.KubeDeleteAllOf(&corev1.Pod{}, client.InNamespace(options.Namespace), client.MatchingLabels{ "job-name": "warp-job", }) }Also applies to: 139-140, 278-296
148-148
: Typo in log message“Unexepected” → “Unexpected”.
- log.Fatal("Unexepected number of containers in the Warp STS") + log.Fatal("Unexpected number of containers in the Warp STS")
331-343
: PodIP mode: rely on Service Endpoints/EndpointSlices instead of hardcoded labelsSelector value may differ from "noobaa"; query via Endpoints/EndpointSlices of the s3 Service to collect Pod IPs directly.
298-316
: Defensive checks: ensure non-empty host list and non-zero portIf no IPs or port=0, fail fast with a clear error.
getPort := func(svc *corev1.Service, portname string, fn func(corev1.ServicePort) int32) int32 { @@ return 0 } @@ - return strings.Join(util.Map(ips, func(ip string) string { + if len(ips) == 0 || port == 0 { + log.Fatalf("❌ Failed to resolve endpoint(s) for %q (https=%v); ips=%v port=%d", endpointType, https, ips, port) + } + return strings.Join(util.Map(ips, func(ip string) string { return fmt.Sprintf("%s:%d", ip, port) }), ",")Also applies to: 377-380
45-50
: CLI UX: document expected bench arg and provide examplesAdd ValidArgs or Example to guide users (e.g., mixed/put/get/list).
cmd := &cobra.Command{ Use: "warp", Short: "Run warp benchmark", Run: RunBenchWarp, Args: cobra.ExactArgs(1), + Example: ` noobaa bench warp mixed --bucket first.bucket --warp-args "--concurrent 1 --objects 100 --obj.size 10MiB"`, + ValidArgs: []string{"mixed", "put", "get", "delete", "stat", "list"}, }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
deploy/warp/warp-job.yaml
(1 hunks)deploy/warp/warp-svc.yaml
(1 hunks)deploy/warp/warp.yaml
(1 hunks)pkg/admission/server.go
(2 hunks)pkg/bench/warp.go
(1 hunks)pkg/bundle/deploy.go
(1 hunks)pkg/cli/cli.go
(2 hunks)pkg/util/util.go
(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
pkg/admission/server.go (1)
pkg/util/util.go (1)
OnSignal
(2422-2428)
pkg/cli/cli.go (1)
pkg/bench/warp.go (1)
Cmd
(32-42)
pkg/bench/warp.go (4)
pkg/cli/cli.go (2)
Cmd
(70-167)Run
(62-67)pkg/util/util.go (13)
Logger
(904-906)GetFlagStringOrPrompt
(1316-1328)KubeObject
(284-294)KubeCheck
(570-591)OnSignal
(2422-2428)KubeApply
(298-334)Context
(909-911)KubeCheckQuiet
(619-632)KubeList
(647-668)GetPodLogs
(715-754)KubeDelete
(445-498)KubeDeleteAllOf
(522-536)Map
(2413-2420)pkg/bundle/deploy.go (5)
File_deploy_internal_secret_empty_yaml
(4902-4909)File_deploy_warp_warp_yaml
(6967-7006)File_deploy_warp_warp_svc_yaml
(6950-6963)File_deploy_warp_warp_job_yaml
(6929-6946)File_deploy_internal_service_s3_yaml
(4966-4992)pkg/options/options.go (1)
Namespace
(57-57)
🪛 YAMLlint (1.37.1)
deploy/warp/warp-svc.yaml
[error] 14-14: no new line character at the end of file
(new-line-at-end-of-file)
🪛 Checkov (3.2.334)
deploy/warp/warp.yaml
[medium] 1-38: Containers should not run with allowPrivilegeEscalation
(CKV_K8S_20)
[medium] 1-38: Minimize the admission of root containers
(CKV_K8S_23)
deploy/warp/warp-job.yaml
[medium] 1-16: Containers should not run with allowPrivilegeEscalation
(CKV_K8S_20)
[medium] 1-16: Minimize the admission of root containers
(CKV_K8S_23)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
- GitHub Check: run-admission-test
- GitHub Check: run-operator-tests
- GitHub Check: run-kms-tls-token-test
- GitHub Check: cnpg-deployment-test
- GitHub Check: run-hac-test
- GitHub Check: run-cli-tests
- GitHub Check: run-core-config-map-tests
- GitHub Check: run-azure-vault-test
- GitHub Check: run-kms-key-rotate-test
- GitHub Check: run-kms-kmip-test
- GitHub Check: run-kms-dev-test
- GitHub Check: run-kms-tls-sa-test
- GitHub Check: golangci-lint
🔇 Additional comments (4)
pkg/util/util.go (1)
2413-2420
: Generic Map helper looks goodPreallocated result and simple loop are fine.
pkg/cli/cli.go (1)
11-11
: CLI wiring for bench command looks correctpkg/cli/cli.go imports github.com/noobaa/noobaa-operator/v5/pkg/bench and registers bench.Cmd() in the "Advanced:" group; pkg/bench/warp.go implements the "bench" command with the "warp" subcommand (flags + RunBenchWarp, Args: cobra.ExactArgs(1)).
pkg/bench/warp.go (2)
201-211
: Resolved — MinIO Warp supports brace expansion in --warp-client Verified: Warp does its own brace expansion, so passing a brace pattern directly is valid and shell expansion isn't required; current code is fine.
213-215
: No change required — Warp accepts scheme-less host:port and uses --tls/--insecure for TLSWarp expects "host:port" (not a full URL); supplying "https://..." causes endpoint validation errors, so the current code appending --tls/--insecure is correct.
Signed-off-by: Utkarsh Srivastava <[email protected]>
a326d74
to
6ca0199
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (3)
deploy/warp/warp-job.yaml (1)
13-16
: Avoid :latest; add minimal security context, resources, and TTLEven though the CLI overrides image/env/args at runtime, it does not touch security context, resources, or ttlSecondsAfterFinished. Hardening here sets sane defaults and ensures finished Jobs get garbage‑collected.
spec: template: spec: containers: - name: warp-job env: - name: WARP_ACCESS_KEY - name: WARP_SECRET_KEY - image: "minio/warp:latest" - imagePullPolicy: Always + image: "minio/warp:vX.Y.Z" # pin an immutable tag; CLI can still override + imagePullPolicy: IfNotPresent + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + resources: + requests: + cpu: "1" + memory: "1Gi" + limits: + cpu: "2" + memory: "2Gi" restartPolicy: Never backoffLimit: 0 + ttlSecondsAfterFinished: 3600deploy/warp/warp.yaml (1)
31-38
: Pin image and add security context/resourcesMirror the Job hardening in the StatefulSet. The CLI only overrides the image tag, not security/resource fields.
- name: warp - image: "minio/warp:latest" - imagePullPolicy: Always + image: "minio/warp:vX.Y.Z" # pin; CLI can override + imagePullPolicy: IfNotPresent args: - client ports: - name: http containerPort: 7761 + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + resources: + requests: + cpu: "500m" + memory: "512Mi" + limits: + cpu: "2" + memory: "2Gi"pkg/bench/warp.go (1)
371-406
: NodePort discovery collects only one IP; broaden and add fallbackGather all ExternalIPs, and if none exist, all InternalIPs, without early breaks. This improves robustness on diverse clusters.
- for _, node := range nodes.Items { - for _, address := range node.Status.Addresses { - if address.Type == corev1.NodeExternalIP { - ips = append(ips, address.Address) - } - } - // Use the first external IP we find on the node - if len(ips) > 0 { - break - } - } - // Fallback to interalIP if no external found - if len(ips) == 0 { - for _, node := range nodes.Items { - for _, a := range node.Status.Addresses { - if a.Type == corev1.NodeInternalIP { - ips = append(ips, a.Address) - } - } - if len(ips) > 0 { - break - } - } - } + // Prefer all ExternalIPs; fallback to all InternalIPs if none + for _, node := range nodes.Items { + for _, a := range node.Status.Addresses { + if a.Type == corev1.NodeExternalIP { + ips = append(ips, a.Address) + } + } + } + if len(ips) == 0 { + for _, node := range nodes.Items { + for _, a := range node.Status.Addresses { + if a.Type == corev1.NodeInternalIP { + ips = append(ips, a.Address) + } + } + } + }
🧹 Nitpick comments (10)
deploy/warp/warp-svc.yaml (2)
14-14
: Add trailing newline to satisfy linters/toolsFile lacks a newline at EOF; add one to quiet YAMLlint and keep diffs clean.
10-12
: Optional: align port name with container’s named port (“http”)Not required, but renaming the Service port from “warp” to “http” matches the containerPort name in deploy/warp/warp.yaml and aids consistency in tooling.
Apply:
ports: - - port: 7761 - name: warp + - port: 7761 + name: httppkg/bench/warp.go (8)
67-69
: Defaulting to :latest is riskyConsider defaulting to a tested, immutable tag (and document how to override with --image). This avoids accidental upgrades changing benchmark behavior.
-cmd.Flags().String( - "image", "minio/warp:latest", - "Warp image", -) +cmd.Flags().String( + "image", "minio/warp:vX.Y.Z", + "Warp image (override with a pinned tag)", +)
126-129
: Redundant zero check after getClientNumsgetClientNums() already validates nodes and sets a non‑zero default. The extra check is dead code.
-clients = getClientNums(clients) -if clients == 0 { - log.Fatal("❌ Number of clients cannot be 0") -} +clients = getClientNums(clients)
171-172
: Typo in fatal message“Unexepected” → “Unexpected”.
- log.Fatal("❌ Unexepected number of containers in the Warp STS") + log.Fatal("❌ Unexpected number of containers in the Warp STS")
190-193
: Create the headless Service before the StatefulSetBest practice is to ensure the headless Service exists so pod DNS (warp-0.warp..svc) is resolvable as pods start. Here it’s created after the STS is Ready; move it earlier.
Add before applying the StatefulSet (after setting namespace/replicas):
warpSts.Namespace = options.Namespace warpSts.Spec.Replicas = &clients if image != "" { containers[0].Image = image } +// Ensure headless Service exists for stable DNS names +warpSvc := util.KubeObject(bundle.File_deploy_warp_warp_svc_yaml).(*corev1.Service) +warpSvc.Namespace = options.Namespace +util.KubeApply(warpSvc) util.KubeApply(warpSts)Remove the later block:
- warpSvc := util.KubeObject(bundle.File_deploy_warp_warp_svc_yaml).(*corev1.Service) - warpSvc.Namespace = options.Namespace - util.KubeApply(warpSvc)Also applies to: 174-181
291-303
: Close pod log streams to avoid leaksRelease ReadClosers after copying.
- for _, container := range logs { - if _, err := io.Copy(os.Stdout, container); err != nil { + for name, r := range logs { + if _, err := io.Copy(os.Stdout, r); err != nil { log.Warn("encountered error while copying logs -", err) } + if err := r.Close(); err != nil { + log.Debugf("failed closing log stream for %s: %v", name, err) + } }
407-422
: Optional: include all LoadBalancer ingress entriesAppend all IPs/hostnames, not just index 0.
- if len(s3svc.Status.LoadBalancer.Ingress) == 0 { + if len(s3svc.Status.LoadBalancer.Ingress) == 0 { log.Fatal("❌ Failed to find loadbalancer ingress") } - - if s3svc.Status.LoadBalancer.Ingress[0].IP != "" { - ips = append(ips, s3svc.Status.LoadBalancer.Ingress[0].IP) - } else if s3svc.Status.LoadBalancer.Ingress[0].Hostname != "" { - ips = append(ips, s3svc.Status.LoadBalancer.Ingress[0].Hostname) - } else { - log.Fatal("❌ Failed to find loadbalancer IP/Hostname") - } + for _, ing := range s3svc.Status.LoadBalancer.Ingress { + if ing.IP != "" { + ips = append(ips, ing.IP) + } else if ing.Hostname != "" { + ips = append(ips, ing.Hostname) + } + } + if len(ips) == 0 { + log.Fatal("❌ Failed to find loadbalancer IP/Hostname") + }
424-427
: Guard against unresolved port namesIf the named port isn’t found, port remains 0 and host list becomes invalid; fail fast.
getPort := func(svc *corev1.Service, portname string, fn func(corev1.ServicePort) int32) int32 { for _, port := range svc.Spec.Ports { if port.Name == portname { return fn(port) } } - - return 0 + return 0 } @@ - return strings.Join(util.Map(ips, func(ip string) string { + if port == 0 { + log.Fatalf("❌ failed to resolve port %q on S3 service", portname) + } + return strings.Join(util.Map(ips, func(ip string) string { return fmt.Sprintf("%s:%d", ip, port) }), ",")Also applies to: 335-343
439-444
: Unreachable warning after reassignmentThe warn branch can never trigger after setting clients = nodeCount. Preserve original input to decide whether to warn.
-func getClientNums(clients int32) int32 { +func getClientNums(clients int32) int32 { ... - if clients > int32(nodeCount) || clients == 0 { - clients = int32(nodeCount) - if clients > int32(nodeCount) { - log.Warn("Number of clients cannot exceed number of nodes") - } - } + if clients > int32(nodeCount) || clients == 0 { + if clients > int32(nodeCount) { + log.Warn("Number of clients cannot exceed number of nodes") + } + clients = int32(nodeCount) + } return clients }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
deploy/warp/warp-job.yaml
(1 hunks)deploy/warp/warp-svc.yaml
(1 hunks)deploy/warp/warp.yaml
(1 hunks)pkg/admission/server.go
(2 hunks)pkg/bench/warp.go
(1 hunks)pkg/bundle/deploy.go
(1 hunks)pkg/cli/cli.go
(4 hunks)pkg/util/util.go
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- pkg/bundle/deploy.go
- pkg/admission/server.go
- pkg/util/util.go
- pkg/cli/cli.go
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: tangledbytes
PR: noobaa/noobaa-operator#1702
File: deploy/warp/warp-job.yaml:9-16
Timestamp: 2025-09-16T11:55:50.631Z
Learning: In the noobaa-operator warp benchmark feature, the warp-job.yaml template serves as a base that gets modified by the CLI code in pkg/bench/warp.go. The CLI dynamically overwrites the container image, environment variables, and args fields, but does not modify security context, resource limits, or TTL settings.
Learnt from: tangledbytes
PR: noobaa/noobaa-operator#1702
File: pkg/bundle/deploy.go:6929-6947
Timestamp: 2025-09-16T12:37:45.701Z
Learning: The bench CLI (pkg/bench/warp.go) dynamically overrides the Warp Job’s image, env, and args; it does not modify ttlSecondsAfterFinished or resource requests. Keeping TTL/resources in the embedded YAML is the intended pattern.
📚 Learning: 2025-09-16T11:55:50.631Z
Learnt from: tangledbytes
PR: noobaa/noobaa-operator#1702
File: deploy/warp/warp-job.yaml:9-16
Timestamp: 2025-09-16T11:55:50.631Z
Learning: In the noobaa-operator warp benchmark feature, the warp-job.yaml template serves as a base that gets modified by the CLI code in pkg/bench/warp.go. The CLI dynamically overwrites the container image, environment variables, and args fields, but does not modify security context, resource limits, or TTL settings.
Applied to files:
pkg/bench/warp.go
deploy/warp/warp-job.yaml
deploy/warp/warp-svc.yaml
deploy/warp/warp.yaml
📚 Learning: 2025-09-16T12:37:45.701Z
Learnt from: tangledbytes
PR: noobaa/noobaa-operator#1702
File: pkg/bundle/deploy.go:6929-6947
Timestamp: 2025-09-16T12:37:45.701Z
Learning: The bench CLI (pkg/bench/warp.go) dynamically overrides the Warp Job’s image, env, and args; it does not modify ttlSecondsAfterFinished or resource requests. Keeping TTL/resources in the embedded YAML is the intended pattern.
Applied to files:
pkg/bench/warp.go
deploy/warp/warp-job.yaml
deploy/warp/warp.yaml
🧬 Code graph analysis (1)
pkg/bench/warp.go (3)
pkg/util/util.go (12)
Logger
(904-906)GetFlagStringOrPrompt
(1316-1328)KubeObject
(284-294)KubeCheck
(570-591)OnSignal
(2422-2428)KubeApply
(298-334)Context
(909-911)KubeCheckQuiet
(619-632)KubeList
(647-668)GetPodLogs
(715-754)KubeDelete
(445-498)Map
(2413-2420)pkg/bundle/deploy.go (5)
File_deploy_internal_secret_empty_yaml
(4902-4909)File_deploy_warp_warp_yaml
(6967-7006)File_deploy_warp_warp_svc_yaml
(6950-6963)File_deploy_warp_warp_job_yaml
(6929-6946)File_deploy_internal_service_s3_yaml
(4966-4992)pkg/options/options.go (1)
Namespace
(57-57)
🪛 Checkov (3.2.334)
deploy/warp/warp-job.yaml
[medium] 1-16: Containers should not run with allowPrivilegeEscalation
(CKV_K8S_20)
[medium] 1-16: Minimize the admission of root containers
(CKV_K8S_23)
deploy/warp/warp.yaml
[medium] 1-38: Containers should not run with allowPrivilegeEscalation
(CKV_K8S_20)
[medium] 1-38: Minimize the admission of root containers
(CKV_K8S_23)
🪛 YAMLlint (1.37.1)
deploy/warp/warp-svc.yaml
[error] 14-14: no new line character at the end of file
(new-line-at-end-of-file)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
- GitHub Check: cnpg-deployment-test
- GitHub Check: run-kms-kmip-test
- GitHub Check: run-kms-tls-token-test
- GitHub Check: run-kms-tls-sa-test
- GitHub Check: run-admission-test
- GitHub Check: run-azure-vault-test
- GitHub Check: golangci-lint
- GitHub Check: run-core-config-map-tests
- GitHub Check: run-kms-dev-test
- GitHub Check: run-cli-tests
- GitHub Check: run-hac-test
- GitHub Check: run-operator-tests
- GitHub Check: run-kms-key-rotate-test
🔇 Additional comments (5)
deploy/warp/warp-job.yaml (1)
10-12
: Env var names are correct for WarpWarp supports WARP_ACCESS_KEY/WARP_SECRET_KEY and corresponding CLI flags. Keeping envs is fine. (github.com)
pkg/bench/warp.go (4)
62-65
: Good: clients flag defined as Int32 to match GetInt32This fixes the earlier type mismatch concern.
254-255
: LGTM: no fatal on KubeApply(false)Treating “updated” as success avoids erroneous exits when the Job already exists.
330-355
: LGTM: internal DNS uses fixed “s3” with HTTPS handled via portThis avoids constructing non-existent DNS names like s3-https..svc; selecting the HTTPS port by name is correct.
205-216
: Credentials envs align with Warp docs; flags also supportedUsing envs is fine; alternatively you could pass --access-key/--secret-key instead of setting envs. (github.com)
If you’d like, I can switch to flags for credentials to keep all configuration visible in args; say the word.
if useHttps { | ||
args = append(args, "--insecure", "--tls") | ||
} | ||
if warpArgs != "" { | ||
args = append(args, strings.Split(warpArgs, " ")...) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
warp-args splitting breaks quotes; use shlex
strings.Split cannot handle quoted args or escaped spaces, causing incorrect Warp invocations. Parse like a shell.
Import:
import (
"context"
"fmt"
"io"
"os"
"slices"
"strings"
"syscall"
"time"
+ "github.com/google/shlex"
Replace splitting:
- if warpArgs != "" {
- args = append(args, strings.Split(warpArgs, " ")...)
- }
+ if warpArgs != "" {
+ parsed, err := shlex.Split(warpArgs)
+ if err != nil {
+ util.Logger().Fatalf("❌ failed to parse --warp-args: %v", err)
+ }
+ args = append(args, parsed...)
+ }
Also applies to: 3-23
🤖 Prompt for AI Agents
In pkg/bench/warp.go around lines 241 to 247, using strings.Split(warpArgs, " ")
breaks quoted/escaped arguments; replace it with a shell-style splitter (e.g.
import github.com/google/shlex and call shlex.Split(warpArgs)) and append the
returned slice to args, handling the possible error from shlex.Split (log/return
on error). Apply the same replacement to the other occurrences mentioned (lines
~3-23) so all warpArgs parsing uses shlex.Split and proper error handling.
Explain the changes
Add support for
noobaa bench warp
command. Example:Sample Run
Issues: Fixed #xxx / Gap #xxx
Testing Instructions:
Summary by CodeRabbit