From 5bc064f71ee57ebea5dfba71d973bcb5ec1e6c56 Mon Sep 17 00:00:00 2001 From: Melvin Hillsman Date: Fri, 12 Jun 2026 00:06:56 -0500 Subject: [PATCH 1/4] docs: add params for all built-in workloads to configuration reference Document the 14 new per-workload params (cpu, memory, disk, database, network) in the YAML example and params table, and remove the note that core workloads accept no params. Signed-off-by: Melvin Hillsman --- docs/configuration.md | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 4191c9e..395c0aa 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -140,15 +140,35 @@ workloads: vm_count: 2 cpu_cores: 4 memory: 4Gi + params: + cpu-load-percent: "50" # default 100 + cpu-method: "matrixprod" # default all memory: vm_count: 1 # enabled by default when not specified + params: + memory-percent: "60" # default 80 + vm-stressors: "2" # default 1 + vm-method: "flip" # default all disk: enabled: false # skip this workload entirely + params: + block-size-rw: "8k" # default 4k + block-size-seq: "256k" # default 128k + rwmixread: "50" # default 70 + numjobs: "8" # default 4 + runtime: "600" # default 300 database: cpu_cores: 2 memory: 4Gi + params: + scale-factor: "100" # default 50 + clients: "20" # default 10 + duration: "600" # default 300 network: vm_count: 1 # creates 1 server + 1 client = 2 VMs + params: + parallel-streams: "8" # default 4 + test-duration: "120" # default 60 tps: vm_count: 1 # creates 1 server + 1 client = 2 VMs params: @@ -215,8 +235,21 @@ Each workload's `params` block accepts string-valued keys. The current parameter | **chaos-process** | `signal` | `SIGTERM` | Signal sent to victims | | **chaos-process** | `interval` | `30` | Seconds between kills | | **chaos-process** | `min-pid` | `1000` | Minimum PID considered eligible | - -Workloads without entries above accept no per-workload `params` today (cpu, memory, disk, database, network). +| **cpu** | `cpu-load-percent` | `100` | Target CPU load percentage for stress-ng (`--cpu-load`) | +| **cpu** | `cpu-method` | `all` | CPU stressor method for stress-ng (`--cpu-method`) | +| **memory** | `memory-percent` | `80` | Target memory usage percentage (`--vm-bytes`) | +| **memory** | `vm-stressors` | `1` | Number of VM worker stressors (`--vm`) | +| **memory** | `vm-method` | `all` | Memory stressor method for stress-ng (`--vm-method`) | +| **disk** | `block-size-rw` | `4k` | Block size for the random read/write fio profile | +| **disk** | `block-size-seq` | `128k` | Block size for the sequential write fio profile | +| **disk** | `rwmixread` | `70` | Read percentage in the mixed read/write fio profile | +| **disk** | `numjobs` | `4` | Number of parallel fio jobs for the mixed read/write profile | +| **disk** | `runtime` | `300` | Runtime in seconds for each fio profile | +| **database** | `scale-factor` | `50` | pgbench initialization scale factor (`-s`) | +| **database** | `clients` | `10` | Number of concurrent pgbench clients (`-c`) | +| **database** | `duration` | `300` | Seconds per pgbench benchmark run (`-T`) | +| **network** | `parallel-streams` | `4` | Number of parallel iperf3 streams (`-P`) | +| **network** | `test-duration` | `60` | Seconds per iperf3 test run (`-t`) | --- @@ -293,7 +326,7 @@ The `--no-audit` flag is checked separately by `initAuditor` in `cmd/virtwork/ma - All config field definitions live in `internal/config/config.go` — `WorkloadConfig` struct, `Config` struct, `SetDefaults`, `BindFlags`, `LoadConfig`. - The mapstructure tag on each field is the YAML key name (e.g., `mapstructure:"data-disk-size"` → YAML key `data_disk_size` after the `_`/`-` replacement). Match this convention when adding new fields. -- Per-workload `params` are surfaced into the workload as `WorkloadConfig.Params map[string]string`. New chaos / multi-VM knobs go here so they're uniformly addressable from YAML. +- Per-workload `params` are surfaced into the workload as `WorkloadConfig.Params map[string]string`. All workloads use this mechanism for tunable knobs so they're uniformly addressable from YAML. - When adding a new env var, prefer letting Viper bind it automatically rather than hand-rolling `os.Getenv` (see `VIRTWORK_SSH_AUTHORIZED_KEYS` in `resolveSSHKeys` for the one current exception, driven by the comma-split list semantics). - Update this document, the ConfigMap default list, and the per-workload `params` table whenever you add a new knob. From eb65f703667c11d4b4eaa027861c3a5c05066348 Mon Sep 17 00:00:00 2001 From: Melvin Hillsman Date: Fri, 12 Jun 2026 00:21:08 -0500 Subject: [PATCH 2/4] docs: note configurable params in architecture, comparison, and overview Update hardcoded command examples in architecture.md, virtwork-vs-kube-burner.md, and guide/01-overview.md to reflect that all core workloads now accept tunable params via WorkloadConfig.Params. Signed-off-by: Melvin Hillsman --- docs/architecture.md | 10 +++++----- docs/guide/01-overview.md | 4 ++-- docs/virtwork-vs-kube-burner.md | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index 9be5141..7b0cdbd 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -358,11 +358,11 @@ classDiagram | Workload | VM Count | Data Volume | K8s Service | Packages | Workload Tool | |----------|----------|-------------|-------------|----------|---------------| -| CPU | N (configurable) | No | No | stress-ng | `stress-ng --cpu 0 --cpu-method all` | -| Memory | N (configurable) | No | No | stress-ng | `stress-ng --vm 1 --vm-bytes 80% --vm-method all` | -| Disk | N (configurable) | Yes (`/mnt/data`) | No | fio | Mixed R/W + sequential write profiles | -| Database | N (configurable) | Yes (`/var/lib/pgsql/data`) | No | postgresql-server | `pgbench -c 10 -j 2 -T 300` loop | -| Network | N × 2 (server + client) | No | Yes — `virtwork-iperf3-server` :5201 | iperf3 | `iperf3 -s` / `iperf3 -c ... --bidir` | +| CPU | N (configurable) | No | No | stress-ng | `stress-ng --cpu 0 --cpu-load 100 --cpu-method all` (defaults; tunable via `params`) | +| Memory | N (configurable) | No | No | stress-ng | `stress-ng --vm 1 --vm-bytes 80% --vm-method all` (defaults; tunable via `params`) | +| Disk | N (configurable) | Yes (`/mnt/data`) | No | fio | Mixed R/W + sequential write profiles (defaults; tunable via `params`) | +| Database | N (configurable) | Yes (`/var/lib/pgsql/data`) | No | postgresql-server | `pgbench -c 10 -j 2 -T 300` loop (defaults; tunable via `params`) | +| Network | N × 2 (server + client) | No | Yes — `virtwork-iperf3-server` :5201 | iperf3 | `iperf3 -s` / `iperf3 -c ... -P 4 -t 60 --bidir` (defaults; tunable via `params`) | | TPS | N × 2 (server + client) | No | Yes — `virtwork-tps-server` :12865 / :12866 / :8080 | netperf, python3 | `netperf -t TCP_RR` + curl HTTP file fetch loop | | Chaos-disk | N (configurable) | Yes (`/mnt/data`) | No | (golden image: `fallocate`, `dd`) | Fill to target percent, sleep, release, repeat | | Chaos-network | N (configurable) | No | No | iproute-tc (+ `sch_netem` kernel module) | `tc qdisc add ... netem delay 100ms loss 5%` | diff --git a/docs/guide/01-overview.md b/docs/guide/01-overview.md index 91404b5..aa6e2df 100644 --- a/docs/guide/01-overview.md +++ b/docs/guide/01-overview.md @@ -56,7 +56,7 @@ write_files: [Service] Type=simple - ExecStart=/usr/bin/stress-ng --cpu 0 --cpu-method all --timeout 0 + ExecStart=/usr/bin/stress-ng --cpu 0 --cpu-load 100 --cpu-method all --timeout 0 Restart=always RestartSec=10 @@ -72,7 +72,7 @@ runcmd: - virtwork-cpu.service ``` -This is a standard [cloud-init](https://cloudinit.readthedocs.io/) configuration. When the VM boots, cloud-init will install `stress-ng` from the package manager, write the systemd unit file, and enable the service. The workload starts automatically. +This is a standard [cloud-init](https://cloudinit.readthedocs.io/) configuration. When the VM boots, cloud-init will install `stress-ng` from the package manager, write the systemd unit file, and enable the service. The workload starts automatically. The values shown above are defaults — all core workloads accept per-workload `params` to override them (see [configuration.md](../configuration.md#per-workload-params-keys)). If SSH credentials were configured, a `users` section is appended with the SSH public keys and/or password. diff --git a/docs/virtwork-vs-kube-burner.md b/docs/virtwork-vs-kube-burner.md index bd88f20..5bf4564 100644 --- a/docs/virtwork-vs-kube-burner.md +++ b/docs/virtwork-vs-kube-burner.md @@ -62,10 +62,10 @@ Workloads deployed inside VMs: | Workload | Tool | What It Generates | |---|---|---| -| `cpu` | `stress-ng --cpu 0 --cpu-method all` | Continuous CPU pressure across all cores | -| `memory` | `stress-ng --vm 1 --vm-bytes 80%` | Sustained memory pressure at 80% | -| `database` | PostgreSQL + `pgbench -c 10 -j 2 -T 300` | Realistic OLTP database transactions | -| `network` | `iperf3 --bidir` (server + client VM pairs) | Bidirectional throughput between VMs | +| `cpu` | `stress-ng --cpu 0 --cpu-load 100 --cpu-method all` | Continuous CPU pressure across all cores (configurable via `params`) | +| `memory` | `stress-ng --vm 1 --vm-bytes 80% --vm-method all` | Sustained memory pressure (configurable via `params`) | +| `database` | PostgreSQL + `pgbench -c 10 -j 2 -T 300` | Realistic OLTP database transactions (configurable via `params`) | +| `network` | `iperf3 -P 4 -t 60 --bidir` (server + client VM pairs) | Bidirectional throughput between VMs (configurable via `params`) | | `tps` | `netperf` + `curl` (server + client VM pairs) | Multi-port HTTP throughput with configurable file size, iterations, and duration | | `disk` | `fio` with mixed random + sequential profiles | Mixed I/O patterns on a dedicated data disk | | `chaos-disk` | `fallocate`/`dd` fill-release loop | Sustained disk-pressure events on a data disk | From ddb47eebaa53be6f390fc2c601d0033b32ab98d5 Mon Sep 17 00:00:00 2001 From: Melvin Hillsman Date: Fri, 12 Jun 2026 00:26:04 -0500 Subject: [PATCH 3/4] docs: add configurable params guidance to development and tutorial docs Add "Going Further: Configurable Params" subsection to development.md with the getter-with-default pattern, template conversion, and 3-case test structure. Update canonical references in both development.md and guide/03-adding-a-workload.md to reflect that all workloads now use params. Add param-related items to the new-workload checklist. Signed-off-by: Melvin Hillsman --- docs/development.md | 52 +++++++++++++++++++++++++++++- docs/guide/03-adding-a-workload.md | 5 ++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/docs/development.md b/docs/development.md index c32fb56..b0470f8 100644 --- a/docs/development.md +++ b/docs/development.md @@ -591,7 +591,7 @@ Pattern: 5. Set `RequiresService()` to `true` and provide a `ServiceSpec()` selecting server VMs by the `virtwork/role: server` label that the orchestrator applies automatically. 6. Clients reach servers via the in-cluster DNS name `..svc.cluster.local` — never poll for pod IPs. -The canonical references are `internal/workloads/network.go` (simplest — one port, iperf3) and `internal/workloads/tps.go` (multi-port Service with configurable `Params` for `file-size`, `iterations`, `duration`). +The canonical references are `internal/workloads/network.go` (simplest — one port, iperf3) and `internal/workloads/tps.go` (multi-port Service). All workloads support configurable `Params` via the getter-with-default pattern (see [Configurable Params](#going-further-configurable-params) below). ### Going Further: Storage-Backed Workloads @@ -604,6 +604,56 @@ If your workload needs persistent storage inside the VM: Reference workloads: `disk.go` (single fio mount), `database.go` (PostgreSQL data dir), `chaos_disk.go` (fill/release loop). All three follow the same pattern. +### Going Further: Configurable Params + +All workloads expose tunable knobs through `WorkloadConfig.Params map[string]string`. Users set these in YAML config under `workloads..params`. The standard pattern is a nil-safe getter method with a hardcoded default: + +```go +func (w *MyWorkload) concurrency() string { + if w.Config.Params != nil { + if val, ok := w.Config.Params["concurrency"]; ok && val != "" { + return val + } + } + return "10" +} +``` + +Then use the getter when building cloud-init content. Convert your systemd unit constant to a template with `%s` placeholders and interpolate with `fmt.Sprintf`: + +```go +const mySystemdUnitTemplate = `[Unit] +Description=My workload + +[Service] +Type=simple +ExecStart=/usr/bin/my-tool --concurrency %s --duration %s +Restart=always + +[Install] +WantedBy=multi-user.target +` + +func (w *MyWorkload) CloudInitUserdata() (string, error) { + unit := fmt.Sprintf(mySystemdUnitTemplate, w.concurrency(), w.duration()) + return w.BuildCloudConfig(CloudConfigOpts{ + // ... + WriteFiles: []WriteFile{{ + Path: "/etc/systemd/system/virtwork-my-workload.service", + Content: unit, + }}, + }) +} +``` + +Every workload should have a `Context("param wiring")` test block with three cases: + +1. **Nil params** — `Params` field omitted, output contains default values +2. **Full override** — all param keys set, output reflects custom values +3. **Partial override** — some keys set, unset keys fall back to defaults + +See `internal/workloads/cpu_test.go` for the simplest example, or `disk_test.go` for a workload with multiple output files to verify. Document new param keys in `docs/configuration.md` — both in the YAML example and the params table. + ### Going Further: Structured Logging The `internal/logging` package provides a shared `*slog.Logger` returned by `NewLogger(w io.Writer, verbose bool)`. Use it instead of `fmt.Fprintf` or `log.Printf` in any code path under `cmd/` or in packages that perform I/O (`internal/wait` is the current example): diff --git a/docs/guide/03-adding-a-workload.md b/docs/guide/03-adding-a-workload.md index 505aa6e..d79be33 100644 --- a/docs/guide/03-adding-a-workload.md +++ b/docs/guide/03-adding-a-workload.md @@ -518,7 +518,7 @@ return w.BuildCloudConfig(CloudConfigOpts{ ### Making It Multi-VM -If your workload needs more than one role of VM (a server and one or more clients, for example), implement the `MultiVMWorkload` interface. The two canonical references are `internal/workloads/network.go` (simplest — one Service port, iperf3) and `internal/workloads/tps.go` (multi-port Service with configurable `Params` for `file-size`, `iterations`, `duration`). +If your workload needs more than one role of VM (a server and one or more clients, for example), implement the `MultiVMWorkload` interface. The two canonical references are `internal/workloads/network.go` (simplest — one Service port, iperf3) and `internal/workloads/tps.go` (multi-port Service). All workloads support configurable `Params` via the getter-with-default pattern — see [development.md](../development.md#going-further-configurable-params). 1. Add a `Namespace` field to your struct — the client needs it to build the server's in-cluster DNS name. 2. Implement `RoleDistribution() []RoleSpec` — return a slice of `RoleSpec{Role: "server", VMCount: 1}` entries declaring how many VMs each role needs. @@ -552,6 +552,9 @@ Before submitting a new workload, verify: - [ ] Packages used are available in Fedora's default repos (or pre-installed in the golden image, if applicable) - [ ] Systemd unit has `Restart=always` and `WantedBy=multi-user.target` - [ ] Tests cover: Name, packages, systemd unit content, valid YAML, VMResources, defaults for optional methods +- [ ] Tunable values exposed as `params` with getter-with-default methods (see [development.md](../development.md#going-further-configurable-params)) +- [ ] Param wiring tests: nil params → defaults, full override, partial override +- [ ] New param keys documented in `docs/configuration.md` (YAML example + params table) - [ ] Registered in `DefaultRegistry()` (`internal/workloads/registry.go`) with a factory function - [ ] Existing registry/orchestration test counts updated - [ ] `go test ./...` passes From 94d1729d17574e79bca6a4ade805c19bf86de7b4 Mon Sep 17 00:00:00 2001 From: Melvin Hillsman Date: Fri, 12 Jun 2026 00:29:45 -0500 Subject: [PATCH 4/4] docs: update workload tables in README with configurable params notes Show default flag values and note configurability via params for cpu, memory, disk, database, and network workloads in the root README workload tables. Signed-off-by: Melvin Hillsman --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index bf4b21b..b1d7485 100644 --- a/README.md +++ b/README.md @@ -106,16 +106,16 @@ virtwork ships nine built-in workloads, grouped by purpose. With `--vm-count 1` | Workload | VMs | Description | Tools | |----------|-----|-------------|-------| -| **cpu** | N | Continuous CPU stress | `stress-ng --cpu 0 --cpu-method all` | -| **memory** | N | Memory pressure at 80% | `stress-ng --vm 1 --vm-bytes 80%` | -| **disk** | N | Mixed random and sequential I/O on a data disk | `fio` with multiple profiles | -| **database** | N | PostgreSQL with pgbench loop on a data disk | `pgbench -c 10 -j 2 -T 300` | +| **cpu** | N | Continuous CPU stress | `stress-ng --cpu 0 --cpu-load 100 --cpu-method all` (configurable via `params`) | +| **memory** | N | Memory pressure | `stress-ng --vm 1 --vm-bytes 80% --vm-method all` (configurable via `params`) | +| **disk** | N | Mixed random and sequential I/O on a data disk | `fio` with multiple profiles (configurable via `params`) | +| **database** | N | PostgreSQL with pgbench loop on a data disk | `pgbench -c 10 -j 2 -T 300` (configurable via `params`) | **Multi-VM benchmarks** — server/client pairs coordinated via a ClusterIP Service: | Workload | VMs | Description | Tools | |----------|-----|-------------|-------| -| **network** | N × 2 | Bidirectional throughput | `iperf3 --bidir` on port 5201 | +| **network** | N × 2 | Bidirectional throughput | `iperf3 -P 4 -t 60 --bidir` on port 5201 (configurable via `params`) | | **tps** | N × 2 | TCP request/response + HTTP file transfer | `netperf` (12865/12866) + Python HTTP server (8080) | **Chaos engineering** — inject failures inside the VM to test resilience. ⚠️ See [docs/chaos-workloads.md](docs/chaos-workloads.md) before deploying: