-
Notifications
You must be signed in to change notification settings - Fork 38
feat(byoo-perf): add scaffolding, translation-driven rendering, and validation #422
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: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| 0.157.0 | ||
| 0.158.0 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| # BYOO Collector Performance Test Suite | ||
|
|
||
| A repeatable performance test suite for the BYOO OpenTelemetry collector. It | ||
| exercises the collector under controlled telemetry load using the same workload | ||
| shape produced in production, by rendering through the shared translation | ||
| library (`icms-translate`) rather than hand-written collector manifests. | ||
|
|
||
| > Status: first milestone. Only rendering and render-shape validation are | ||
| > implemented today. Deployment, load generation, the OTLP sink, measurements, | ||
| > and reporting land in later milestones. | ||
|
|
||
| ## Why translation-driven | ||
|
|
||
| The collector spec and config are generated by calling | ||
| `function.Translate(msg, tcfg)` from the shared translation library, so the | ||
| suite tests the identical collector spec and config that would be deployed: | ||
|
|
||
| - Container functions get the collector as a workload sidecar. | ||
| - Helm functions get the collector on the generated utils pod, fronted by a | ||
| ClusterIP OTLP Service. | ||
|
|
||
| The suite extracts the authentic collector container and, for deployment, runs | ||
| it with lightweight stand-ins for the non-collector containers, which are not | ||
| what the suite measures. | ||
|
|
||
| ## Layout | ||
|
|
||
| - `cmd/perf` — CLI entrypoint (`render`, `run`, `cleanup`). | ||
| - `pkg/spec` — synthetic container/Helm launch specs and translate config. | ||
| - `pkg/render` — runs `function.Translate` and extracts the collector. | ||
| - `pkg/validate` — render-shape validation gate. | ||
| - `pkg/profile` — `dev` and `baseline` execution profiles. | ||
|
|
||
| This is a standalone Go module. Run its Go commands with `GOWORK=off`: | ||
|
|
||
| ```bash | ||
| GOWORK=off go build ./... | ||
| GOWORK=off go test ./... | ||
| ``` | ||
|
|
||
| ## Usage | ||
|
|
||
| ```bash | ||
| # Render and validate both shapes (no cluster required). | ||
| GOWORK=off go run ./cmd/perf render --shape both | ||
|
|
||
| # Render one shape and print the deployable workload as YAML. | ||
| GOWORK=off go run ./cmd/perf render --shape container --output yaml | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will this run it using kubectl against a cluster? |
||
| ``` | ||
|
|
||
| ### `render` flags | ||
|
|
||
| - `--shape` (`both`): `container`, `helm`, or `both`. | ||
| - `--profile` (`dev`): `dev` or `baseline`. | ||
| - `--collector-image`: BYOO collector image reference. | ||
| - `--namespace` (`byoo-perf`): namespace for rendered objects. | ||
| - `--output` (`summary`): `summary`, `yaml`, or `json`. | ||
|
|
||
| `run` and `cleanup` are scaffolded and return a clear "not implemented" message. | ||
|
|
||
| ## Execution profiles | ||
|
|
||
| - `dev`: short run (10s warmup, 30s window, 1 repetition) for wiring and local | ||
| iteration. | ||
| - `baseline`: longer, repeatable run (60s warmup, 5m window, 3 repetitions). | ||
|
|
||
| All profile values are documented defaults and will be overridable per run. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,207 @@ | ||
| /* | ||
| SPDX-FileCopyrightText: Copyright (c) NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. | ||
| You may obtain a copy of the License at | ||
|
|
||
| http://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| Unless required by applicable law or agreed to in writing, software | ||
| distributed under the License is distributed on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| See the License for the specific language governing permissions and | ||
| limitations under the License. | ||
| */ | ||
|
|
||
| // Command perf is the entrypoint for the BYOO collector performance test | ||
| // suite. In this first milestone only "render" is fully wired; "run" and | ||
| // "cleanup" are scaffolding for the deployment/load milestones (S4+). | ||
| package main | ||
|
|
||
| import ( | ||
| "flag" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This CLI is fairly comprehensive with many of sub-commands. I would recommend using cobra as we were are with other CLIs |
||
| "fmt" | ||
| "os" | ||
| "strings" | ||
|
|
||
| "sigs.k8s.io/yaml" | ||
|
|
||
| "github.com/NVIDIA/nvcf/src/libraries/go/lib/pkg/icms-translate/translate/common" | ||
|
|
||
| "github.com/NVIDIA/nvcf/src/compute-plane-services/byoo-otel-collector/perf/pkg/profile" | ||
| "github.com/NVIDIA/nvcf/src/compute-plane-services/byoo-otel-collector/perf/pkg/render" | ||
| "github.com/NVIDIA/nvcf/src/compute-plane-services/byoo-otel-collector/perf/pkg/spec" | ||
| "github.com/NVIDIA/nvcf/src/compute-plane-services/byoo-otel-collector/perf/pkg/validate" | ||
| ) | ||
|
|
||
| func main() { | ||
| if len(os.Args) < 2 { | ||
| usage() | ||
| os.Exit(2) | ||
| } | ||
|
|
||
| var err error | ||
| switch os.Args[1] { | ||
| case "render": | ||
| err = cmdRender(os.Args[2:]) | ||
| case "run": | ||
| err = cmdRun(os.Args[2:]) | ||
| case "cleanup": | ||
| err = cmdCleanup(os.Args[2:]) | ||
| case "-h", "--help", "help": | ||
| usage() | ||
| return | ||
| default: | ||
| fmt.Fprintf(os.Stderr, "unknown command %q\n\n", os.Args[1]) | ||
| usage() | ||
| os.Exit(2) | ||
| } | ||
|
|
||
| if err != nil { | ||
| fmt.Fprintf(os.Stderr, "error: %v\n", err) | ||
| os.Exit(1) | ||
| } | ||
| } | ||
|
|
||
| func usage() { | ||
| fmt.Fprint(os.Stderr, `perf - BYOO OpenTelemetry collector performance test suite | ||
|
|
||
| Usage: | ||
| perf <command> [flags] | ||
|
|
||
| Commands: | ||
| render Render the production workload shape via icms-translate and validate it (no cluster required). | ||
| run Deploy, drive load, and measure (not yet implemented; see S4+). | ||
| cleanup Remove test resources (not yet implemented; see S5+). | ||
|
|
||
| Run "perf <command> -h" for command flags. | ||
| `) | ||
| } | ||
|
|
||
| func cmdRender(args []string) error { | ||
| fs := flag.NewFlagSet("render", flag.ContinueOnError) | ||
| shapeFlag := fs.String("shape", "both", `deployment shape: "container", "helm", or "both"`) | ||
| profileFlag := fs.String("profile", "dev", `execution profile: "dev" or "baseline"`) | ||
| collectorImage := fs.String("collector-image", spec.DefaultCollectorImage, "BYOO collector image reference") | ||
| namespace := fs.String("namespace", "byoo-perf", "namespace for rendered objects") | ||
| output := fs.String("output", "summary", `output format: "summary", "yaml", or "json"`) | ||
| if err := fs.Parse(args); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| prof, err := profile.Lookup(*profileFlag) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| shapes, err := shapesFromFlag(*shapeFlag) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| opts := spec.DefaultOptions() | ||
| opts.Namespace = *namespace | ||
| opts.CollectorImage = *collectorImage | ||
|
|
||
| exp := validate.Expectations{ | ||
| Image: opts.CollectorImage, | ||
| Resources: common.GetDefaultContainerResourcesBYOO(), | ||
| } | ||
|
|
||
| fmt.Printf("profile=%s warmup=%s window=%s reps=%d\n\n", prof.Name, prof.Warmup, prof.MeasurementWindow, prof.Repetitions) | ||
|
|
||
| for _, shape := range shapes { | ||
| res, err := render.Render(shape, opts) | ||
| if err != nil { | ||
| return fmt.Errorf("render %s: %w", shape, err) | ||
| } | ||
| if err := validate.Render(res, exp); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| switch *output { | ||
| case "summary": | ||
| printSummary(res) | ||
| case "yaml", "json": | ||
| if err := printObjects(res, *namespace, *output); err != nil { | ||
| return err | ||
| } | ||
| default: | ||
| return fmt.Errorf("unknown output %q (want \"summary\", \"yaml\", or \"json\")", *output) | ||
| } | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| func printSummary(res *render.Result) { | ||
| fmt.Printf("[%s] VALID\n", res.Shape) | ||
| fmt.Printf(" collector image : %s\n", res.Collector.Image) | ||
| fmt.Printf(" config version : %s\n", res.OTelVersion) | ||
| fmt.Printf(" owner pod : %s\n", res.OwnerPod) | ||
| if res.Service != nil { | ||
| fmt.Printf(" otlp service : %s\n", res.Service.Name) | ||
| } | ||
| fmt.Printf(" ports : %s\n", portSummary(res)) | ||
| fmt.Printf(" objects : %d translated\n\n", len(res.Objects)) | ||
| } | ||
|
|
||
| func portSummary(res *render.Result) string { | ||
| parts := make([]string, 0, len(res.Collector.Ports)) | ||
| for _, p := range res.Collector.Ports { | ||
| parts = append(parts, fmt.Sprintf("%s:%d", p.Name, p.ContainerPort)) | ||
| } | ||
| return strings.Join(parts, " ") | ||
| } | ||
|
|
||
| func printObjects(res *render.Result, namespace, format string) error { | ||
| pod := res.BenchPod(namespace) | ||
| out, err := yaml.Marshal(pod) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal bench pod: %w", err) | ||
| } | ||
| if format == "json" { | ||
| out, err = yaml.YAMLToJSON(out) | ||
| if err != nil { | ||
| return fmt.Errorf("convert to json: %w", err) | ||
| } | ||
| } | ||
| fmt.Printf("# shape=%s benchmark workload (authentic collector + emptyDir stand-ins)\n", res.Shape) | ||
| fmt.Printf("%s\n", out) | ||
| return nil | ||
| } | ||
|
|
||
| func cmdRun(args []string) error { | ||
| fs := flag.NewFlagSet("run", flag.ContinueOnError) | ||
| _ = fs.String("shape", "both", `deployment shape: "container", "helm", or "both"`) | ||
| _ = fs.String("profile", "dev", `execution profile: "dev" or "baseline"`) | ||
| _ = fs.String("mode", "k3d", `deployment mode: "k3d" or "remote"`) | ||
| _ = fs.Bool("retain", false, "retain test resources for debugging instead of cleaning up") | ||
| if err := fs.Parse(args); err != nil { | ||
| return err | ||
| } | ||
| return fmt.Errorf("`run` is not implemented yet; it lands with the deployment/load milestones (S4-S9)") | ||
| } | ||
|
|
||
| func cmdCleanup(args []string) error { | ||
| fs := flag.NewFlagSet("cleanup", flag.ContinueOnError) | ||
| _ = fs.String("mode", "k3d", `deployment mode: "k3d" or "remote"`) | ||
| _ = fs.String("namespace", "byoo-perf", "namespace to clean up") | ||
| if err := fs.Parse(args); err != nil { | ||
| return err | ||
| } | ||
| return fmt.Errorf("`cleanup` is not implemented yet; it lands with the deployment milestone (S5)") | ||
| } | ||
|
|
||
| func shapesFromFlag(s string) ([]spec.Shape, error) { | ||
| switch s { | ||
| case "container": | ||
| return []spec.Shape{spec.ShapeContainer}, nil | ||
| case "helm": | ||
| return []spec.Shape{spec.ShapeHelm}, nil | ||
| case "both": | ||
| return []spec.Shape{spec.ShapeContainer, spec.ShapeHelm}, nil | ||
| default: | ||
| return nil, fmt.Errorf("unknown shape %q (want \"container\", \"helm\", or \"both\")", s) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| module github.com/NVIDIA/nvcf/src/compute-plane-services/byoo-otel-collector/perf | ||
|
|
||
| go 1.25.0 | ||
|
|
||
| require ( | ||
| github.com/NVIDIA/nvcf/src/libraries/go/lib v0.0.0 | ||
| k8s.io/api v0.34.2 | ||
| k8s.io/apimachinery v0.34.2 | ||
| sigs.k8s.io/yaml v1.6.0 | ||
| ) | ||
|
|
||
| require ( | ||
| dario.cat/mergo v1.0.2 // indirect | ||
| github.com/Masterminds/semver/v3 v3.4.0 // indirect | ||
| github.com/fxamacker/cbor/v2 v2.9.0 // indirect | ||
| github.com/go-logr/logr v1.4.3 // indirect | ||
| github.com/gogo/protobuf v1.3.2 // indirect | ||
| github.com/golang-jwt/jwt/v5 v5.3.1 // indirect | ||
| github.com/google/go-containerregistry v0.21.5 // indirect | ||
| github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect | ||
| github.com/google/uuid v1.6.0 // indirect | ||
| github.com/json-iterator/go v1.1.12 // indirect | ||
| github.com/kr/text v0.2.0 // indirect | ||
| github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||
| github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect | ||
| github.com/opencontainers/go-digest v1.0.0 // indirect | ||
| github.com/x448/float16 v0.8.4 // indirect | ||
| go.yaml.in/yaml/v2 v2.4.4 // indirect | ||
| golang.org/x/net v0.55.0 // indirect | ||
| golang.org/x/text v0.37.0 // indirect | ||
| gopkg.in/inf.v0 v0.9.1 // indirect | ||
| gopkg.in/yaml.v3 v3.0.1 // indirect | ||
| k8s.io/klog/v2 v2.130.1 // indirect | ||
| k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect | ||
| sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect | ||
| sigs.k8s.io/randfill v1.0.0 // indirect | ||
| sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect | ||
| ) | ||
|
|
||
| // The perf suite consumes the shared SIS translation library directly so that | ||
| // it renders exactly the workload shape NVCA produces in production. | ||
| replace github.com/NVIDIA/nvcf/src/libraries/go/lib => ../../../libraries/go/lib | ||
|
|
||
| // Mirror the k8s pins used by the shared library. replace directives in | ||
| // dependencies are not inherited, so we repeat them here for the main module. | ||
| replace ( | ||
| k8s.io/api => k8s.io/api v0.34.2 | ||
| k8s.io/apimachinery => k8s.io/apimachinery v0.34.2 | ||
| k8s.io/client-go => k8s.io/client-go v0.34.2 | ||
| ) |
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.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: NVIDIA/nvcf
Length of output: 50368
🏁 Script executed:
Repository: NVIDIA/nvcf
Length of output: 14758
Align
VERSIONwith the collector configsrc/compute-plane-services/byoo-otel-collector/VERSIONis0.158.0, butsrc/compute-plane-services/byoo-otel-collector/otel-collector-build.yamlstill pinsv0.157.0throughout. This drifts the release version from the collector major/minor and will produce inconsistent release metadata.🤖 Prompt for AI Agents
Source: Coding guidelines