From ce7adc152a85fdf467b2f6e6cc32359bc0391d9d Mon Sep 17 00:00:00 2001 From: Melvin Hillsman Date: Fri, 22 May 2026 19:12:10 -0500 Subject: [PATCH 1/2] feat(workloads): wire chaos workload params through WorkloadConfig.Params Read mount, fill-percent, fill-sleep, release-sleep (chaos-disk), latency-ms, packet-loss-percent (chaos-network), and signal, interval, min-pid (chaos-process) from Config.Params with documented defaults, mirroring the tps workload pattern. Convert hardcoded script constants and systemd units to format-string templates. Remove ChaosNetworkWorkload Latency/PacketLoss struct fields in favor of param accessors. Add tests for default values, full custom overrides, and partial param maps across all three chaos workloads. Signed-off-by: Melvin Hillsman --- internal/workloads/chaos_disk.go | 72 +++++++++++++-- internal/workloads/chaos_disk_test.go | 113 +++++++++++++++++++++++ internal/workloads/chaos_network.go | 32 ++++--- internal/workloads/chaos_network_test.go | 83 +++++++++++++++++ internal/workloads/chaos_process.go | 44 ++++++++- internal/workloads/chaos_process_test.go | 87 +++++++++++++++++ 6 files changed, 405 insertions(+), 26 deletions(-) diff --git a/internal/workloads/chaos_disk.go b/internal/workloads/chaos_disk.go index f1cf723..0606358 100644 --- a/internal/workloads/chaos_disk.go +++ b/internal/workloads/chaos_disk.go @@ -4,20 +4,22 @@ package workloads import ( + "fmt" + kubevirtv1 "kubevirt.io/api/core/v1" "github.com/opdev/virtwork/internal/config" "github.com/opdev/virtwork/internal/vm" ) -const chaosDiskScript = `#!/bin/bash +const chaosDiskScriptTemplate = `#!/bin/bash set -euo pipefail -MOUNT_POINT="${CHAOS_DISK_MOUNT:-/mnt/data}" -FILL_PERCENT="${CHAOS_DISK_FILL_PERCENT:-90}" +MOUNT_POINT="%s" +FILL_PERCENT="%s" FILL_FILE="${MOUNT_POINT}/chaos-disk-fill" -RELEASE_SLEEP="${CHAOS_DISK_RELEASE_SLEEP:-30}" -FILL_SLEEP="${CHAOS_DISK_FILL_SLEEP:-60}" +RELEASE_SLEEP="%s" +FILL_SLEEP="%s" while true; do TOTAL_KB=$(df -k "${MOUNT_POINT}" | awk 'NR==2 {print $2}') @@ -37,12 +39,16 @@ while true; do done ` -const chaosDiskSystemdUnit = `[Unit] +const chaosDiskSystemdUnitTemplate = `[Unit] Description=Virtwork chaos-disk fill/release workload After=network.target local-fs.target [Service] Type=simple +Environment="CHAOS_DISK_MOUNT=%s" +Environment="CHAOS_DISK_FILL_PERCENT=%s" +Environment="CHAOS_DISK_FILL_SLEEP=%s" +Environment="CHAOS_DISK_RELEASE_SLEEP=%s" ExecStart=/usr/local/bin/chaos-disk.sh Restart=always RestartSec=10 @@ -76,6 +82,42 @@ func NewChaosDiskWorkload( } } +func (w *ChaosDiskWorkload) mount() string { + if w.Config.Params != nil { + if val, ok := w.Config.Params["mount"]; ok && val != "" { + return val + } + } + return "/mnt/data" +} + +func (w *ChaosDiskWorkload) fillPercent() string { + if w.Config.Params != nil { + if val, ok := w.Config.Params["fill-percent"]; ok && val != "" { + return val + } + } + return "90" +} + +func (w *ChaosDiskWorkload) fillSleep() string { + if w.Config.Params != nil { + if val, ok := w.Config.Params["fill-sleep"]; ok && val != "" { + return val + } + } + return "60" +} + +func (w *ChaosDiskWorkload) releaseSleep() string { + if w.Config.Params != nil { + if val, ok := w.Config.Params["release-sleep"]; ok && val != "" { + return val + } + } + return "30" +} + // Name returns "chaos-disk". func (w *ChaosDiskWorkload) Name() string { return "chaos-disk" @@ -84,21 +126,33 @@ func (w *ChaosDiskWorkload) Name() string { // CloudInitUserdata returns cloud-init YAML that writes a fill/release script // and a systemd service that runs it in a loop. func (w *ChaosDiskWorkload) CloudInitUserdata() (string, error) { + mountPoint := w.mount() + script := fmt.Sprintf(chaosDiskScriptTemplate, + mountPoint, + w.fillPercent(), + w.releaseSleep(), + w.fillSleep()) + unit := fmt.Sprintf(chaosDiskSystemdUnitTemplate, + mountPoint, + w.fillPercent(), + w.fillSleep(), + w.releaseSleep()) + return w.BuildCloudConfig(CloudConfigOpts{ WriteFiles: []WriteFile{ { Path: "/usr/local/bin/virtwork-disk-setup.sh", - Content: diskSetupScript("virtwork-chdisk", "/mnt/data"), + Content: diskSetupScript("virtwork-chdisk", mountPoint), Permissions: "0755", }, { Path: "/usr/local/bin/chaos-disk.sh", - Content: chaosDiskScript, + Content: script, Permissions: "0755", }, { Path: "/etc/systemd/system/virtwork-chaos-disk.service", - Content: chaosDiskSystemdUnit, + Content: unit, Permissions: "0644", }, }, diff --git a/internal/workloads/chaos_disk_test.go b/internal/workloads/chaos_disk_test.go index 95a4540..b5a9dae 100644 --- a/internal/workloads/chaos_disk_test.go +++ b/internal/workloads/chaos_disk_test.go @@ -192,4 +192,117 @@ var _ = Describe("ChaosDiskWorkload", func() { Expect(cmdStrings).To(ContainElement(ContainSubstring("daemon-reload"))) Expect(cmdStrings).To(ContainElement(ContainSubstring("virtwork-chaos-disk.service"))) }) + + Context("param wiring", func() { + It("should use default param values when Params is nil", func() { + result, err := w.CloudInitUserdata() + Expect(err).NotTo(HaveOccurred()) + + parsed := parseYAML(result) + files := parsed["write_files"].([]interface{}) + + var scriptContent, unitContent, setupContent string + for _, f := range files { + fm := f.(map[string]interface{}) + switch fm["path"] { + case "/usr/local/bin/chaos-disk.sh": + scriptContent = fm["content"].(string) + case "/etc/systemd/system/virtwork-chaos-disk.service": + unitContent = fm["content"].(string) + case "/usr/local/bin/virtwork-disk-setup.sh": + setupContent = fm["content"].(string) + } + } + + Expect(scriptContent).To(ContainSubstring(`MOUNT_POINT="/mnt/data"`)) + Expect(scriptContent).To(ContainSubstring(`FILL_PERCENT="90"`)) + Expect(scriptContent).To(ContainSubstring(`FILL_SLEEP="60"`)) + Expect(scriptContent).To(ContainSubstring(`RELEASE_SLEEP="30"`)) + + Expect(unitContent).To(ContainSubstring("CHAOS_DISK_MOUNT=/mnt/data")) + Expect(unitContent).To(ContainSubstring("CHAOS_DISK_FILL_PERCENT=90")) + Expect(unitContent).To(ContainSubstring("CHAOS_DISK_FILL_SLEEP=60")) + Expect(unitContent).To(ContainSubstring("CHAOS_DISK_RELEASE_SLEEP=30")) + + Expect(setupContent).To(ContainSubstring("/mnt/data")) + }) + + It("should wire custom params from WorkloadConfig.Params", func() { + custom := workloads.NewChaosDiskWorkload(config.WorkloadConfig{ + Enabled: true, + VMCount: 1, + CPUCores: 2, + Memory: "2Gi", + Params: map[string]string{ + "mount": "/data", + "fill-percent": "75", + "fill-sleep": "120", + "release-sleep": "45", + }, + }, "10Gi", "virtwork", "", nil) + + result, err := custom.CloudInitUserdata() + Expect(err).NotTo(HaveOccurred()) + + parsed := parseYAML(result) + files := parsed["write_files"].([]interface{}) + + var scriptContent, unitContent, setupContent string + for _, f := range files { + fm := f.(map[string]interface{}) + switch fm["path"] { + case "/usr/local/bin/chaos-disk.sh": + scriptContent = fm["content"].(string) + case "/etc/systemd/system/virtwork-chaos-disk.service": + unitContent = fm["content"].(string) + case "/usr/local/bin/virtwork-disk-setup.sh": + setupContent = fm["content"].(string) + } + } + + Expect(scriptContent).To(ContainSubstring(`MOUNT_POINT="/data"`)) + Expect(scriptContent).To(ContainSubstring(`FILL_PERCENT="75"`)) + Expect(scriptContent).To(ContainSubstring(`FILL_SLEEP="120"`)) + Expect(scriptContent).To(ContainSubstring(`RELEASE_SLEEP="45"`)) + + Expect(unitContent).To(ContainSubstring("CHAOS_DISK_MOUNT=/data")) + Expect(unitContent).To(ContainSubstring("CHAOS_DISK_FILL_PERCENT=75")) + Expect(unitContent).To(ContainSubstring("CHAOS_DISK_FILL_SLEEP=120")) + Expect(unitContent).To(ContainSubstring("CHAOS_DISK_RELEASE_SLEEP=45")) + + Expect(setupContent).To(ContainSubstring("/data")) + }) + + It("should use defaults for missing individual params", func() { + partial := workloads.NewChaosDiskWorkload(config.WorkloadConfig{ + Enabled: true, + VMCount: 1, + CPUCores: 2, + Memory: "2Gi", + Params: map[string]string{ + "fill-percent": "50", + }, + }, "10Gi", "virtwork", "", nil) + + result, err := partial.CloudInitUserdata() + Expect(err).NotTo(HaveOccurred()) + + parsed := parseYAML(result) + files := parsed["write_files"].([]interface{}) + + var scriptContent string + for _, f := range files { + fm := f.(map[string]interface{}) + if fm["path"] == "/usr/local/bin/chaos-disk.sh" { + scriptContent = fm["content"].(string) + break + } + } + + Expect(scriptContent).To(ContainSubstring(`MOUNT_POINT="/mnt/data"`)) + Expect(scriptContent).To(ContainSubstring(`FILL_PERCENT="50"`)) + Expect(scriptContent).To(ContainSubstring(`FILL_SLEEP="60"`)) + Expect(scriptContent).To(ContainSubstring(`RELEASE_SLEEP="30"`)) + }) + }) }) diff --git a/internal/workloads/chaos_network.go b/internal/workloads/chaos_network.go index 75f4f50..84cd046 100644 --- a/internal/workloads/chaos_network.go +++ b/internal/workloads/chaos_network.go @@ -49,8 +49,6 @@ WantedBy=multi-user.target // using tc (traffic control) and netem (network emulation). type ChaosNetworkWorkload struct { BaseWorkload - Latency int // Latency in milliseconds (default: 100) - PacketLoss float64 // Packet loss percentage (default: 5.0) } // NewChaosNetworkWorkload creates a ChaosNetworkWorkload with the given configuration @@ -60,12 +58,6 @@ func NewChaosNetworkWorkload( sshUser, sshPassword string, sshKeys []string, ) *ChaosNetworkWorkload { - latency := 100 - packetLoss := 5.0 - - // Allow configuration via WorkloadConfig if needed in future - // For now, use sensible defaults as specified in the issue - return &ChaosNetworkWorkload{ BaseWorkload: BaseWorkload{ Config: cfg, @@ -73,11 +65,27 @@ func NewChaosNetworkWorkload( SSHPassword: sshPassword, SSHAuthorizedKeys: sshKeys, }, - Latency: latency, - PacketLoss: packetLoss, } } +func (w *ChaosNetworkWorkload) latencyMs() string { + if w.Config.Params != nil { + if val, ok := w.Config.Params["latency-ms"]; ok && val != "" { + return val + } + } + return "100" +} + +func (w *ChaosNetworkWorkload) packetLossPercent() string { + if w.Config.Params != nil { + if val, ok := w.Config.Params["packet-loss-percent"]; ok && val != "" { + return val + } + } + return "5.0" +} + // Name returns "chaos-network". func (w *ChaosNetworkWorkload) Name() string { return "chaos-network" @@ -87,8 +95,8 @@ func (w *ChaosNetworkWorkload) Name() string { // injection via systemd. Assumes iproute-tc is pre-installed (golden image dependency). func (w *ChaosNetworkWorkload) CloudInitUserdata() (string, error) { startScript := fmt.Sprintf(chaosNetworkStartScript, - fmt.Sprintf("%d", w.Latency), - fmt.Sprintf("%.1f", w.PacketLoss)) + w.latencyMs(), + w.packetLossPercent()) return w.BuildCloudConfig(CloudConfigOpts{ Packages: []string{"iproute-tc"}, diff --git a/internal/workloads/chaos_network_test.go b/internal/workloads/chaos_network_test.go index 6bfc69b..a698211 100644 --- a/internal/workloads/chaos_network_test.go +++ b/internal/workloads/chaos_network_test.go @@ -131,4 +131,87 @@ var _ = Describe("ChaosNetworkWorkload", func() { It("should have VMCount of 1", func() { Expect(w.VMCount()).To(Equal(1)) }) + + Context("param wiring", func() { + It("should use default param values when Params is nil", func() { + result, err := w.CloudInitUserdata() + Expect(err).NotTo(HaveOccurred()) + + parsed := parseYAML(result) + files := parsed["write_files"].([]interface{}) + + var startScript string + for _, f := range files { + fm := f.(map[string]interface{}) + if fm["path"] == "/usr/local/bin/virtwork-chaos-network-start.sh" { + startScript = fm["content"].(string) + break + } + } + + Expect(startScript).To(ContainSubstring("delay 100ms")) + Expect(startScript).To(ContainSubstring("loss 5.0%")) + }) + + It("should wire custom params from WorkloadConfig.Params", func() { + custom := workloads.NewChaosNetworkWorkload(config.WorkloadConfig{ + Enabled: true, + VMCount: 1, + CPUCores: 1, + Memory: "1Gi", + Params: map[string]string{ + "latency-ms": "200", + "packet-loss-percent": "10.5", + }, + }, "virtwork", "", nil) + + result, err := custom.CloudInitUserdata() + Expect(err).NotTo(HaveOccurred()) + + parsed := parseYAML(result) + files := parsed["write_files"].([]interface{}) + + var startScript string + for _, f := range files { + fm := f.(map[string]interface{}) + if fm["path"] == "/usr/local/bin/virtwork-chaos-network-start.sh" { + startScript = fm["content"].(string) + break + } + } + + Expect(startScript).To(ContainSubstring("delay 200ms")) + Expect(startScript).To(ContainSubstring("loss 10.5%")) + }) + + It("should use defaults for missing individual params", func() { + partial := workloads.NewChaosNetworkWorkload(config.WorkloadConfig{ + Enabled: true, + VMCount: 1, + CPUCores: 1, + Memory: "1Gi", + Params: map[string]string{ + "latency-ms": "50", + }, + }, "virtwork", "", nil) + + result, err := partial.CloudInitUserdata() + Expect(err).NotTo(HaveOccurred()) + + parsed := parseYAML(result) + files := parsed["write_files"].([]interface{}) + + var startScript string + for _, f := range files { + fm := f.(map[string]interface{}) + if fm["path"] == "/usr/local/bin/virtwork-chaos-network-start.sh" { + startScript = fm["content"].(string) + break + } + } + + Expect(startScript).To(ContainSubstring("delay 50ms")) + Expect(startScript).To(ContainSubstring("loss 5.0%")) + }) + }) }) diff --git a/internal/workloads/chaos_process.go b/internal/workloads/chaos_process.go index 80c310a..8d5e298 100644 --- a/internal/workloads/chaos_process.go +++ b/internal/workloads/chaos_process.go @@ -4,6 +4,8 @@ package workloads import ( + "fmt" + "github.com/opdev/virtwork/internal/config" ) @@ -103,15 +105,15 @@ main() { main "$@" ` -const chaosProcessSystemdUnit = `[Unit] +const chaosProcessSystemdUnitTemplate = `[Unit] Description=Virtwork chaos-process workload After=network.target [Service] Type=simple -Environment="CHAOS_SIGNAL=SIGTERM" -Environment="CHAOS_INTERVAL=30" -Environment="CHAOS_MIN_PID=1000" +Environment="CHAOS_SIGNAL=%s" +Environment="CHAOS_INTERVAL=%s" +Environment="CHAOS_MIN_PID=%s" ExecStart=/usr/local/bin/chaos-process.sh Restart=always RestartSec=10 @@ -144,6 +146,33 @@ func NewChaosProcessWorkload( } } +func (w *ChaosProcessWorkload) signal() string { + if w.Config.Params != nil { + if val, ok := w.Config.Params["signal"]; ok && val != "" { + return val + } + } + return "SIGTERM" +} + +func (w *ChaosProcessWorkload) interval() string { + if w.Config.Params != nil { + if val, ok := w.Config.Params["interval"]; ok && val != "" { + return val + } + } + return "30" +} + +func (w *ChaosProcessWorkload) minPid() string { + if w.Config.Params != nil { + if val, ok := w.Config.Params["min-pid"]; ok && val != "" { + return val + } + } + return "1000" +} + // Name returns "chaos-process". func (w *ChaosProcessWorkload) Name() string { return "chaos-process" @@ -152,6 +181,11 @@ func (w *ChaosProcessWorkload) Name() string { // CloudInitUserdata returns cloud-init YAML that installs the chaos-process script // and runs it as a systemd service. func (w *ChaosProcessWorkload) CloudInitUserdata() (string, error) { + unit := fmt.Sprintf(chaosProcessSystemdUnitTemplate, + w.signal(), + w.interval(), + w.minPid()) + return w.BuildCloudConfig(CloudConfigOpts{ Packages: []string{"procps-ng"}, WriteFiles: []WriteFile{ @@ -162,7 +196,7 @@ func (w *ChaosProcessWorkload) CloudInitUserdata() (string, error) { }, { Path: "/etc/systemd/system/virtwork-chaos-process.service", - Content: chaosProcessSystemdUnit, + Content: unit, Permissions: "0644", }, }, diff --git a/internal/workloads/chaos_process_test.go b/internal/workloads/chaos_process_test.go index a0f4e38..5e0b2fe 100644 --- a/internal/workloads/chaos_process_test.go +++ b/internal/workloads/chaos_process_test.go @@ -153,4 +153,91 @@ var _ = Describe("ChaosProcessWorkload", func() { It("should default to 1 VM", func() { Expect(w.VMCount()).To(Equal(1)) }) + + Context("param wiring", func() { + It("should use default param values when Params is nil", func() { + result, err := w.CloudInitUserdata() + Expect(err).NotTo(HaveOccurred()) + + parsed := parseYAML(result) + files := parsed["write_files"].([]interface{}) + + var unitContent string + for _, f := range files { + fm := f.(map[string]interface{}) + if fm["path"] == "/etc/systemd/system/virtwork-chaos-process.service" { + unitContent = fm["content"].(string) + break + } + } + + Expect(unitContent).To(ContainSubstring("CHAOS_SIGNAL=SIGTERM")) + Expect(unitContent).To(ContainSubstring("CHAOS_INTERVAL=30")) + Expect(unitContent).To(ContainSubstring("CHAOS_MIN_PID=1000")) + }) + + It("should wire custom params from WorkloadConfig.Params", func() { + custom := workloads.NewChaosProcessWorkload(config.WorkloadConfig{ + Enabled: true, + VMCount: 1, + CPUCores: 2, + Memory: "2Gi", + Params: map[string]string{ + "signal": "SIGKILL", + "interval": "10", + "min-pid": "500", + }, + }, "virtwork", "", nil) + + result, err := custom.CloudInitUserdata() + Expect(err).NotTo(HaveOccurred()) + + parsed := parseYAML(result) + files := parsed["write_files"].([]interface{}) + + var unitContent string + for _, f := range files { + fm := f.(map[string]interface{}) + if fm["path"] == "/etc/systemd/system/virtwork-chaos-process.service" { + unitContent = fm["content"].(string) + break + } + } + + Expect(unitContent).To(ContainSubstring("CHAOS_SIGNAL=SIGKILL")) + Expect(unitContent).To(ContainSubstring("CHAOS_INTERVAL=10")) + Expect(unitContent).To(ContainSubstring("CHAOS_MIN_PID=500")) + }) + + It("should use defaults for missing individual params", func() { + partial := workloads.NewChaosProcessWorkload(config.WorkloadConfig{ + Enabled: true, + VMCount: 1, + CPUCores: 2, + Memory: "2Gi", + Params: map[string]string{ + "signal": "SIGUSR1", + }, + }, "virtwork", "", nil) + + result, err := partial.CloudInitUserdata() + Expect(err).NotTo(HaveOccurred()) + + parsed := parseYAML(result) + files := parsed["write_files"].([]interface{}) + + var unitContent string + for _, f := range files { + fm := f.(map[string]interface{}) + if fm["path"] == "/etc/systemd/system/virtwork-chaos-process.service" { + unitContent = fm["content"].(string) + break + } + } + + Expect(unitContent).To(ContainSubstring("CHAOS_SIGNAL=SIGUSR1")) + Expect(unitContent).To(ContainSubstring("CHAOS_INTERVAL=30")) + Expect(unitContent).To(ContainSubstring("CHAOS_MIN_PID=1000")) + }) + }) }) From 85f9df03d5f8443c2ba6c10749687091b436e4e0 Mon Sep 17 00:00:00 2001 From: Melvin Hillsman Date: Fri, 22 May 2026 19:44:24 -0500 Subject: [PATCH 2/2] docs: add config.yaml.example with all supported keys Complete annotated example covering every global setting, SSH field, audit option, and all nine workloads with their per-workload params. Each key includes its default value, equivalent env var, and CLI flag. Signed-off-by: Melvin Hillsman --- config.yaml.example | 228 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 config.yaml.example diff --git a/config.yaml.example b/config.yaml.example new file mode 100644 index 0000000..7f6ff47 --- /dev/null +++ b/config.yaml.example @@ -0,0 +1,228 @@ +# config.yaml.example — Complete virtwork configuration reference +# +# Pass to virtwork with: virtwork run --config config.yaml +# +# Priority chain (highest wins): +# CLI flags > environment variables (VIRTWORK_*) > this YAML file > built-in defaults +# +# All keys are optional. Unset keys fall back to built-in defaults. +# YAML keys use underscores (e.g., cpu_cores); the equivalent CLI flags +# and env vars use hyphens and SCREAMING_SNAKE_CASE respectively: +# YAML: cpu_cores | Flag: --cpu-cores | Env: VIRTWORK_CPU_CORES + +# --------------------------------------------------------------------------- +# Cluster connection +# --------------------------------------------------------------------------- + +# Kubernetes namespace where VMs and supporting resources are created. +# Env: VIRTWORK_NAMESPACE | Flag: --namespace +# namespace: virtwork + +# Path to kubeconfig file. When empty, virtwork uses in-cluster config +# (if running inside a pod) or falls back to ~/.kube/config. +# Env: VIRTWORK_KUBECONFIG | Flag: --kubeconfig +# kubeconfig: "" + +# --------------------------------------------------------------------------- +# VM defaults (global — per-workload overrides are in the workloads section) +# --------------------------------------------------------------------------- + +# Container disk image used as the boot volume for every VM. +# Set to a golden image with pre-installed packages for faster boot. +# Env: VIRTWORK_CONTAINER_DISK_IMAGE | Flag: --container-disk-image +# container_disk_image: quay.io/containerdisks/fedora:41 + +# Size of the additional data disk attached to storage-backed workloads +# (disk, database, chaos-disk). +# Env: VIRTWORK_DATA_DISK_SIZE | Flag: --data-disk-size +# data_disk_size: 10Gi + +# Default CPU cores allocated to each VM. +# Env: VIRTWORK_CPU_CORES | Flag: --cpu-cores +# cpu_cores: 2 + +# Default memory allocated to each VM (Kubernetes quantity, e.g., 2Gi). +# Env: VIRTWORK_MEMORY | Flag: --memory +# memory: 2Gi + +# --------------------------------------------------------------------------- +# Run behavior +# --------------------------------------------------------------------------- + +# Print VM YAML specs and exit without creating any cluster resources. +# Env: VIRTWORK_DRY_RUN | Flag: --dry-run +# dry_run: false + +# Enable DEBUG-level structured logging (default is INFO). +# Env: VIRTWORK_VERBOSE | Flag: --verbose +# verbose: false + +# Wait for all VMs to reach the Ready condition after creation. +# Set to false (or use --no-wait) to skip readiness polling. +# Env: VIRTWORK_WAIT_FOR_READY | Flag: --no-wait (inverts this value) +# wait_for_ready: true + +# Maximum seconds to wait for VM readiness before timing out. +# Env: VIRTWORK_TIMEOUT | Flag: --timeout +# timeout: 600 + +# --------------------------------------------------------------------------- +# SSH access (optional) +# +# When at least one SSH credential (password or key) is provided, virtwork +# creates an in-VM user account with the given username. When all SSH fields +# are empty, no user account is created and VMs are headless. +# --------------------------------------------------------------------------- + +# Username for the in-VM SSH user account. +# Env: VIRTWORK_SSH_USER | Flag: --ssh-user +# ssh_user: virtwork + +# Password for the SSH user (plain-text in the VM spec — prefer keys). +# Env: VIRTWORK_SSH_PASSWORD | Flag: --ssh-password +# ssh_password: "" + +# List of SSH public keys injected into the VM's authorized_keys. +# In YAML, provide a list. Via env var, provide a comma-separated string. +# Via CLI, use --ssh-key (repeatable) or --ssh-key-file (path, repeatable). +# Env: VIRTWORK_SSH_AUTHORIZED_KEYS (comma-separated) +# Flag: --ssh-key / --ssh-key-file +# ssh_authorized_keys: [] + # - ssh-ed25519 AAAAC3Nz... user@host + # - ssh-rsa AAAAB3NzaC1yc2E... user@host + +# --------------------------------------------------------------------------- +# Audit +# +# Virtwork records each run in a local SQLite database for post-run analysis. +# --------------------------------------------------------------------------- + +# Enable or disable audit tracking. Override with --no-audit to hard-disable. +# Env: VIRTWORK_AUDIT | Flag: --audit / --no-audit +# audit: true + +# Path to the SQLite audit database file. +# In-cluster, mount a PVC and set to /data/virtwork.db. +# Env: VIRTWORK_AUDIT_DB | Flag: --audit-db +# audit_db: virtwork.db + +# --------------------------------------------------------------------------- +# Per-workload overrides +# +# Each key under "workloads" is a workload name. Available workloads: +# chaos-disk, chaos-network, chaos-process, cpu, database, disk, +# memory, network, tps +# +# Every field is optional — unspecified fields inherit the global defaults. +# The "params" sub-map accepts string-valued, workload-specific tuning knobs. +# Workloads not listed here still run (unless filtered via --workloads flag). +# --------------------------------------------------------------------------- + +# workloads: + + # --- CPU stress workload --- + # Drives all allocated cores to 100% utilization. +# cpu: +# enabled: true +# vm_count: 1 +# cpu_cores: 2 +# memory: 2Gi + # No workload-specific params. + + # --- Memory stress workload --- + # Allocates and touches memory to stress the VM's memory subsystem. +# memory: +# enabled: true +# vm_count: 1 +# cpu_cores: 2 +# memory: 2Gi + # No workload-specific params. + + # --- Disk I/O workload --- + # Performs sequential/random I/O against the data disk. +# disk: +# enabled: true +# vm_count: 1 +# cpu_cores: 2 +# memory: 2Gi + # No workload-specific params. + + # --- Database workload --- + # Runs a database benchmark against a local database instance. +# database: +# enabled: true +# vm_count: 1 +# cpu_cores: 2 +# memory: 4Gi + # No workload-specific params. + + # --- Network throughput workload --- + # Client/server pair measuring network throughput. + # vm_count is per-role: 1 creates 1 server + 1 client = 2 VMs total. +# network: +# enabled: true +# vm_count: 1 +# cpu_cores: 2 +# memory: 2Gi + # No workload-specific params. + + # --- TPS (transactions per second) workload --- + # HTTP-based throughput test between a server and client VM. + # vm_count is per-role: 1 creates 1 server + 1 client = 2 VMs total. +# tps: +# enabled: true +# vm_count: 1 +# cpu_cores: 2 +# memory: 2Gi +# params: + # Size of the HTTP test file. Accepts K/M/G suffixes. +# file-size: "10M" + # Number of test iterations to run. +# iterations: "30" + # Seconds per iteration. +# duration: "60" + + # --- Chaos disk workload --- + # Fills a data disk to a target percentage, holds, releases, and repeats. +# chaos-disk: +# enabled: true +# vm_count: 1 +# cpu_cores: 2 +# memory: 2Gi +# params: + # Mountpoint of the data disk to fill. +# mount: "/mnt/data" + # Target fill percentage (0-100). +# fill-percent: "90" + # Seconds the disk is held at the target fill level. +# fill-sleep: "60" + # Seconds the disk is empty before refilling. +# release-sleep: "30" + + # --- Chaos network workload --- + # Injects latency and packet loss via tc/netem on the VM's default interface. +# chaos-network: +# enabled: true +# vm_count: 1 +# cpu_cores: 1 +# memory: 1Gi +# params: + # Egress delay in milliseconds added via netem. +# latency-ms: "100" + # Egress packet drop rate (percentage, e.g., "5.0" for 5%). +# packet-loss-percent: "5.0" + + # --- Chaos process workload --- + # Periodically sends a signal to a random non-essential process in the VM. +# chaos-process: +# enabled: true +# vm_count: 1 +# cpu_cores: 2 +# memory: 2Gi +# params: + # Signal sent to the randomly selected victim process. +# signal: "SIGTERM" + # Seconds between kills. +# interval: "30" + # Minimum PID considered eligible for the kill (filters out system processes). +# min-pid: "1000"