Skip to content
Merged
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
8 changes: 8 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,11 @@ Closes #<!-- issue number -->
- [ ] 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
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.
# =============================================================================

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
80 changes: 80 additions & 0 deletions .github/workflows/validate-repo-structure.yml
Original file line number Diff line number Diff line change
@@ -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"
Comment on lines +44 to +45
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"
Comment on lines +54 to +59
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)"
Comment on lines +73 to +74
exit 1
fi
echo "Variable reference doc found"
else
echo "No config/ directory — skipping variable reference check"
fi
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
10 changes: 10 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
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
}
Loading
Loading