Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 22 additions & 44 deletions .github/workflows/validate-config.yml
Original file line number Diff line number Diff line change
@@ -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.
Comment on lines +4 to +5
# =============================================================================

name: Validate Configuration
Expand All @@ -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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
136 changes: 136 additions & 0 deletions config/schema/variables.schema.json
Original file line number Diff line number Diff line change
@@ -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
}
133 changes: 133 additions & 0 deletions config/variables.example.yml
Original file line number Diff line number Diff line change
@@ -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://<vault-name>/<secret-name>
# 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/<tool>/
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"
Loading
Loading