diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 3ed7ac3..dcbbd38 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -26,3 +26,11 @@ Closes # - [ ] I have added an entry to `CHANGELOG.md` under `[Unreleased]` - [ ] My code follows the project's style guidelines in `CONTRIBUTING.md` - [ ] My commit messages follow [Conventional Commits](https://www.conventionalcommits.org/) + +## Standards Compliance + +- [ ] Follows [repo structure standard](https://azurelocal.cloud/standards/repo-structure) (required files present) +- [ ] Follows [naming conventions](https://azurelocal.cloud/standards/documentation/naming-conventions) (files, variables, resources) +- [ ] Uses [IIC fictional company](https://azurelocal.cloud/standards/fictional-company-policy) in all examples (never Contoso) +- [ ] Config changes validated against JSON Schema (if applicable) +- [ ] No hardcoded IPs, names, secrets, or environment-specific values in committed code diff --git a/.github/workflows/validate-config.yml b/.github/workflows/validate-config.yml index 8187454..538f0ef 100644 --- a/.github/workflows/validate-config.yml +++ b/.github/workflows/validate-config.yml @@ -1,7 +1,8 @@ # ============================================================================= -# validate-config.yml - Validate configuration files +# validate-config.yml — Validate config/variables.example.yml against schema # ============================================================================= -# Validates YAML configs against JSON Schema and checks for syntax errors. +# Triggered on PRs and pushes that touch config/ or this workflow. +# Validates YAML syntax and JSON Schema compliance. # ============================================================================= name: Validate Configuration @@ -22,61 +23,38 @@ permissions: contents: read jobs: - validate-config: + validate: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - - name: Setup Node.js (for ajv-cli) - uses: actions/setup-node@v4 + - name: Setup Python + uses: actions/setup-python@v5 with: - node-version: '20' + python-version: '3.12' - - name: Install validation tools - run: | - npm install -g ajv-cli yaml-lint - - - name: Lint YAML files - run: | - find config -name '*.yml' -o -name '*.yaml' | while read f; do - echo "Validating YAML syntax: $f" - yamllint "$f" || echo "WARN: $f has lint issues" - done + - name: Install dependencies + run: pip install pyyaml jsonschema - - name: Validate JSON files + - name: Validate variables.example.yml against schema run: | - find config -name '*.json' | while read f; do - echo "Validating JSON syntax: $f" - python3 -m json.tool "$f" > /dev/null - done - - - name: Validate master config against schema - run: | - # Convert YAML to JSON for schema validation - pip install pyyaml python3 -c " import yaml, json, sys - with open('config/variables/master-environment.yml') as f: + from jsonschema import validate, ValidationError + + with open('config/variables.example.yml') as f: data = yaml.safe_load(f) - json.dump(data, sys.stdout) - " > /tmp/master-config.json - ajv validate \ - -s config/variables/schema.json \ - -d /tmp/master-config.json \ - || echo "Schema validation completed with issues" + with open('config/schema/variables.schema.json') as f: + schema = json.load(f) - - name: Validate solution configs - run: | - for f in config/variables/solutions/*.json; do - echo "Checking solution config: $f" - python3 -c " - import json - with open('$f') as fh: - data = json.load(fh) - assert '_metadata' in data, 'Missing _metadata section' - print(f\" Solution: {data['_metadata']['solution']} - OK\") + try: + validate(instance=data, schema=schema) + print('✅ config/variables.example.yml passes schema validation') + except ValidationError as e: + print(f'❌ Schema validation failed: {e.message}') + print(f' Path: {\" > \".join(str(p) for p in e.absolute_path)}') + sys.exit(1) " - done diff --git a/.github/workflows/validate-repo-structure.yml b/.github/workflows/validate-repo-structure.yml new file mode 100644 index 0000000..e50c5a7 --- /dev/null +++ b/.github/workflows/validate-repo-structure.yml @@ -0,0 +1,80 @@ +name: Validate Repo Structure +on: + pull_request: + branches: [main] + +jobs: + check-structure: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Check required root files + run: | + missing=0 + for f in README.md CONTRIBUTING.md LICENSE CHANGELOG.md .gitignore; do + if [ ! -f "$f" ]; then + echo "::error::Missing required file: $f" + missing=$((missing + 1)) + fi + done + if [ $missing -gt 0 ]; then + echo "::error::$missing required root file(s) missing" + exit 1 + fi + echo "All required root files present" + + - name: Check required directories + run: | + missing=0 + for d in docs .github; do + if [ ! -d "$d" ]; then + echo "::error::Missing required directory: $d/" + missing=$((missing + 1)) + fi + done + if [ $missing -gt 0 ]; then + echo "::error::$missing required directory(s) missing" + exit 1 + fi + echo "All required directories present" + + - name: Check PR template + run: | + if [ ! -f ".github/PULL_REQUEST_TEMPLATE.md" ]; then + echo "::error::Missing .github/PULL_REQUEST_TEMPLATE.md" + exit 1 + fi + echo "PR template found" + + - name: Check config structure (if config dir exists) + run: | + if [ -d "config" ]; then + missing=0 + if [ ! -f "config/variables.example.yml" ]; then + echo "::error::Missing config/variables.example.yml" + missing=$((missing + 1)) + fi + if [ ! -f "config/schema/variables.schema.json" ]; then + echo "::error::Missing config/schema/variables.schema.json" + missing=$((missing + 1)) + fi + if [ $missing -gt 0 ]; then + exit 1 + fi + echo "Config structure valid" + else + echo "No config/ directory — skipping config checks" + fi + + - name: Check variable reference doc (if config dir exists) + run: | + if [ -d "config" ]; then + if [ ! -f "docs/reference/variables.md" ]; then + echo "::error::Missing docs/reference/variables.md (required when config/ exists)" + exit 1 + fi + echo "Variable reference doc found" + else + echo "No config/ directory — skipping variable reference check" + fi diff --git a/.gitignore b/.gitignore index 7afd989..7b87ad8 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,9 @@ config/credentials/*.key config/credentials/*.pem config/credentials/.env +# User-specific config (actual values — never commit) +config/variables.yml + # Generated solution config files (built from master-environment.yml) # Uncomment the next line if you want these regenerated every time: # config/variables/solutions/*.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 46c621d..fade4e5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -74,6 +74,16 @@ Examples: - Include performance baselines or comparative results when relevant - Describe your test environment and results in the PR +## Standards + +This project follows the **org-wide AzureLocal standards** documented at [azurelocal.cloud/standards](https://azurelocal.cloud/standards/). Key references: + +- [Repository Structure](https://azurelocal.cloud/standards/repo-structure) — Required files, directories, labels, branch naming +- [Scripting Standards](https://azurelocal.cloud/standards/scripting/scripting-standards) — PowerShell conventions +- [Documentation Standards](https://azurelocal.cloud/standards/documentation/documentation-standards) — Writing and formatting +- [Variable Management](https://azurelocal.cloud/docs/implementation/04-variable-management-standard) — Config file patterns +- [Fictional Company Policy](https://azurelocal.cloud/standards/fictional-company-policy) — Use IIC, never Contoso + ## Code of Conduct Be respectful and constructive. Keep discussions on-topic and collaborative. diff --git a/config/schema/variables.schema.json b/config/schema/variables.schema.json new file mode 100644 index 0000000..a8adee4 --- /dev/null +++ b/config/schema/variables.schema.json @@ -0,0 +1,136 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://github.com/AzureLocal/azurelocal-loadtools/config/schema/variables.schema.json", + "title": "Load Tools Variables", + "description": "Schema for config/variables.example.yml — validates required sections and key structure.", + "type": "object", + "required": ["azure", "keyvault", "azure_local", "storage", "credentials", "testing", "tags"], + "properties": { + "azure": { + "type": "object", + "required": ["subscription_id", "tenant_id", "resource_group", "location"], + "properties": { + "subscription_id": { "type": "string" }, + "tenant_id": { "type": "string" }, + "resource_group": { "type": "string" }, + "location": { "type": "string" } + } + }, + "keyvault": { + "type": "object", + "required": ["name"], + "properties": { + "name": { "type": "string" }, + "auth_method": { "type": "string", "enum": ["managed_identity", "service_principal", "az_cli"] } + } + }, + "azure_local": { + "type": "object", + "required": ["cluster_name", "nodes"], + "properties": { + "cluster_name": { "type": "string" }, + "cluster_domain": { "type": "string" }, + "nodes": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": ["name", "management_ip"], + "properties": { + "name": { "type": "string" }, + "management_ip": { "type": "string" }, + "storage_ip": { "type": "string" } + } + } + } + } + }, + "storage": { + "type": "object", + "required": ["csv_path"], + "properties": { + "csv_path": { "type": "string" }, + "collect_volume_path": { "type": "string" }, + "base_vhd_path": { "type": "string" } + } + }, + "networking": { + "type": "object", + "properties": { + "management": { + "type": "object", + "properties": { + "subnet": { "type": "string" }, + "vlan_id": { "type": "integer" } + } + }, + "storage": { + "type": "object", + "properties": { + "subnet": { "type": "string" }, + "vlan_id": { "type": "integer" }, + "rdma_enabled": { "type": "boolean" } + } + }, + "compute": { + "type": "object", + "properties": { + "subnet": { "type": "string" }, + "vlan_id": { "type": "integer" } + } + } + } + }, + "credentials": { + "type": "object", + "required": ["cluster_admin_username", "cluster_admin_password"], + "properties": { + "cluster_admin_username": { "type": "string" }, + "cluster_admin_password": { "type": "string" }, + "vmfleet_admin_username": { "type": "string" }, + "vmfleet_admin_password": { "type": "string" } + } + }, + "testing": { + "type": "object", + "required": ["default_tool"], + "properties": { + "default_tool": { "type": "string", "enum": ["vmfleet", "fio", "iperf", "hammerdb", "stress-ng"] }, + "default_profile": { "type": "string" }, + "duration_seconds": { "type": "integer", "minimum": 1 }, + "warmup_seconds": { "type": "integer", "minimum": 0 } + } + }, + "monitoring": { + "type": "object", + "properties": { + "log_analytics_workspace_id": { "type": "string" }, + "log_analytics_shared_key": { "type": "string" }, + "enable_real_time": { "type": "boolean" }, + "collection_interval_seconds": { "type": "integer", "minimum": 1 } + } + }, + "reporting": { + "type": "object", + "properties": { + "output_dir": { "type": "string" }, + "format": { "type": "string", "enum": ["html", "json", "csv"] }, + "include_charts": { "type": "boolean" } + } + }, + "winrm": { + "type": "object", + "properties": { + "port": { "type": "integer" }, + "use_ssl": { "type": "boolean" }, + "authentication": { "type": "string", "enum": ["Kerberos", "Negotiate", "Basic"] }, + "operation_timeout_seconds": { "type": "integer", "minimum": 1 } + } + }, + "tags": { + "type": "object", + "additionalProperties": { "type": "string" } + } + }, + "additionalProperties": false +} diff --git a/config/variables.example.yml b/config/variables.example.yml new file mode 100644 index 0000000..7c477c0 --- /dev/null +++ b/config/variables.example.yml @@ -0,0 +1,133 @@ +# ============================================================================= +# variables.example.yml +# Central configuration — single source of truth for load testing deployments. +# +# Copy this file to variables.yml and fill in your values: +# cp config/variables.example.yml config/variables.yml +# +# DO NOT commit variables.yml — it is excluded by .gitignore. +# +# Key Vault References: +# Secrets use keyvault:// URIs and are resolved at runtime. +# Format: keyvault:/// +# NEVER put actual passwords or secrets in this file. +# +# Detailed configs live in subdirectories: +# - config/clusters/ Cluster definitions (per-cluster YAML) +# - config/credentials/ Key Vault credential mappings +# - config/profiles/ Workload profiles (vmfleet, fio, iperf, etc.) +# - config/variables/ Master variable registry and solution schemas +# ============================================================================= + + +# ============================================================================= +# Azure +# ============================================================================= +azure: + subscription_id: "00000000-0000-0000-0000-000000000000" + tenant_id: "00000000-0000-0000-0000-000000000000" + resource_group: "rg-loadtools-eus-01" + location: "eastus" + + +# ============================================================================= +# Key Vault +# ============================================================================= +keyvault: + name: "kv-iic-loadtools" + auth_method: "az_cli" # managed_identity | service_principal | az_cli + + +# ============================================================================= +# Azure Local Cluster +# ============================================================================= +azure_local: + cluster_name: "azl-cluster-01" + cluster_domain: "iic.local" + nodes: + - name: "azl-node-01" + management_ip: "10.0.0.1" + storage_ip: "10.0.1.1" + - name: "azl-node-02" + management_ip: "10.0.0.2" + storage_ip: "10.0.1.2" + + +# ============================================================================= +# Storage +# ============================================================================= +storage: + csv_path: "C:\\ClusterStorage\\Volume1" + collect_volume_path: "C:\\ClusterStorage\\Collect" + base_vhd_path: "C:\\ClusterStorage\\Collect\\BaseImage.vhdx" + + +# ============================================================================= +# Networking +# ============================================================================= +networking: + management: + subnet: "10.0.0.0/24" + vlan_id: 0 + storage: + subnet: "10.0.1.0/24" + vlan_id: 100 + rdma_enabled: true + compute: + subnet: "10.0.2.0/24" + vlan_id: 200 + + +# ============================================================================= +# Credentials (mapped to Key Vault secrets) +# ============================================================================= +credentials: + cluster_admin_username: "keyvault://kv-iic-loadtools/hci-cluster-admin-user" + cluster_admin_password: "keyvault://kv-iic-loadtools/hci-cluster-admin-pwd" + vmfleet_admin_username: "keyvault://kv-iic-loadtools/vmfleet-admin-user" + vmfleet_admin_password: "keyvault://kv-iic-loadtools/vmfleet-admin-pwd" + + +# ============================================================================= +# Testing Defaults +# ============================================================================= +testing: + default_tool: "vmfleet" # vmfleet | fio | iperf | hammerdb | stress-ng + default_profile: "general" # Profile name from config/profiles// + duration_seconds: 300 + warmup_seconds: 60 + + +# ============================================================================= +# Monitoring & Reporting +# ============================================================================= +monitoring: + log_analytics_workspace_id: "00000000-0000-0000-0000-000000000000" + log_analytics_shared_key: "keyvault://kv-iic-loadtools/log-analytics-key" + enable_real_time: true + collection_interval_seconds: 10 + +reporting: + output_dir: "reports" + format: "html" # html | json | csv + include_charts: true + + +# ============================================================================= +# WinRM +# ============================================================================= +winrm: + port: 5985 + use_ssl: false + authentication: "Kerberos" # Kerberos | Negotiate | Basic + operation_timeout_seconds: 300 + + +# ============================================================================= +# Tags +# ============================================================================= +tags: + project: "LoadTools" + environment: "production" + workload: "performance-testing" + solution: "loadtools-azure-local" diff --git a/docs/reference/variable-reference.md b/docs/reference/variable-reference.md deleted file mode 100644 index 81f41af..0000000 --- a/docs/reference/variable-reference.md +++ /dev/null @@ -1,79 +0,0 @@ -# Variable Reference - -![Category: Reference](https://img.shields.io/badge/Category-Reference-7F8C8D?style=flat-square) - -This document provides a complete catalog of all variables defined in `master-environment.yml`. - -## Cluster Variables - -| Variable | Type | Required | Default | Description | -| --- | --- | --- | --- | --- | -| `cluster_name` | string | Yes | (none) | Azure Local cluster name | -| `cluster_domain` | string | Yes | (none) | Active Directory domain for the cluster | -| `cluster_nodes` | array | Yes | (none) | List of cluster node hostnames | -| `csv_path` | string | Yes | (none) | Path to the Cluster Shared Volume for VM storage | -| `collect_volume_path` | string | Yes | (none) | Path to the VMFleet Collect volume (ReFS, 200GB+) | -| `base_vhd_path` | string | Yes | (none) | Path to the Windows Server Core base VHDX | - -## VMFleet Variables - -| Variable | Type | Required | Default | Description | -| --- | --- | --- | --- | --- | -| `vm_count_per_node` | integer | Yes | 10 | Number of fleet VMs to create per cluster node | -| `vm_vcpu_count` | integer | Yes | 2 | Number of virtual CPUs per fleet VM | -| `vm_memory_gb` | integer | Yes | 2 | Memory allocation (GB) per fleet VM | -| `test_duration_seconds` | integer | Yes | 300 | Duration of each test pass in seconds | -| `warmup_seconds` | integer | No | 60 | Warmup period before measurement begins | -| `vmfleet_admin_username` | string | Yes | (none) | Administrator username for fleet VMs (sensitive) | -| `vmfleet_admin_password` | string | Yes | (none) | Administrator password for fleet VMs (sensitive — use Key Vault) | - -## Monitoring Variables - -| Variable | Type | Required | Default | Description | -| --- | --- | --- | --- | --- | -| `monitoring_enabled` | boolean | No | true | Enable metric collection during tests | -| `sample_interval_seconds` | integer | No | 5 | Metric sampling interval | -| `push_to_azure_monitor` | boolean | No | false | Send metrics to Azure Monitor / Log Analytics | -| `log_analytics_workspace_id` | string | No | (none) | Azure Log Analytics workspace ID (required if push enabled) | -| `log_analytics_shared_key` | string | No | (none) | Log Analytics shared key (sensitive — use Key Vault) | - -## Reporting Variables - -| Variable | Type | Required | Default | Description | -| --- | --- | --- | --- | --- | -| `report_formats` | array | No | ["PDF"] | Report output formats: PDF, DOCX, XLSX | -| `report_title` | string | No | "Azure Local Load Test Report" | Title on report cover page | -| `report_author` | string | No | "Azure Local Load Tools" | Author attribution on reports | -| `include_raw_data` | boolean | No | true | Include raw metrics in Excel report | - -## fio Variables - -| Variable | Type | Required | Default | Description | -| --- | --- | --- | --- | --- | -| `fio_runtime_seconds` | integer | No | 300 | Duration of fio test execution | -| `fio_block_size` | string | No | "4k" | Default block size for fio jobs | -| `fio_io_engine` | string | No | "libaio" | I/O engine for fio (libaio, io_uring, etc.) | - -## iPerf Variables - -| Variable | Type | Required | Default | Description | -| --- | --- | --- | --- | --- | -| `iperf_duration_seconds` | integer | No | 30 | Duration of each iPerf test | -| `iperf_parallel_streams` | integer | No | 4 | Number of parallel streams per iPerf test | -| `iperf_protocol` | string | No | "TCP" | Protocol: TCP or UDP | - -## HammerDB Variables - -| Variable | Type | Required | Default | Description | -| --- | --- | --- | --- | --- | -| `hammerdb_db_type` | string | No | "mssql" | Target database: mssql, postgresql, mysql | -| `hammerdb_warehouse_count` | integer | No | 10 | Number of TPC-C warehouses | -| `hammerdb_virtual_users` | integer | No | 4 | Number of virtual users for workload generation | - -## stress-ng Variables - -| Variable | Type | Required | Default | Description | -| --- | --- | --- | --- | --- | -| `stress_ng_timeout` | string | No | "5m" | Stress test duration | -| `stress_ng_stressors` | array | No | ["cpu", "vm", "hdd"] | Stressor classes to execute | -| `stress_ng_workers` | integer | No | 0 | Number of workers per stressor (0 = auto-detect CPU count) | diff --git a/docs/reference/variables.md b/docs/reference/variables.md new file mode 100644 index 0000000..472e2aa --- /dev/null +++ b/docs/reference/variables.md @@ -0,0 +1,247 @@ +# Variable Reference + +All load testing tools read from a single central configuration file: `config/variables.yml`. This file is the **single source of truth** — cluster details, credentials, testing defaults, and monitoring settings are all declared here and consumed by every automation tool. + +!!! tip "Getting started" + Copy the example and fill in your values: + ```powershell + cp config/variables.example.yml config/variables.yml + ``` + **Never commit** `variables.yml` — it is excluded by `.gitignore` because it contains environment-specific values and Key Vault references. + +!!! info "Additional configs" + Detailed configs live in subdirectories under `config/`: + + - `config/clusters/` — Per-cluster connection details + - `config/credentials/` — Key Vault credential mappings + - `config/profiles/` — Workload profiles (vmfleet, fio, iperf, etc.) + - `config/variables/` — Master variable registry and solution schemas + +--- + +## Naming Rules + +| Scope | Convention | Example | +|-------|-----------|---------| +| Top-level sections | `snake_case` | `azure_local`, `credentials` | +| Keys within sections | `snake_case` | `subscription_id`, `default_tool` | +| Booleans | Descriptive name | `enable_real_time: true` | +| Secrets | `keyvault://` URI | `keyvault://kv-name/secret-name` | + +--- + +## Azure + +```yaml +azure: + subscription_id: "00000000-0000-0000-0000-000000000000" + tenant_id: "00000000-0000-0000-0000-000000000000" + resource_group: "rg-loadtools-eus-01" + location: "eastus" +``` + +| Variable | Type | Required | Description | Default | +|----------|------|:--------:|-------------|---------| +| `azure.subscription_id` | string | **Yes** | Azure subscription ID | — | +| `azure.tenant_id` | string | **Yes** | Azure AD tenant ID | — | +| `azure.resource_group` | string | **Yes** | Resource group for load testing resources | — | +| `azure.location` | string | **Yes** | Azure region | `eastus` | + +--- + +## Key Vault + +```yaml +keyvault: + name: "kv-iic-loadtools" + auth_method: "az_cli" +``` + +| Variable | Type | Required | Description | Default | +|----------|------|:--------:|-------------|---------| +| `keyvault.name` | string | **Yes** | Key Vault name for all `keyvault://` URI resolution | — | +| `keyvault.auth_method` | string | No | Auth method: `managed_identity`, `service_principal`, or `az_cli` | `az_cli` | + +--- + +## Azure Local Cluster + +```yaml +azure_local: + cluster_name: "azl-cluster-01" + cluster_domain: "iic.local" + nodes: + - name: "azl-node-01" + management_ip: "10.0.0.1" + storage_ip: "10.0.1.1" +``` + +| Variable | Type | Required | Description | Default | +|----------|------|:--------:|-------------|---------| +| `azure_local.cluster_name` | string | **Yes** | Azure Local cluster name | — | +| `azure_local.cluster_domain` | string | **Yes** | Active Directory domain for the cluster | — | +| `azure_local.nodes` | list | **Yes** | Cluster nodes with name, management_ip, and storage_ip | — | + +--- + +## Storage + +```yaml +storage: + csv_path: "C:\\ClusterStorage\\Volume1" + collect_volume_path: "C:\\ClusterStorage\\Collect" + base_vhd_path: "C:\\ClusterStorage\\Collect\\BaseImage.vhdx" +``` + +| Variable | Type | Required | Description | Default | +|----------|------|:--------:|-------------|---------| +| `storage.csv_path` | string | **Yes** | Cluster Shared Volume path for VM storage | — | +| `storage.collect_volume_path` | string | No | VMFleet Collect volume path (ReFS, 200 GB+) | — | +| `storage.base_vhd_path` | string | No | Windows Server Core base VHDX path | — | + +--- + +## Networking + +```yaml +networking: + management: + subnet: "10.0.0.0/24" + vlan_id: 0 + storage: + subnet: "10.0.1.0/24" + vlan_id: 100 + rdma_enabled: true + compute: + subnet: "10.0.2.0/24" + vlan_id: 200 +``` + +| Variable | Type | Required | Description | Default | +|----------|------|:--------:|-------------|---------| +| `networking.management.subnet` | string | No | Management network CIDR | — | +| `networking.management.vlan_id` | integer | No | Management VLAN ID | `0` | +| `networking.storage.subnet` | string | No | Storage network CIDR | — | +| `networking.storage.vlan_id` | integer | No | Storage VLAN ID | — | +| `networking.storage.rdma_enabled` | boolean | No | Whether RDMA is enabled on storage NICs | `true` | +| `networking.compute.subnet` | string | No | Compute network CIDR | — | +| `networking.compute.vlan_id` | integer | No | Compute VLAN ID | — | + +--- + +## Credentials + +```yaml +credentials: + cluster_admin_username: "keyvault://kv-iic-loadtools/hci-cluster-admin-user" + cluster_admin_password: "keyvault://kv-iic-loadtools/hci-cluster-admin-pwd" + vmfleet_admin_username: "keyvault://kv-iic-loadtools/vmfleet-admin-user" + vmfleet_admin_password: "keyvault://kv-iic-loadtools/vmfleet-admin-pwd" +``` + +| Variable | Type | Required | Description | Default | +|----------|------|:--------:|-------------|---------| +| `credentials.cluster_admin_username` | string | **Yes** | Key Vault URI — cluster admin username | — | +| `credentials.cluster_admin_password` | string | **Yes** | Key Vault URI — cluster admin password | — | +| `credentials.vmfleet_admin_username` | string | No | Key Vault URI — VMFleet VM admin username | — | +| `credentials.vmfleet_admin_password` | string | No | Key Vault URI — VMFleet VM admin password | — | + +--- + +## Testing Defaults + +```yaml +testing: + default_tool: "vmfleet" + default_profile: "general" + duration_seconds: 300 + warmup_seconds: 60 +``` + +| Variable | Type | Required | Description | Default | +|----------|------|:--------:|-------------|---------| +| `testing.default_tool` | string | **Yes** | Default tool: `vmfleet`, `fio`, `iperf`, `hammerdb`, `stress-ng` | `vmfleet` | +| `testing.default_profile` | string | No | Profile name from `config/profiles//` | `general` | +| `testing.duration_seconds` | integer | No | Duration of each test pass in seconds | `300` | +| `testing.warmup_seconds` | integer | No | Warmup period before measurement | `60` | + +--- + +## Monitoring & Reporting + +```yaml +monitoring: + log_analytics_workspace_id: "00000000-0000-0000-0000-000000000000" + log_analytics_shared_key: "keyvault://kv-iic-loadtools/log-analytics-key" + enable_real_time: true + collection_interval_seconds: 10 + +reporting: + output_dir: "reports" + format: "html" + include_charts: true +``` + +| Variable | Type | Required | Description | Default | +|----------|------|:--------:|-------------|---------| +| `monitoring.log_analytics_workspace_id` | string | No | Log Analytics workspace ID | — | +| `monitoring.log_analytics_shared_key` | string | No | Key Vault URI — Log Analytics shared key | — | +| `monitoring.enable_real_time` | boolean | No | Enable real-time metric collection | `true` | +| `monitoring.collection_interval_seconds` | integer | No | Metric sampling interval | `10` | +| `reporting.output_dir` | string | No | Report output directory | `reports` | +| `reporting.format` | string | No | Output format: `html`, `json`, `csv` | `html` | +| `reporting.include_charts` | boolean | No | Include charts in reports | `true` | + +--- + +## WinRM + +```yaml +winrm: + port: 5985 + use_ssl: false + authentication: "Kerberos" + operation_timeout_seconds: 300 +``` + +| Variable | Type | Required | Description | Default | +|----------|------|:--------:|-------------|---------| +| `winrm.port` | integer | No | WinRM port | `5985` | +| `winrm.use_ssl` | boolean | No | Enable HTTPS for WinRM | `false` | +| `winrm.authentication` | string | No | Auth method: `Kerberos`, `Negotiate`, `Basic` | `Kerberos` | +| `winrm.operation_timeout_seconds` | integer | No | Command timeout in seconds | `300` | + +--- + +## Tags + +```yaml +tags: + project: "LoadTools" + environment: "production" + workload: "performance-testing" + solution: "loadtools-azure-local" +``` + +| Variable | Type | Required | Description | Default | +|----------|------|:--------:|-------------|---------| +| `tags.project` | string | No | Project tag | `LoadTools` | +| `tags.environment` | string | No | Environment tag | `production` | +| `tags.workload` | string | No | Workload tag | `performance-testing` | +| `tags.solution` | string | No | Solution tag | `loadtools-azure-local` | + +--- + +## Key Vault Secret Resolution + +Secrets are never stored in plaintext. The `keyvault://` URI format tells deployment tools to resolve the value at runtime: + +```yaml +cluster_admin_password: "keyvault://kv-iic-loadtools/hci-cluster-admin-pwd" +``` + +**Resolution flow:** + +1. Tool parses the URI → vault name: `kv-iic-loadtools`, secret name: `hci-cluster-admin-pwd` +2. Tool calls `az keyvault secret show --vault-name kv-iic-loadtools --name hci-cluster-admin-pwd` +3. Secret value is passed directly to the script — never written to disk diff --git a/docs/standards/badge-standards.md b/docs/standards/badge-standards.md index 9c0540f..1639af7 100644 --- a/docs/standards/badge-standards.md +++ b/docs/standards/badge-standards.md @@ -1,258 +1,6 @@ -# Badge Standards — Azure Local Load Tools +# Badge Standards -![Category: Standards](https://img.shields.io/badge/Category-Standards-E67E22?style=flat-square) -![Updated: 2026-02-16](https://img.shields.io/badge/Updated-2026--02--16-lightgrey?style=flat-square) +!!! info "Moved to Central Standards" + Badge standards are now maintained org-wide at the central documentation site. -This document defines the standard badges used across all `.adoc` files in the Azure Local Load Tools repository. -Badges provide at-a-glance metadata — platform, tool, status, audience, and version — rendered as shields.io image macros. - -## Quick Navigation - -- [Badge Syntax](#badge-syntax) — AsciiDoc syntax for rendering badges -- [Badge Catalog](#badge-catalog) — Complete list of all project badges -- [Badge Placement](#badge-placement-rules) — Where badges go in each document tier -- [Custom Badges](#creating-custom-badges) — How to create new badges - ---- - -## Badge Syntax - -Badges use the shields.io static badge service rendered as AsciiDoc inline image macros: - -```asciidoc -image:https://img.shields.io/badge/{LABEL}-{VALUE}-{COLOR}?style=flat-square&logo={LOGO}[{ALT TEXT}] -``` - -**URL Encoding Rules** - -- Spaces → `%20` -- Hyphens in values → `--` (double dash) -- Underscores → `__` (double underscore) - -**Style** - -All badges in this project use `?style=flat-square` for visual consistency. - -## Badge Catalog - -### Platform & Project Badges - -Used on root README and `docs/index.adoc`. - -| Badge | AsciiDoc Syntax | Preview Text | -| --- | --- | --- | -| **Platform** | `image:https://img.shields.io/badge/Platform-Azure%20Local-0078D4?style=flat-square&logo=microsoft-azure[Platform: Azure Local]` | Platform: Azure Local | -| **PowerShell Version** | `image:https://img.shields.io/badge/PowerShell-7.2%2B-5391FE?style=flat-square&logo=powershell[PowerShell 7.2+]` | PowerShell 7.2+ | -| **License** | `image:https://img.shields.io/badge/License-MIT-green?style=flat-square[License: MIT]` | License: MIT | -| **Project Status** | `image:https://img.shields.io/badge/Status-Pre--Release-orange?style=flat-square[Status: Pre-Release]` | Status: Pre-Release | -| **Docs Hub** | `image:https://img.shields.io/badge/Docs-index.adoc-blue?style=flat-square&logo=readthedocs[Docs]` | Docs: index.adoc | - -### Tool Badges - -Identify which load testing tool a document covers. Used on tool guide pages and tool-specific READMEs. - -| Badge | AsciiDoc Syntax | Preview Text | -| --- | --- | --- | -| **VMFleet** | `image:https://img.shields.io/badge/Tool-VMFleet-0078D4?style=flat-square[Tool: VMFleet]` | Tool: VMFleet | -| **fio** | `image:https://img.shields.io/badge/Tool-fio-6C3483?style=flat-square[Tool: fio]` | Tool: fio | -| **iPerf3** | `image:https://img.shields.io/badge/Tool-iPerf3-1ABC9C?style=flat-square[Tool: iPerf3]` | Tool: iPerf3 | -| **HammerDB** | `image:https://img.shields.io/badge/Tool-HammerDB-E74C3C?style=flat-square[Tool: HammerDB]` | Tool: HammerDB | -| **stress-ng** | `image:https://img.shields.io/badge/Tool-stress--ng-F39C12?style=flat-square[Tool: stress-ng]` | Tool: stress-ng | - -### Version Badges - -Show the version of a tool or component. Used on tool overview pages. - -| Badge | AsciiDoc Syntax | Preview Text | -| --- | --- | --- | -| **VMFleet Version** | `image:https://img.shields.io/badge/VMFleet-2.1.0.0-blue?style=flat-square[VMFleet 2.1.0.0]` | VMFleet 2.1.0.0 | -| **DiskSpd Version** | `image:https://img.shields.io/badge/DiskSpd-2.2-blue?style=flat-square[DiskSpd 2.2]` | DiskSpd 2.2 | -| **Ansible Version** | `image:https://img.shields.io/badge/Ansible-2.14%2B-EE0000?style=flat-square&logo=ansible[Ansible 2.14+]` | Ansible 2.14+ | - -### Implementation Status Badges - -Indicate whether a tool or feature is implemented. Used on tool overview pages and READMEs. - -| Badge | AsciiDoc Syntax | Preview Text | -| --- | --- | --- | -| **Fully Implemented** | `image:https://img.shields.io/badge/Status-Implemented-brightgreen?style=flat-square[Status: Implemented]` | Status: Implemented | -| **In Progress** | `image:https://img.shields.io/badge/Status-In%20Progress-yellow?style=flat-square[Status: In Progress]` | Status: In Progress | -| **Placeholder** | `image:https://img.shields.io/badge/Status-Placeholder-lightgrey?style=flat-square[Status: Placeholder]` | Status: Placeholder | -| **Deprecated** | `image:https://img.shields.io/badge/Status-Deprecated-red?style=flat-square[Status: Deprecated]` | Status: Deprecated | - -### Document Category Badges - -Identify the type/category of the document. Used on guide documents. - -| Badge | AsciiDoc Syntax | Preview Text | -| --- | --- | --- | -| **Getting Started** | `image:https://img.shields.io/badge/Category-Getting%20Started-2ECC71?style=flat-square[Category: Getting Started]` | Category: Getting Started | -| **Tool Guide** | `image:https://img.shields.io/badge/Category-Tool%20Guide-3498DB?style=flat-square[Category: Tool Guide]` | Category: Tool Guide | -| **Operations** | `image:https://img.shields.io/badge/Category-Operations-9B59B6?style=flat-square[Category: Operations]` | Category: Operations | -| **Reference** | `image:https://img.shields.io/badge/Category-Reference-7F8C8D?style=flat-square[Category: Reference]` | Category: Reference | -| **Standards** | `image:https://img.shields.io/badge/Category-Standards-E67E22?style=flat-square[Category: Standards]` | Category: Standards | - -### Audience & Difficulty Badges - -Indicate the intended audience or skill level. Used on getting-started and tool guide pages. - -| Badge | AsciiDoc Syntax | Preview Text | -| --- | --- | --- | -| **Beginner** | `image:https://img.shields.io/badge/Level-Beginner-brightgreen?style=flat-square[Level: Beginner]` | Level: Beginner | -| **Intermediate** | `image:https://img.shields.io/badge/Level-Intermediate-yellow?style=flat-square[Level: Intermediate]` | Level: Intermediate | -| **Advanced** | `image:https://img.shields.io/badge/Level-Advanced-red?style=flat-square[Level: Advanced]` | Level: Advanced | - -### Audience Role Badges - -Identify the target reader role. Used on guide documents. - -| Badge | AsciiDoc Syntax | Preview Text | -| --- | --- | --- | -| **Infrastructure Engineer** | `image:https://img.shields.io/badge/Audience-Infra%20Engineer-0078D4?style=flat-square[Audience: Infra Engineer]` | Audience: Infra Engineer | -| **DevOps Engineer** | `image:https://img.shields.io/badge/Audience-DevOps-5C2D91?style=flat-square[Audience: DevOps]` | Audience: DevOps | -| **Storage Admin** | `image:https://img.shields.io/badge/Audience-Storage%20Admin-2E86C1?style=flat-square[Audience: Storage Admin]` | Audience: Storage Admin | -| **Network Engineer** | `image:https://img.shields.io/badge/Audience-Network%20Engineer-1ABC9C?style=flat-square[Audience: Network Engineer]` | Audience: Network Engineer | -| **DBA** | `image:https://img.shields.io/badge/Audience-DBA-E74C3C?style=flat-square[Audience: DBA]` | Audience: DBA | - -### OS & Environment Badges - -Indicate required operating system or execution environment. Used on prerequisites and tool pages. - -| Badge | AsciiDoc Syntax | Preview Text | -| --- | --- | --- | -| **Windows** | `image:https://img.shields.io/badge/OS-Windows-0078D6?style=flat-square&logo=windows[OS: Windows]` | OS: Windows | -| **Linux** | `image:https://img.shields.io/badge/OS-Linux-FCC624?style=flat-square&logo=linux&logoColor=black[OS: Linux]` | OS: Linux | -| **WSL2** | `image:https://img.shields.io/badge/OS-WSL2-FCC624?style=flat-square&logo=linux&logoColor=black[OS: WSL2]` | OS: WSL2 | - -### CI/CD Pipeline Badges - -Show pipeline status (link to actual workflows once created). Used on root README. - -| Badge | AsciiDoc Syntax | Preview Text | -| --- | --- | --- | -| **Build Docs** | `image:https://img.shields.io/github/actions/workflow/status/AzureLocal/azurelocal-loadtools/build-docs.yml?style=flat-square&label=Docs%20Build[Docs Build]` | Docs Build: passing | -| **Tests** | `image:https://img.shields.io/github/actions/workflow/status/AzureLocal/azurelocal-loadtools/run-tests.yml?style=flat-square&label=Tests[Tests]` | Tests: passing | -| **Lint** | `image:https://img.shields.io/github/actions/workflow/status/AzureLocal/azurelocal-loadtools/lint.yml?style=flat-square&label=Lint[Lint]` | Lint: passing | - -!!! note - CI/CD badges use the GitHub Actions workflow status endpoint. These will show actual pass/fail status once the workflows exist. - -### Last Updated Badge - -Show when a document was last modified. Used on any document where freshness matters. - -| Badge | AsciiDoc Syntax | Preview Text | -| --- | --- | --- | -| **Last Updated** | `image:https://img.shields.io/badge/Updated-2026--02--16-lightgrey?style=flat-square[Updated: 2026-02-16]` | Updated: 2026-02-16 | - -!!! tip - Update the date in the badge URL each time the document is modified. Use ISO 8601 format (`YYYY-MM-DD`) with double-dash escaping. - -## Badge Placement Rules - -### Placement by Document Tier - -| Tier | Badge Placement | -| --- | --- | -| **Standards** | After all attributes, before the `[abstract]` block. One badge per line. | -| **Guides** | After all attributes, before the first content paragraph. One badge per line. | -| **READMEs** | After all attributes, before the first content paragraph. Badges on the same line (inline) separated by a space. | - -### Example: Guide with Badges - -```asciidoc -= VMFleet Deployment -:toc: left -:toclevels: 3 -... - -image:https://img.shields.io/badge/Tool-VMFleet-0078D4?style=flat-square[Tool: VMFleet] -image:https://img.shields.io/badge/Category-Tool%20Guide-3498DB?style=flat-square[Category: Tool Guide] -image:https://img.shields.io/badge/Status-Implemented-brightgreen?style=flat-square[Status: Implemented] - -This guide covers installing the VMFleet module... -``` - -### Example: Standards with Badges - -```asciidoc -= Document Title — Subtitle -Kristopher Turner -v1.0, 2026-02-16 - -:description: ... -:toc: left -... - -image:https://img.shields.io/badge/Category-Standards-E67E22?style=flat-square[Category: Standards] - -[abstract] -One to three sentences. -``` - -### Example: README with Badges - -```asciidoc -= Azure Local Load Tools -:toc: macro -:toclevels: 2 -:icons: font - -image:https://img.shields.io/badge/Platform-Azure%20Local-0078D4?style=flat-square&logo=microsoft-azure[Platform] image:https://img.shields.io/badge/PowerShell-7.2%2B-5391FE?style=flat-square&logo=powershell[PowerShell] image:https://img.shields.io/badge/License-MIT-green?style=flat-square[License] - -Comprehensive load testing framework... - -toc::[] -``` - -!!! note - In READMEs, placing badges on the same line (separated by spaces) renders them as an inline row. In guides and standards, one badge per line stacks vertically. - -### Required Badges by Location - -| Document | Required Badges | -| --- | --- | -| Root `README.adoc` | Platform, PowerShell Version, License, Project Status | -| `docs/index.adoc` | Platform, Project Status | -| `docs/getting-started/*.adoc` | Category (Getting Started) | -| `docs/tools/{tool}/overview.adoc` | Tool, Status (Implemented/Placeholder), Version (if applicable) | -| `docs/tools/{tool}/*.adoc` (other) | Tool, Category (Tool Guide) | -| `docs/operations/*.adoc` | Category (Operations) | -| `docs/reference/*.adoc` | Category (Reference) | -| `docs/standards/*.adoc` | Category (Standards) | -| `src/solutions/*/README.md` | Tool, Status | - -!!! tip - Audience, Level, OS, and Last Updated badges are **optional** — add them when they provide meaningful context. - -## Creating Custom Badges - -To create a new badge not listed in the catalog: - -1. Go to [https://shields.io/badges/static-badge](https://shields.io/badges/static-badge) -2. Set the label, value, and color -3. Always use `?style=flat-square` for consistency -4. Add `&logo={logo-name}` if an appropriate [Simple Icons](https://simpleicons.org) logo exists -5. Add the new badge to this standards document before using it in other files - -### Color Palette - -Use these colors for consistency across the project: - -| Color | Hex | Usage | -| --- | --- | --- | -| `0078D4` | Microsoft Blue | Platform, Azure-related, VMFleet | -| `5391FE` | PowerShell Blue | PowerShell version | -| `brightgreen` | (shields default) | Implemented, Beginner, positive status | -| `green` | (shields default) | License, Getting Started category | -| `yellow` | (shields default) | In Progress, Intermediate | -| `orange` | (shields default) | Pre-Release, Standards category | -| `red` | (shields default) | Deprecated, Advanced | -| `lightgrey` | (shields default) | Placeholder, Last Updated, neutral | -| `6C3483` | Purple | fio tool | -| `1ABC9C` | Teal | iPerf3 tool, Network Engineer | -| `E74C3C` | Red | HammerDB tool, DBA | -| `F39C12` | Amber | stress-ng tool | -| `3498DB` | Blue | Tool Guide category | -| `9B59B6` | Violet | Operations category | -| `7F8C8D` | Grey | Reference category | +**Canonical reference:** [Badge Library](https://azurelocal.cloud/standards/documentation/badge-library) diff --git a/docs/standards/documentation-standards.md b/docs/standards/documentation-standards.md index fd5dae0..a8692a5 100644 --- a/docs/standards/documentation-standards.md +++ b/docs/standards/documentation-standards.md @@ -1,545 +1,6 @@ -# Documentation Standards — Azure Local Load Tools +# Documentation Standards -![Category: Standards](https://img.shields.io/badge/Category-Standards-E67E22?style=flat-square) +!!! info "Moved to Central Standards" + Documentation standards are now maintained org-wide at the central documentation site. -This document defines the mandatory AsciiDoc documentation standards for all `.adoc` files in the Azure Local Load Tools repository. -It covers document types, required attributes, formatting conventions, and templates. -All documentation must conform to these standards before merging. - -## Quick Navigation - -- [Document Types](#document-types) — Three tiers: Standards, Guides, READMEs -- [Header Attribute Standards](#header-attribute-standards) — Required attributes per document type -- [Heading and Structure Rules](#heading-and-structure-rules) — Heading hierarchy, section numbering -- [Formatting Conventions](#formatting-conventions) — Code blocks, tables, admonitions, links -- [Cross-References and Linking](#cross-references-and-linking) — Anchors, xref, inter-document links -- [Templates](#templates) — Copy-paste templates for each document type -- [File Organization](#file-organization) — Where files go and how they are named - ---- - -## Document Types - -This project uses three distinct document tiers. **Every document is standalone** — viewable independently on GitHub, in VS Code, or rendered to PDF. - -| Tier | Purpose | Location | Examples | -| --- | --- | --- | --- | -| **Standards** | Formal reference documents — rules and specifications | `docs/standards/` | This file, scripting-standards.adoc | -| **Guides** | Standalone documentation — getting-started, tool guides, operations, reference | `docs/getting-started/`, `docs/tools/*/`, `docs/operations/`, `docs/reference/` | installation.adoc, tools/vmfleet/overview.adoc, ci-cd.adoc | -| **READMEs** | Quick-start and overview pages — viewed on GitHub | Root and `src/solutions/*/` | README.adoc, src/solutions/vmfleet/README.md | - -### Tier Comparison - -| Feature | Standards | Guides | READMEs | -| --- | --- | --- | --- | -| `:toc:` placement | `left` (sidebar) | `left` (sidebar) | `macro` (inline) | -| `:toclevels:` | `4` (deep) | `3` (standard) | `2` (light) | -| `:sectnums:` | Yes | Yes | No | -| Quick Navigation block | Yes | No | No | -| Abstract block | Yes | No | No | -| Admonition captions | Emoji (💡 ℹ️ ⚠️) | Font icons | Font icons | -| Author / version line | Yes | No | No | -| Badges | Yes — after attributes, before `[abstract]` | Yes — after attributes, before first content | Yes — after attributes, inline on one line | -| Standalone rendering | Yes — self-contained | Yes — self-contained | Yes — self-contained | - -## Header Attribute Standards - -### Standards Documents - -Full self-contained header with sidebar TOC, emoji admonitions, and abstract: - -```asciidoc -= Document Title — Subtitle -Kristopher Turner -v1.0, 2026-02-13 - -:description: Brief description for metadata. -:keywords: comma, separated, keywords -:toc: left -:toclevels: 4 -:sectnums: -:icons: font -:source-highlighter: rouge -:experimental: -:imagesdir: ../images -:tip-caption: 💡 -:note-caption: ℹ️ -:warning-caption: ⚠️ -:caution-caption: 🔥 -:important-caption: ❗ - -image:https://img.shields.io/badge/Category-Standards-E67E22?style=flat-square[Category: Standards] - -[abstract] -One to three sentences describing the document's purpose and audience. - -[discrete] -== Quick Navigation - -* <> — Brief description -* <> — Brief description - -''' -``` - -### Guide Documents - -Standalone documents with their own sidebar TOC. Each guide has a level-1 title and includes project-wide attributes: - -```asciidoc -= Guide Title -:toc: left -:toclevels: 3 -:sectnums: -:icons: font -:source-highlighter: rouge -:experimental: -:imagesdir: ../images -:project-name: Azure Local Load Testing Framework -:project-repo: https://github.com/AzureLocal/azurelocal-loadtools - -image:https://img.shields.io/badge/Category-Getting%20Started-2ECC71?style=flat-square[Category: Getting Started] - -Brief introduction to the topic. - -== First Section -``` - -!!! note - Replace the Category badge with the appropriate one for the document location (Tool Guide, Operations, Reference). See [Badge Standards](badge-standards.md) for the full catalog. - -!!! note - Adjust `:imagesdir:` based on folder depth — `../images` for one level deep, `../../images` for two levels (e.g., `tools/vmfleet/`). - -!!! tip - Guides are also included into `main.adoc` for optional combined PDF builds. When included, the level-1 title is shifted by `leveloffset=+1`. - -### README Documents - -GitHub-friendly inline TOC: - -```asciidoc -= Project or Solution Name -:toc: macro -:toclevels: 2 -:icons: font - -image:https://img.shields.io/badge/Platform-Azure%20Local-0078D4?style=flat-square&logo=microsoft-azure[Platform] image:https://img.shields.io/badge/PowerShell-7.2%2B-5391FE?style=flat-square&logo=powershell[PowerShell] image:https://img.shields.io/badge/License-MIT-green?style=flat-square[License] - -Brief one-paragraph description. - -toc::[] - -== First Section -``` - -!!! tip - The `:toc: macro` + `toc::[]` pattern places the table of contents exactly where you put the macro — ideal for GitHub rendering where sidebar TOCs are not supported. - -### Book Document (`main.adoc` — Optional PDF Builder) - -`main.adoc` is retained as an optional combined PDF builder. It includes all standalone guides and is **not** the primary documentation entry point — `docs/index.adoc` is. - -```asciidoc -= Book Title -:author: AzureLocal -:email: azurelocal@microsoft.com -:revdate: 2026-02-13 -:revnumber: 0.1.0 -:doctype: book -:toc: left -:toclevels: 3 -:sectnums: -:sectnumlevels: 3 -:icons: font -:source-highlighter: rouge -:experimental: -:imagesdir: images -:pdf-themesdir: themes -:pdf-theme: azurelocal-theme - -// Project-wide attributes (reusable variables) -:project-name: Azure Local Load Testing Framework -:project-repo: https://github.com/AzureLocal/azurelocal-loadtools -``` - -### Documentation Hub (`docs/index.adoc`) - -`index.adoc` is the landing page with links to all documentation organized by category. It uses the same header pattern as guides. - -## Heading and Structure Rules - -### Heading Levels - -| Level | Usage | Scope | -| --- | --- | --- | -| `=` | Document title | Every standalone document (guides, standards, READMEs, `main.adoc`) | -| `==` | Major sections | Top-level sections within any document | -| `===` | Subsections | Detailed topic areas within a section | -| `====` | Sub-subsections | Granular detail, usually the deepest needed | -| `=====` | Avoid | If you need this depth, consider restructuring | - -### Section Anchors - -AsciiDoc auto-generates anchors from headings, but for cross-reference stability, use explicit anchors for any section you link to: - -```asciidoc -[[my-stable-anchor]] -== My Section Title -``` - -!!! warning - Auto-generated anchors break when headings are renamed. Use explicit `[[anchor]]` IDs for any section referenced from other documents. - -### Section Numbering - -- Standards documents: `:sectnums:` is **on** — sections show as 1, 1.1, 1.2, etc. -- READMEs: `:sectnums:` is **off** — no numbering for casual docs -- Chapters: Inherit from `main.adoc` (on by default) - -## Formatting Conventions - -### Code Blocks - -Always specify the language for syntax highlighting: - -````asciidoc -[source,powershell] ----- -Get-Process | Where-Object { $_.CPU -gt 100 } ----- -```` - -Supported languages in this project: - -| Language | Usage | -| --- | --- | -| `powershell` | All PowerShell scripts and examples | -| `yaml` | Configuration files | -| `json` | Solution configs, schema files | -| `xml` | PerfMon counter definitions, event log queries | -| `bash` | Ansible playbooks, Linux commands | -| `asciidoc` | Documentation examples (like in this file) | - -### Code Callouts - -Use numbered callouts to annotate code: - -````asciidoc -[source,powershell] ----- -$config = Import-MasterConfig # <1> -$filtered = $config | Where-Object { $_.solutions -contains 'vmfleet' } # <2> ----- -<1> Loads the master YAML and caches it -<2> Filters to VMFleet-tagged variables only -```` - -!!! tip - Callouts make code examples self-documenting. Use them for any code block longer than three lines. - -### Tables - -Use the AsciiDoc table syntax with explicit column specs: - -```asciidoc -[cols="1,2,3"] -|=== -| Column A | Column B | Column C - -| Data 1 -| Data 2 -| Data 3 -|=== -``` - -Rules for tables: - -- Always include `[cols="..."]` with relative widths -- First row is always the header row -- Put each cell on its own line for readability in source -- Use `a` column type for cells containing AsciiDoc markup: `[cols="1,2a"]` - -### Admonitions - -Use admonition blocks for callouts. Each type has a specific purpose: - -| Type | Icon | When to Use | -| --- | --- | --- | -| `TIP` | 💡 | Helpful suggestions, best practices, shortcuts | -| `NOTE` | ℹ️ | Additional context, clarifications, "good to know" | -| `WARNING` | ⚠️ | Potential problems if instructions are not followed | -| `CAUTION` | 🔥 | Risk of data loss, security exposure, or breaking changes | -| `IMPORTANT` | ❗ | Critical information that must not be skipped | - -Inline syntax: - -```asciidoc -TIP: Run the linter before committing. - -WARNING: This will delete all fleet VMs. -``` - -Block syntax (for multi-line content): - -```asciidoc -[WARNING] -==== -This operation is destructive and cannot be undone. -Make sure you have a backup before proceeding. -==== -``` - -### Bold, Italic, and Monospace - -| Style | Syntax | Usage | -| --- | --- | --- | -| **Bold** | `**bold text**` | UI elements, emphasis on key terms | -| *Italic* | `_italic text_` | First use of a defined term, file descriptions | -| `Monospace` | `` `monospace` `` | Code references, file names, variable names, commands | -| **`Bold mono`** | `` **`bold mono`** `` | Tool names in tables (e.g., **VMFleet**) | - -### Lists - -Use `*` for unordered lists, `.` for ordered lists: - -```asciidoc -* First item -* Second item -** Nested item - -. Step one -. Step two -.. Sub-step -``` - -!!! important - Do not mix `*` and `-` for unordered lists. Always use `*` in this project. - -## Cross-References and Linking - -### Internal Cross-References - -Link to sections within the same document: - -```asciidoc -See <> for details. - -See <> for details. -``` - -### Inter-Document References - -Link to sections in other documents using `xref`: - -```asciidoc -See xref:chapters/06-vmfleet-guide.adoc#deploy-phase[the deployment phase]. -``` - -### External Links - -```asciidoc -https://github.com/AzureLocal/azurelocal-loadtools[Azure Local Load Tools repository] - -https://docs.microsoft.com/en-us/azure-stack/hci/[Azure Stack HCI documentation] -``` - -### Image References - -```asciidoc -image::diagram-name.png[Alt text, width=600] -``` - -- Store images in `docs/images/` -- Use descriptive file names: `vmfleet-pipeline-workflow.png`, not `diagram1.png` -- Always include alt text -- Specify width for large images to control rendering - -## Templates - -### Standards Document Template - -```asciidoc -= Title — Subtitle -Kristopher Turner -v1.0, 2026-02-13 - -:description: Single sentence describing the document. -:keywords: relevant, keywords -:toc: left -:toclevels: 4 -:sectnums: -:icons: font -:source-highlighter: rouge -:experimental: -:imagesdir: ../images -:tip-caption: 💡 -:note-caption: ℹ️ -:warning-caption: ⚠️ -:caution-caption: 🔥 -:important-caption: ❗ - -image:https://img.shields.io/badge/Category-Standards-E67E22?style=flat-square[Category: Standards] - -[abstract] -One to three sentences. - -[discrete] -== Quick Navigation - -* <> — Description -* <> — Description - -''' - -== First Section - -Content here. - -== Second Section - -Content here. -``` - -### Guide Template - -```asciidoc -= Guide Title -:toc: left -:toclevels: 3 -:sectnums: -:icons: font -:source-highlighter: rouge -:experimental: -:imagesdir: ../images -:project-name: Azure Local Load Testing Framework -:project-repo: https://github.com/AzureLocal/azurelocal-loadtools - -image:https://img.shields.io/badge/Category-Getting%20Started-2ECC71?style=flat-square[Category: Getting Started] - -Brief introduction to the topic. - -== First Section - -Content here. - -== Second Section - -Content here. -``` - -### README Template (AsciiDoc) - -```asciidoc -= Component Name -:toc: macro -:toclevels: 2 -:icons: font - -image:https://img.shields.io/badge/Platform-Azure%20Local-0078D4?style=flat-square&logo=microsoft-azure[Platform] image:https://img.shields.io/badge/License-MIT-green?style=flat-square[License] - -Brief one-paragraph description of what this component does. - -toc::[] - -== Overview - -What this component does and why it exists. - -== Quick Start - -[source,powershell] ----- -# Minimal usage example ----- - -== Configuration - -Describe configuration options. - -== Usage - -Describe detailed usage. -``` - -### README Template (Markdown) - -For solution READMEs that use Markdown: - -````markdown -# Component Name - -Brief description. - -## Overview - -What this component does. - -## Quick Start - -```powershell -# Minimal usage example -``` - -## Configuration - -Options and settings. -```` - -!!! note - The root `README.adoc` uses AsciiDoc. Solution-level READMEs (`src/solutions/*/README.md`) currently use Markdown. Both are acceptable — but be consistent within each directory. - -## File Organization - -### Documentation Directory Structure - -```text -docs/ -├── index.adoc # Documentation hub — links to everything -├── main.adoc # Optional combined PDF builder -├── getting-started/ # Shared setup docs (all tools) -│ ├── introduction.adoc -│ ├── architecture.adoc -│ ├── prerequisites.adoc -│ ├── installation.adoc -│ └── configuration.adoc -├── tools/ # One folder per tool — self-contained -│ ├── vmfleet/ -│ │ ├── overview.adoc -│ │ ├── prerequisites.adoc -│ │ ├── deployment.adoc -│ │ ├── workload-profiles.adoc -│ │ ├── monitoring.adoc -│ │ ├── reporting.adoc -│ │ └── troubleshooting.adoc -│ ├── fio/ -│ │ └── overview.adoc -│ ├── iperf/ -│ │ └── overview.adoc -│ ├── hammerdb/ -│ │ └── overview.adoc -│ └── stress-ng/ -│ └── overview.adoc -├── operations/ # Cross-tool operational docs -│ ├── ci-cd.adoc -│ └── credential-management.adoc -├── reference/ # Reference material -│ ├── variable-reference.adoc -│ ├── cmdlet-reference.adoc -│ └── glossary.adoc -├── standards/ # Standards documents (self-contained) -│ ├── documentation-standards.adoc -│ ├── scripting-standards.adoc -│ └── variables-environment-standards.adoc -├── diagrams/ # Draw.io and other diagram sources -├── images/ # Rendered images and screenshots -└── themes/ # PDF theme files -``` - -### File Naming Rules - -| Location | Convention | Example | -| --- | --- | --- | -| `getting-started/` | `kebab-case.adoc` | `prerequisites.adoc` | -| `tools/{tool}/` | `kebab-case.adoc` | `workload-profiles.adoc` | -| `operations/` | `kebab-case.adoc` | `credential-management.adoc` | +**Canonical reference:** [Documentation Standards](https://azurelocal.cloud/standards/documentation/documentation-standards) diff --git a/docs/standards/scripting-standards.md b/docs/standards/scripting-standards.md index e95223d..bd4f089 100644 --- a/docs/standards/scripting-standards.md +++ b/docs/standards/scripting-standards.md @@ -1,444 +1,6 @@ -# PowerShell Scripting Standards — Azure Local Load Tools +# Scripting Standards -![Category: Standards](https://img.shields.io/badge/Category-Standards-E67E22?style=flat-square) +!!! info "Moved to Central Standards" + Scripting standards are now maintained org-wide at the central documentation site. -This document defines the mandatory PowerShell scripting standards for all scripts and modules in the Azure Local Load Tools repository. -Every contributor must follow these standards. -Scripts that do not comply must be refactored before merging. - -## Quick Navigation - -- [File and Naming Conventions](#file-and-naming-conventions) — File names, function names, approved verbs -- [Script Structure](#script-structure) — Required layout for every `.ps1` file -- [Parameter Standards](#parameter-standards) — Mandatory/optional params, validation, override chain -- [Module Structure](#module-structure) — `.psd1` / `.psm1` layout, exports, versioning -- [Error Handling](#error-handling) — `try`/`catch`, `$ErrorActionPreference`, `-ErrorAction Stop` -- [Logging Standards](#logging-standards) — JSON-lines, correlation IDs, severity levels -- [Testing Requirements](#testing-requirements) — Pester conventions, naming, coverage -- [PSScriptAnalyzer Rules](#psscriptanalyzer-rules) — Enforced linting rules and exclusions - ---- - -## File and Naming Conventions - -### File Naming - -All PowerShell files must follow PascalCase naming with the `Verb-Noun` pattern: - -| Type | Pattern | Example | -| --- | --- | --- | -| Standalone scripts | `Verb-Noun.ps1` | `Install-VMFleet.ps1` | -| Pipeline orchestrators | `Invoke-Pipeline.ps1` | `Invoke-VMFleetPipeline.ps1` | -| Monitoring scripts | `Verb-Metrics.ps1` | `Collect-StorageMetrics.ps1` | -| Helper scripts | `PascalCase-Description.ps1` | `Common-Functions.ps1` | -| Module files | `ModuleName.psm1` / `.psd1` | `ConfigManager.psm1` | -| Test files | `ModuleName.Tests.ps1` | `Logger.Tests.ps1` | - -### Function Naming - -- Use **approved PowerShell verbs** only — run `Get-Verb` to see the full list. -- Common approved verbs in this project: - -| Verb | Usage | -| --- | --- | -| `Install` | Installing modules or prerequisites | -| `Deploy` | Creating VMs, provisioning resources | -| `Start` | Launching workloads or tests | -| `Stop` | Halting workloads | -| `Collect` | Gathering results or metrics | -| `Remove` | Tearing down resources | -| `Invoke` | Running a pipeline or multi-phase workflow | -| `Watch` | Real-time monitoring | -| `Import` | Loading configuration into memory | -| `Export` | Writing configuration or data to disk | -| `Get` | Retrieving status, state, or info | -| `Set` | Modifying state or configuration | -| `New` | Creating objects (e.g., `New-RunState`) | -| `Push` | Sending data to external services (e.g., Azure Monitor) | - -!!! caution - Never use unapproved verbs like `Create`, `Delete`, `Run`, or `Setup`. PSScriptAnalyzer will flag these. - -### Variable Naming - -- **Local variables:** `$camelCase` (e.g., `$vmCount`, `$logSession`) -- **Script-scoped variables:** `$script:PascalCase` (e.g., `$script:ActiveSessions`) -- **Parameters:** `PascalCase` (e.g., `$ProjectRoot`, `$SolutionConfigPath`) -- **Constants:** `$UPPER_SNAKE_CASE` or descriptive `$PascalCase` (e.g., `$ErrorActionPreference`) - -## Script Structure - -Every standalone `.ps1` script must follow this exact structure: - -```powershell -# ============================================================================= -# Verb-Noun.ps1 - Azure Local Load Tools -# ============================================================================= -# Brief description of what this script does. -# Phase N of the pipeline (if applicable). -# ============================================================================= - -#Requires -Version 7.2 # <1> - -[CmdletBinding(SupportsShouldProcess)] # <2> -param( - [Parameter(Mandatory)] # <3> - [string]$RequiredParam, - - [Parameter()] - [string]$OptionalParam, - - [Parameter()] - [string]$ProjectRoot # <4> -) - -$ErrorActionPreference = 'Stop' # <5> - -# ---- Resolve Project Root ---- # <6> -if (-not $ProjectRoot) { - $ProjectRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..\..')).Path -} - -# ---- Bootstrap ---- # <7> -. (Join-Path $ProjectRoot 'src\core\powershell\helpers\Common-Functions.ps1') -Import-Module (Join-Path $ProjectRoot 'src\core\powershell\modules\Logger\Logger.psm1') -Force -Import-Module (Join-Path $ProjectRoot 'src\core\powershell\modules\ConfigManager\ConfigManager.psm1') -Force - -# ---- Initialize Logging ---- # <8> -$logSession = Start-LogSession -Component '-' ` - -LogRootPath (Join-Path $ProjectRoot 'logs\') - -try { - Write-Log -Message 'Starting ' -Severity Information - - # ... main logic ... # <9> - - Write-Log -Message ' completed successfully' -Severity Information - Write-Host ' complete.' -ForegroundColor Green -} -catch { - Write-Log -Message "Error: $_" -Severity Error -ErrorRecord $_ # <10> - throw -} -finally { - Stop-LogSession # <11> -} -``` - -1. Always require PowerShell 7.2+ -2. All scripts must support `-WhatIf` and `-Confirm` -3. Mark truly required parameters with `[Parameter(Mandatory)]` -4. `$ProjectRoot` is always optional — auto-resolved from `$PSScriptRoot` -5. Set `$ErrorActionPreference = 'Stop'` at the top of every script -6. Auto-resolve project root relative to script location -7. Dot-source helpers, import modules with `-Force` -8. Start a log session before any work -9. Main logic wrapped in `try` -10. Log errors with the `$ErrorRecord` for full stack trace -11. Always close the log session in `finally` - -### Comment Block Header - -Every script **must** start with the banner comment: - -```powershell -# ============================================================================= -# Script-Name.ps1 - Azure Local Load Tools -# ============================================================================= -# Description of the script purpose. -# ============================================================================= -``` - -!!! note - This banner replaces traditional comment-based help at the *file* level. Functions inside modules use `<# .SYNOPSIS #>` blocks instead. - -## Parameter Standards - -### Required vs Optional - -| Attribute | When to Use | Example | -| --- | --- | --- | -| `[Parameter(Mandatory)]` | Value must be provided — no fallback exists | Rarely used; most params have config fallbacks | -| `[Parameter()]` | Has a fallback via solution config or master config | Most parameters in this project | -| `[ValidateSet(...)]` | Constrained to known values | `[ValidateSet('PDF', 'DOCX', 'XLSX')]` | -| `[ValidateRange(...)]` | Numeric bounds | `[ValidateRange(1, 1000)]` | -| `[PSCredential]` | Credential parameters | `[PSCredential]$Credential` | -| `[switch]` | Boolean flags | `[switch]$Force`, `[switch]$Resume` | - -### Three-Level Override Chain - -All configuration-driven parameters must follow this override chain: - -```powershell -$vmCount = if ($VMCount) { $VMCount } # <1> - elseif ($profile) { $profile.vm_count } # <2> - else { [int]$solutionConfig.vmfleet_vm_count } # <3> -``` - -1. **Explicit parameter** — highest priority, user passed it directly -2. **Profile config** — loaded from `config/profiles//*.yml` -3. **Solution config** — generated from `master-environment.yml` - -!!! important - Never skip levels. Every configurable parameter must check all three levels in this exact order. - -### Default Config Path Resolution - -When a config path parameter is not provided, resolve it from `$ProjectRoot`: - -```powershell -if (-not $SolutionConfigPath) { - $SolutionConfigPath = Join-Path $ProjectRoot 'config\variables\solutions\vmfleet.json' -} -``` - -## Module Structure - -### File Layout - -Each module consists of two files in its own directory: - -```text -src/core/powershell/modules/ -└── ModuleName/ - ├── ModuleName.psd1 # Module manifest - └── ModuleName.psm1 # Module implementation -``` - -### Manifest Requirements (`.psd1`) - -Every module manifest must include: - -```powershell -@{ - RootModule = 'ModuleName.psm1' - ModuleVersion = '0.1.0' # <1> - GUID = '' - Author = 'AzureLocal' - Description = 'Brief module description' - PowerShellVersion = '7.2' - FunctionsToExport = @('Public-Function1', 'Public-Function2') # <2> - PrivateData = @{ - PSData = @{ - ProjectUri = 'https://github.com/AzureLocal/azurelocal-loadtools' # <3> - } - } -} -``` - -1. Follow semantic versioning -2. Explicitly list exported functions — never use `'*'` -3. Always include the project URI - -### Module Implementation (`.psm1`) - -```powershell -# ============================================================================= -# ModuleName Module - Azure Local Load Tools -# ============================================================================= -# Module description. -# ============================================================================= - -# Module-level variables -$script:ProjectRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..\..\..')).Path - -function Public-Function { - <# - .SYNOPSIS - One-line description. - .DESCRIPTION - Detailed description. - .PARAMETER ParamName - Parameter description. - .OUTPUTS - Output type description. - .EXAMPLE - Public-Function -ParamName 'value' - #> - [CmdletBinding()] - param( ... ) - # Implementation -} -``` - -!!! warning - Every public function must have a complete comment-based help block with `.SYNOPSIS`, `.DESCRIPTION`, `.PARAMETER`, and `.OUTPUTS` at minimum. - -## Error Handling - -### Global Error Preference - -Every script sets `$ErrorActionPreference = 'Stop'` at the top. This ensures all errors are terminating. - -### Try/Catch Pattern - -```powershell -try { - # Operations that may fail - Invoke-Command -ComputerName $node -ScriptBlock { - Import-Module VMFleet -ErrorAction Stop # <1> - # ... - } -Credential $cred -} -catch { - Write-Log -Message "Failed: $_" -Severity Error -ErrorRecord $_ # <2> - throw # <3> -} -``` - -1. Use `-ErrorAction Stop` on individual cmdlets inside remote sessions -2. Always log before re-throwing -3. Re-throw to propagate — let the caller decide recovery - -### ShouldProcess for Destructive Operations - -Wrap all cluster-modifying operations in `$PSCmdlet.ShouldProcess()`: - -```powershell -if ($PSCmdlet.ShouldProcess($primaryNode, 'Stop VMFleet workload')) { - Invoke-Command -ComputerName $primaryNode -ScriptBlock { - Import-Module VMFleet -ErrorAction Stop - Stop-Fleet - } -Credential $clusterCred -} -``` - -!!! tip - This enables users to run scripts with `-WhatIf` for a dry run or `-Confirm` for step-by-step approval. - -## Logging Standards - -### Log Format - -All logs are written in **JSON-lines** format (`.jsonl`), one JSON object per line: - -```json -{"timestamp":"2026-02-13T10:30:00.000Z","severity":"INFO","component":"VMFleet-Install","correlation_id":"abc123","message":"VMFleet module installation complete","data":null} -``` - -### Severity Levels - -| Level | When to Use | -| --- | --- | -| `DEBUG` | Verbose diagnostic info (parameter values, intermediate state) | -| `INFO` | Normal operation milestones (phase started, phase completed) | -| `WARNING` | Non-fatal issues (fallback used, optional feature unavailable) | -| `ERROR` | Failures that stop the current operation | -| `CRITICAL` | Unrecoverable failures requiring immediate attention | - -### Log Session Lifecycle - -```powershell -# Start -$logSession = Start-LogSession -Component 'VMFleet-Deploy' ` - -LogRootPath (Join-Path $ProjectRoot 'logs\vmfleet') - -# Write entries -Write-Log -Message 'Starting deployment' -Severity Information -Write-Log -Message 'Deployment complete' -Severity Information -Data @{ - vm_count = $vmCount - duration = $elapsed.TotalSeconds -} - -# Stop (always in finally block) -Stop-LogSession -``` - -### Log Directory Convention - -```text -logs/ -├── vmfleet/ # VMFleet component logs -├── fio/ # fio component logs -├── pipeline/ # Orchestrator logs -├── monitoring/ # Metric collection logs -└── reports/ # Report generation logs -``` - -## Testing Requirements - -### Framework - -All tests use **Pester v5+**. - -### File Naming - -Test files follow the pattern `.Tests.ps1` and live in the `tests/` directory: - -```text -tests/ -├── ConfigManager.Tests.ps1 -├── Logger.Tests.ps1 -├── ReportGenerator.Tests.ps1 -├── StateManager.Tests.ps1 -└── PSScriptAnalyzer.ps1 -``` - -### Test Structure - -```powershell -BeforeAll { - $modulePath = Join-Path $PSScriptRoot '..\src\core\powershell\modules\\.psm1' - Import-Module $modulePath -Force -} - -Describe ' Module' { - Context '' { - It 'Should ' { - # Arrange - $input = ... - - # Act - $result = Function-Name -Param $input - - # Assert - $result | Should -Be $expected - } - } -} -``` - -### Test Isolation - -- Use `$TestDrive` for any file I/O — never write to the real project tree -- Import modules in `BeforeAll`, not at the top of the file -- Each `It` block must be independent — no shared mutable state between tests - -### Coverage Expectations - -| Area | Minimum Requirement | -| --- | --- | -| Core modules (`ConfigManager`, `Logger`, `StateManager`) | One `Describe` block per public function | -| Solution scripts | Integration test via pipeline orchestrator | -| Config validation | Schema validation tests for all JSON and YAML files | - -## PSScriptAnalyzer Rules - -### Running the Linter - -```powershell -# Lint all scripts -pwsh -File tests/PSScriptAnalyzer.ps1 - -# Lint with auto-fix -pwsh -File tests/PSScriptAnalyzer.ps1 -Fix -``` - -### Enforced Rules - -All default PSScriptAnalyzer rules are enforced **except** the following exclusions: - -| Excluded Rule | Reason | -| --- | --- | -| `PSAvoidUsingConvertToSecureStringWithPlainText` | Used in demo/template code for examples only | - -### Severity Policy - -| Severity | Policy | -| --- | --- | -| **Error** | Must be fixed before merge — PR will be blocked | -| **Warning** | Must be fixed before merge unless a documented exception exists | -| **Information** | Should be addressed; not a merge blocker | - -!!! tip - Run `tests/PSScriptAnalyzer.ps1` locally before every commit. The CI pipeline (`lint.yml`) runs this automatically on PR. +**Canonical reference:** [Scripting Standards](https://azurelocal.cloud/standards/scripting/scripting-standards) diff --git a/docs/standards/variables-environment-standards.md b/docs/standards/variables-environment-standards.md index 09de017..082842b 100644 --- a/docs/standards/variables-environment-standards.md +++ b/docs/standards/variables-environment-standards.md @@ -1,446 +1,8 @@ -# Variables & Environment Standards — Azure Local Load Tools +# Variable & Environment Standards -![Category: Standards](https://img.shields.io/badge/Category-Standards-E67E22?style=flat-square) +!!! info "Moved to Central Standards" + Variable management standards are now maintained org-wide at the central documentation site. -This document defines the mandatory standards for all configuration files, environment variables, YAML structures, JSON schemas, and the variable override chain in the Azure Local Load Tools repository. -Every configuration file must conform to these standards before merging. +**Canonical reference:** [Variable Management Standard](https://azurelocal.cloud/docs/implementation/04-variable-management-standard) -## Quick Navigation - -- [Configuration File Hierarchy](#configuration-file-hierarchy) — How config files relate to each other -- [Master Environment File](#master-environment-file) — `master-environment.yml` structure and rules -- [Variable Definition Standards](#variable-definition-standards) — Naming, types, tags, categories -- [Solution JSON Configs](#solution-json-configs) — Auto-generated per-solution JSON files -- [Cluster Configuration](#cluster-configuration) — Cluster YAML file standards -- [Workload Profiles](#workload-profiles) — Profile YAML format and conventions -- [Credential Configuration](#credential-configuration) — Key Vault mappings, never plaintext -- [Schema Validation](#schema-validation) — JSON Schema requirements - ---- - -## Configuration File Hierarchy - -The configuration system follows a layered architecture: - -```text -config/ -├── variables/ -│ ├── master-environment.yml # <1> Master source of truth -│ ├── schema.json # <2> JSON Schema — validates master -│ └── solutions/ -│ ├── vmfleet.json # <3> Auto-generated by ConfigManager -│ ├── fio.json -│ ├── iperf.json -│ ├── hammerdb.json -│ └── stress-ng.json -├── clusters/ -│ └── example-cluster.yml # <4> Per-cluster connection details -├── credentials/ -│ └── keyvault-config.yml # <5> Maps logical names to Key Vault secrets -└── profiles/ - └── vmfleet/ # <6> Preset parameter sets - ├── general.yml - ├── peak-iops.yml - ├── sql-oltp.yml - └── vdi.yml -``` - -1. **Master source of truth** — all variables defined here with solution tags -2. **JSON Schema** — validates the structure of `master-environment.yml` -3. **Solution configs** — auto-generated by `ConfigManager` from master file -4. **Cluster configs** — per-cluster connection details and topology -5. **Credential config** — maps logical names to Key Vault secrets -6. **Workload profiles** — preset parameter sets for common test scenarios - -### Data Flow - -```text -master-environment.yml - │ - ▼ (ConfigManager filters by solution tag) - solutions/*.json - │ - ▼ (Script reads at runtime) - Three-Level Override Chain: - 1. Explicit parameter → highest priority - 2. Profile config → middle priority - 3. Solution config → lowest priority (default) -``` - -!!! important - Variables flow in one direction only: master → solution JSON → runtime. Never edit solution JSON files by hand — they are generated artifacts. - -## Master Environment File - -### Location - -`config/variables/master-environment.yml` - -### Required Structure - -```yaml -metadata: - version: "1.0.0" # <1> - description: "Master environment configuration for Azure Local Load Tools" - last_modified: "2026-02-13" # <2> - -variables: # <3> - - name: variable_name - value: default_value - type: string - required: true - description: "Human-readable description" - solutions: [vmfleet, fio] - category: cluster -``` - -1. Semantic version — increment on every change -2. Update `last_modified` on every edit -3. `variables` is an array of variable definition objects - -### Metadata Block - -| Field | Type | Description | -| --- | --- | --- | -| `version` | string | Semantic version (`major.minor.patch`) | -| `description` | string | Purpose of this configuration file | -| `last_modified` | date | ISO 8601 date of last modification | - -!!! warning - Always update `version` and `last_modified` when editing the master file. CI validation checks for stale dates. - -## Variable Definition Standards - -### Required Fields - -Every variable entry in `master-environment.yml` must include **all** of these fields: - -| Field | Type | Required | Description | -| --- | --- | --- | --- | -| `name` | string | Yes | Unique identifier in `snake_case` | -| `value` | any | Yes | Default value (empty string `""` or `[]` for unset) | -| `type` | string | Yes | One of: `string`, `integer`, `boolean`, `array` | -| `required` | boolean | Yes | Whether the variable must have a non-empty value at runtime | -| `description` | string | Yes | Human-readable explanation of the variable's purpose | -| `solutions` | array | Yes | List of solutions that consume this variable | -| `category` | string | No | Logical grouping (see [Variable Categories](#variable-categories)) | -| `sensitive` | boolean | No | If `true`, value must come from Key Vault at runtime | - -### Variable Naming Rules - -| Rule | Description | Example | -| --- | --- | --- | -| Format | Always `snake_case`, lowercase | `vm_count_per_node` | -| Pattern | `^[a-z][a-z0-9_]*$` | Enforced by JSON Schema | -| Solution prefix | Use solution name as prefix for solution-specific variables | `vmfleet_admin_username`, `hammerdb_db_name` | -| Shared variables | No prefix for variables shared across solutions | `cluster_name`, `csv_path` | -| Avoid abbreviations | Use full words unless the abbreviation is universally understood | `vm_memory_gb` not `vm_mem_gb` | - -!!! caution - Variable names are used as JSON keys in solution configs. Renaming a variable is a breaking change — update all scripts that reference it. - -### Variable Types - -| Type | YAML Value | JSON Equivalent | -| --- | --- | --- | -| `string` | `"text value"` | `"text value"` | -| `integer` | `42` | `42` | -| `boolean` | `true` / `false` | `true` / `false` | -| `array` | `[item1, item2]` | `["item1", "item2"]` | - -### Solution Tags - -The `solutions` field is an array of solution identifiers. Valid values: - -- `vmfleet` -- `fio` -- `iperf` -- `hammerdb` -- `stress-ng` - -A variable tagged with multiple solutions will appear in each solution's generated JSON: - -```yaml -- name: cluster_name - value: "" - type: string - required: true - description: "Azure Local cluster name" - solutions: [vmfleet, fio, iperf, hammerdb, stress-ng] # Shared across all - category: cluster -``` - -### Variable Categories - -| Category | Variables In This Group | -| --- | --- | -| `cluster` | Cluster identity, nodes, storage paths, network config | -| `deployment` | VM counts, memory, CPU, disk sizes | -| `testing` | Block sizes, write ratios, durations, thresholds | -| `monitoring` | Log Analytics workspace, collection intervals, Azure Monitor settings | -| `reporting` | Report formats, output paths, template paths | -| `credentials` | Usernames, passwords (sensitive — Key Vault only) | -| `logging` | Log levels, retention, paths | - -### Sensitive Variables - -Variables marked `sensitive: true` must follow these rules: - -1. The `value` field in master config must be empty (`""`) -2. Actual values are retrieved at runtime from Azure Key Vault -3. The mapping from variable name to Key Vault secret is in `keyvault-config.yml` -4. Scripts must never write sensitive values to logs - -```yaml -- name: vmfleet_admin_password - value: "" # <1> - type: string - required: true - sensitive: true # <2> - description: "Password for VMFleet admin account" - solutions: [vmfleet] - category: credentials -``` - -1. Always empty — never store the actual password -2. Marks this variable for Key Vault retrieval - -!!! caution - Never commit actual credentials to any configuration file. The `.gitignore` should protect against this, but manual vigilance is still required. - -## Solution JSON Configs - -### Generation - -Solution JSON configs are **generated** by the `Export-SolutionConfig` function in `ConfigManager`: - -```powershell -Export-SolutionConfig -ConfigPath 'config/variables/master-environment.yml' ` - -Solution 'VMFleet' ` - -OutputPath 'config/variables/solutions/vmfleet.json' -``` - -### Format - -```json -{ - "_metadata": { - "generated_at": "2026-02-13T10:00:00Z", - "solution": "VMFleet", - "source": "config/variables/master-environment.yml", - "variable_count": 25 - }, - "cluster_name": "", - "cluster_domain": "", - "vm_count_per_node": 10, - "vmfleet_admin_username": "FleetAdmin" -} -``` - -### Rules - -1. **Never edit by hand** — these are generated artifacts -2. **Always regenerate** after editing `master-environment.yml` -3. **Commit the generated files** — they are checked into source control for offline/airgapped use -4. The `_metadata` block is informational and ignored by scripts - -!!! tip - Run `Export-SolutionConfig` for all solutions after any master config change. The CI pipeline validates that generated files are in sync. - -## Cluster Configuration - -### Location - -`config/clusters/.yml` - -### Required Structure - -```yaml -cluster: - # Identity - name: "hci-cluster-01" # <1> - domain: "contoso.local" - description: "Production Azure Local cluster" - - # Nodes - nodes: # <2> - - name: "hci-node-01" - management_ip: "10.0.0.1" - storage_ip: "10.0.1.1" - - # Storage - csv_path: "C:\\ClusterStorage\\Volume1" - collect_volume_path: "C:\\ClusterStorage\\Collect" - base_vhd_path: "C:\\ClusterStorage\\Collect\\BaseImage.vhdx" - - # WinRM - winrm: # <3> - port: 5985 - use_ssl: false - authentication: "Kerberos" - skip_ca_check: false - operation_timeout_seconds: 300 - - # Networks - networks: # <4> - management: - subnet: "10.0.0.0/24" - vlan_id: 0 - storage: - subnet: "10.0.1.0/24" - vlan_id: 100 - rdma_enabled: true - compute: - subnet: "10.0.2.0/24" - vlan_id: 200 - - # Linux VMs (for fio, iperf, stress-ng, hammerdb) - linux_vms: [] # <5> -``` - -1. Cluster name must match the actual failover cluster name -2. List every node — used for remote command execution -3. WinRM settings for PowerShell remoting into cluster nodes -4. Network topology for network-aware testing (iPerf) -5. Empty array for solutions not yet deployed - -### File Naming - -- Use descriptive names: `prod-seattle-hci01.yml`, `lab-east-cluster.yml` -- The `example-cluster.yml` file is a **template** — copy it, never edit it directly - -!!! warning - Cluster config files may contain IP addresses and hostnames. Do **not** commit production cluster configs with real IPs to a public repository. Use `.gitignore` or a separate private config repo. - -## Workload Profiles - -### Location - -`config/profiles//.yml` - -### Required Structure - -```yaml -profile: - name: "Peak IOPS" # <1> - description: "100% random 4K reads for maximum IOPS measurement" - category: "iops" # <2> - - parameters: # <3> - block_size: "4k" - write_ratio: 0 - random_ratio: 100 - outstanding_io: 32 - threads_per_vm: 4 - duration_seconds: 300 - warmup_seconds: 60 - - expected_thresholds: # <4> - min_iops_per_node: 50000 - max_latency_ms: 10 - min_throughput_mbps: 200 -``` - -1. Human-readable profile name — shown in reports -2. Category for grouping (`iops`, `throughput`, `latency`, `mixed`, `oltp`, `olap`) -3. Parameters that override solution config defaults when this profile is selected -4. Optional thresholds for pass/fail reporting - -### Profile Naming Convention - -| Profile | Purpose | -| --- | --- | -| `general.yml` | Balanced mixed workload — default if no profile specified | -| `peak-iops.yml` | Maximum IOPS (small random reads) | -| `sql-oltp.yml` | Simulates SQL Server OLTP patterns | -| `vdi.yml` | Simulates VDI (Virtual Desktop Infrastructure) patterns | - -!!! tip - Create new profiles rather than modifying existing ones. Profiles are meant to be reusable presets. - -## Credential Configuration - -### Location - -`config/credentials/keyvault-config.yml` - -### Structure - -```yaml -keyvault: - name: "" # <1> - resource_group: "" - subscription_id: "" - tenant_id: "" - - auth_method: "az_cli" # <2> - - service_principal: # <3> - client_id: "" - client_secret_env_var: "AZURE_CLIENT_SECRET" - tenant_id: "" - - secrets: # <4> - cluster_admin_username: "hci-cluster-admin-user" - cluster_admin_password: "hci-cluster-admin-pwd" - vmfleet_admin_username: "vmfleet-admin-user" - vmfleet_admin_password: "vmfleet-admin-pwd" -``` - -1. Key Vault identity — fill in when configuring for your environment -2. Authentication method: `az_cli`, `managed_identity`, or `service_principal` -3. Only needed for `service_principal` auth — note the env var reference for the secret -4. Maps logical names (used in scripts) to actual Key Vault secret names - -### Rules - -| Rule | Description | -| --- | --- | -| No plaintext secrets | The `secrets` block maps **names only** — never actual values | -| Environment variables | Service principal secrets reference env vars, not literal values | -| Auth method | Use `az_cli` for local development, `managed_identity` for CI/CD | -| Key Vault naming | Secret names in Key Vault should use `kebab-case` | - -!!! important - The `keyvault-config.yml` file itself is safe to commit — it contains only name mappings. The actual secrets live exclusively in Azure Key Vault. - -## Schema Validation - -### Master Config Schema - -The file `config/variables/schema.json` validates `master-environment.yml`: - -| Validation | Rule | -| --- | --- | -| Variable name format | `^[a-z][a-z0-9_]*$` (snake_case only) | -| Variable type | One of: `string`, `integer`, `boolean`, `array` | -| Solutions array | At least one solution from the allowed set | -| Category values | Must be from the defined set (cluster, deployment, testing, etc.) | -| Required fields | `name`, `value`, `type`, `required`, `description`, `solutions` | -| No extra fields | `additionalProperties: false` — prevents typos | - -### Running Validation - -```powershell -# Validate master config structure -$config = Import-MasterConfig -Force - -# Export and validate all solution configs -foreach ($solution in @('vmfleet', 'fio', 'iperf', 'hammerdb', 'stress-ng')) { - Export-SolutionConfig -Solution $solution -} -``` - -!!! note - The CI pipeline (`validate-config.yml`) runs schema validation automatically on every PR that touches files under `config/`. - -### Adding New Variables - -When adding a new variable, follow this checklist: - -1. Add the variable to `master-environment.yml` with all required fields -2. Update `schema.json` if adding a new category or solution -3. Regenerate the affected solution JSON configs -4. Update any scripts that should consume the new variable -5. Add the variable to the documentation appendix (Appendix A — Variable Reference) -6. Increment the metadata `version` and update `last_modified` +See also: [Variable Reference](../reference/variables.md) for this repo's specific variables. diff --git a/mkdocs.yml b/mkdocs.yml index 3b87968..f1f2fe5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -56,7 +56,7 @@ nav: - CI/CD Pipelines: operations/ci-cd.md - Credential Management: operations/credential-management.md - Reference: - - Variable Reference: reference/variable-reference.md + - Variable Reference: reference/variables.md - Cmdlet Reference: reference/cmdlet-reference.md - Glossary: reference/glossary.md - Standards: