Skip to content

Commit 9ee283c

Browse files
Merge pull request #7 from AzureLocal/feature/issue-14-unified-standards
[INFRA] Establish unified project-wide standards (issue #14)
2 parents 2c9323e + b51def5 commit 9ee283c

14 files changed

Lines changed: 657 additions & 1808 deletions

.github/pull_request_template.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,11 @@ Closes #<!-- issue number -->
2626
- [ ] I have added an entry to `CHANGELOG.md` under `[Unreleased]`
2727
- [ ] My code follows the project's style guidelines in `CONTRIBUTING.md`
2828
- [ ] My commit messages follow [Conventional Commits](https://www.conventionalcommits.org/)
29+
30+
## Standards Compliance
31+
32+
- [ ] Follows [repo structure standard](https://azurelocal.cloud/standards/repo-structure) (required files present)
33+
- [ ] Follows [naming conventions](https://azurelocal.cloud/standards/documentation/naming-conventions) (files, variables, resources)
34+
- [ ] Uses [IIC fictional company](https://azurelocal.cloud/standards/fictional-company-policy) in all examples (never Contoso)
35+
- [ ] Config changes validated against JSON Schema (if applicable)
36+
- [ ] No hardcoded IPs, names, secrets, or environment-specific values in committed code
Lines changed: 22 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# =============================================================================
2-
# validate-config.yml - Validate configuration files
2+
# validate-config.yml Validate config/variables.example.yml against schema
33
# =============================================================================
4-
# Validates YAML configs against JSON Schema and checks for syntax errors.
4+
# Triggered on PRs and pushes that touch config/ or this workflow.
5+
# Validates YAML syntax and JSON Schema compliance.
56
# =============================================================================
67

78
name: Validate Configuration
@@ -22,61 +23,38 @@ permissions:
2223
contents: read
2324

2425
jobs:
25-
validate-config:
26+
validate:
2627
runs-on: ubuntu-latest
2728

2829
steps:
2930
- name: Checkout repository
3031
uses: actions/checkout@v4
3132

32-
- name: Setup Node.js (for ajv-cli)
33-
uses: actions/setup-node@v4
33+
- name: Setup Python
34+
uses: actions/setup-python@v5
3435
with:
35-
node-version: '20'
36+
python-version: '3.12'
3637

37-
- name: Install validation tools
38-
run: |
39-
npm install -g ajv-cli yaml-lint
40-
41-
- name: Lint YAML files
42-
run: |
43-
find config -name '*.yml' -o -name '*.yaml' | while read f; do
44-
echo "Validating YAML syntax: $f"
45-
yamllint "$f" || echo "WARN: $f has lint issues"
46-
done
38+
- name: Install dependencies
39+
run: pip install pyyaml jsonschema
4740

48-
- name: Validate JSON files
41+
- name: Validate variables.example.yml against schema
4942
run: |
50-
find config -name '*.json' | while read f; do
51-
echo "Validating JSON syntax: $f"
52-
python3 -m json.tool "$f" > /dev/null
53-
done
54-
55-
- name: Validate master config against schema
56-
run: |
57-
# Convert YAML to JSON for schema validation
58-
pip install pyyaml
5943
python3 -c "
6044
import yaml, json, sys
61-
with open('config/variables/master-environment.yml') as f:
45+
from jsonschema import validate, ValidationError
46+
47+
with open('config/variables.example.yml') as f:
6248
data = yaml.safe_load(f)
63-
json.dump(data, sys.stdout)
64-
" > /tmp/master-config.json
6549
66-
ajv validate \
67-
-s config/variables/schema.json \
68-
-d /tmp/master-config.json \
69-
|| echo "Schema validation completed with issues"
50+
with open('config/schema/variables.schema.json') as f:
51+
schema = json.load(f)
7052
71-
- name: Validate solution configs
72-
run: |
73-
for f in config/variables/solutions/*.json; do
74-
echo "Checking solution config: $f"
75-
python3 -c "
76-
import json
77-
with open('$f') as fh:
78-
data = json.load(fh)
79-
assert '_metadata' in data, 'Missing _metadata section'
80-
print(f\" Solution: {data['_metadata']['solution']} - OK\")
53+
try:
54+
validate(instance=data, schema=schema)
55+
print('✅ config/variables.example.yml passes schema validation')
56+
except ValidationError as e:
57+
print(f'❌ Schema validation failed: {e.message}')
58+
print(f' Path: {\" > \".join(str(p) for p in e.absolute_path)}')
59+
sys.exit(1)
8160
"
82-
done
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
name: Validate Repo Structure
2+
on:
3+
pull_request:
4+
branches: [main]
5+
6+
jobs:
7+
check-structure:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/checkout@v4
11+
12+
- name: Check required root files
13+
run: |
14+
missing=0
15+
for f in README.md CONTRIBUTING.md LICENSE CHANGELOG.md .gitignore; do
16+
if [ ! -f "$f" ]; then
17+
echo "::error::Missing required file: $f"
18+
missing=$((missing + 1))
19+
fi
20+
done
21+
if [ $missing -gt 0 ]; then
22+
echo "::error::$missing required root file(s) missing"
23+
exit 1
24+
fi
25+
echo "All required root files present"
26+
27+
- name: Check required directories
28+
run: |
29+
missing=0
30+
for d in docs .github; do
31+
if [ ! -d "$d" ]; then
32+
echo "::error::Missing required directory: $d/"
33+
missing=$((missing + 1))
34+
fi
35+
done
36+
if [ $missing -gt 0 ]; then
37+
echo "::error::$missing required directory(s) missing"
38+
exit 1
39+
fi
40+
echo "All required directories present"
41+
42+
- name: Check PR template
43+
run: |
44+
if [ ! -f ".github/PULL_REQUEST_TEMPLATE.md" ]; then
45+
echo "::error::Missing .github/PULL_REQUEST_TEMPLATE.md"
46+
exit 1
47+
fi
48+
echo "PR template found"
49+
50+
- name: Check config structure (if config dir exists)
51+
run: |
52+
if [ -d "config" ]; then
53+
missing=0
54+
if [ ! -f "config/variables.example.yml" ]; then
55+
echo "::error::Missing config/variables.example.yml"
56+
missing=$((missing + 1))
57+
fi
58+
if [ ! -f "config/schema/variables.schema.json" ]; then
59+
echo "::error::Missing config/schema/variables.schema.json"
60+
missing=$((missing + 1))
61+
fi
62+
if [ $missing -gt 0 ]; then
63+
exit 1
64+
fi
65+
echo "Config structure valid"
66+
else
67+
echo "No config/ directory — skipping config checks"
68+
fi
69+
70+
- name: Check variable reference doc (if config dir exists)
71+
run: |
72+
if [ -d "config" ]; then
73+
if [ ! -f "docs/reference/variables.md" ]; then
74+
echo "::error::Missing docs/reference/variables.md (required when config/ exists)"
75+
exit 1
76+
fi
77+
echo "Variable reference doc found"
78+
else
79+
echo "No config/ directory — skipping variable reference check"
80+
fi

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ config/credentials/*.key
3333
config/credentials/*.pem
3434
config/credentials/.env
3535

36+
# User-specific config (actual values — never commit)
37+
config/variables.yml
38+
3639
# Generated solution config files (built from master-environment.yml)
3740
# Uncomment the next line if you want these regenerated every time:
3841
# config/variables/solutions/*.json

CONTRIBUTING.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,16 @@ Examples:
7474
- Include performance baselines or comparative results when relevant
7575
- Describe your test environment and results in the PR
7676

77+
## Standards
78+
79+
This project follows the **org-wide AzureLocal standards** documented at [azurelocal.cloud/standards](https://azurelocal.cloud/standards/). Key references:
80+
81+
- [Repository Structure](https://azurelocal.cloud/standards/repo-structure) — Required files, directories, labels, branch naming
82+
- [Scripting Standards](https://azurelocal.cloud/standards/scripting/scripting-standards) — PowerShell conventions
83+
- [Documentation Standards](https://azurelocal.cloud/standards/documentation/documentation-standards) — Writing and formatting
84+
- [Variable Management](https://azurelocal.cloud/docs/implementation/04-variable-management-standard) — Config file patterns
85+
- [Fictional Company Policy](https://azurelocal.cloud/standards/fictional-company-policy) — Use IIC, never Contoso
86+
7787
## Code of Conduct
7888

7989
Be respectful and constructive. Keep discussions on-topic and collaborative.
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"$id": "https://github.com/AzureLocal/azurelocal-loadtools/config/schema/variables.schema.json",
4+
"title": "Load Tools Variables",
5+
"description": "Schema for config/variables.example.yml — validates required sections and key structure.",
6+
"type": "object",
7+
"required": ["azure", "keyvault", "azure_local", "storage", "credentials", "testing", "tags"],
8+
"properties": {
9+
"azure": {
10+
"type": "object",
11+
"required": ["subscription_id", "tenant_id", "resource_group", "location"],
12+
"properties": {
13+
"subscription_id": { "type": "string" },
14+
"tenant_id": { "type": "string" },
15+
"resource_group": { "type": "string" },
16+
"location": { "type": "string" }
17+
}
18+
},
19+
"keyvault": {
20+
"type": "object",
21+
"required": ["name"],
22+
"properties": {
23+
"name": { "type": "string" },
24+
"auth_method": { "type": "string", "enum": ["managed_identity", "service_principal", "az_cli"] }
25+
}
26+
},
27+
"azure_local": {
28+
"type": "object",
29+
"required": ["cluster_name", "nodes"],
30+
"properties": {
31+
"cluster_name": { "type": "string" },
32+
"cluster_domain": { "type": "string" },
33+
"nodes": {
34+
"type": "array",
35+
"minItems": 1,
36+
"items": {
37+
"type": "object",
38+
"required": ["name", "management_ip"],
39+
"properties": {
40+
"name": { "type": "string" },
41+
"management_ip": { "type": "string" },
42+
"storage_ip": { "type": "string" }
43+
}
44+
}
45+
}
46+
}
47+
},
48+
"storage": {
49+
"type": "object",
50+
"required": ["csv_path"],
51+
"properties": {
52+
"csv_path": { "type": "string" },
53+
"collect_volume_path": { "type": "string" },
54+
"base_vhd_path": { "type": "string" }
55+
}
56+
},
57+
"networking": {
58+
"type": "object",
59+
"properties": {
60+
"management": {
61+
"type": "object",
62+
"properties": {
63+
"subnet": { "type": "string" },
64+
"vlan_id": { "type": "integer" }
65+
}
66+
},
67+
"storage": {
68+
"type": "object",
69+
"properties": {
70+
"subnet": { "type": "string" },
71+
"vlan_id": { "type": "integer" },
72+
"rdma_enabled": { "type": "boolean" }
73+
}
74+
},
75+
"compute": {
76+
"type": "object",
77+
"properties": {
78+
"subnet": { "type": "string" },
79+
"vlan_id": { "type": "integer" }
80+
}
81+
}
82+
}
83+
},
84+
"credentials": {
85+
"type": "object",
86+
"required": ["cluster_admin_username", "cluster_admin_password"],
87+
"properties": {
88+
"cluster_admin_username": { "type": "string" },
89+
"cluster_admin_password": { "type": "string" },
90+
"vmfleet_admin_username": { "type": "string" },
91+
"vmfleet_admin_password": { "type": "string" }
92+
}
93+
},
94+
"testing": {
95+
"type": "object",
96+
"required": ["default_tool"],
97+
"properties": {
98+
"default_tool": { "type": "string", "enum": ["vmfleet", "fio", "iperf", "hammerdb", "stress-ng"] },
99+
"default_profile": { "type": "string" },
100+
"duration_seconds": { "type": "integer", "minimum": 1 },
101+
"warmup_seconds": { "type": "integer", "minimum": 0 }
102+
}
103+
},
104+
"monitoring": {
105+
"type": "object",
106+
"properties": {
107+
"log_analytics_workspace_id": { "type": "string" },
108+
"log_analytics_shared_key": { "type": "string" },
109+
"enable_real_time": { "type": "boolean" },
110+
"collection_interval_seconds": { "type": "integer", "minimum": 1 }
111+
}
112+
},
113+
"reporting": {
114+
"type": "object",
115+
"properties": {
116+
"output_dir": { "type": "string" },
117+
"format": { "type": "string", "enum": ["html", "json", "csv"] },
118+
"include_charts": { "type": "boolean" }
119+
}
120+
},
121+
"winrm": {
122+
"type": "object",
123+
"properties": {
124+
"port": { "type": "integer" },
125+
"use_ssl": { "type": "boolean" },
126+
"authentication": { "type": "string", "enum": ["Kerberos", "Negotiate", "Basic"] },
127+
"operation_timeout_seconds": { "type": "integer", "minimum": 1 }
128+
}
129+
},
130+
"tags": {
131+
"type": "object",
132+
"additionalProperties": { "type": "string" }
133+
}
134+
},
135+
"additionalProperties": false
136+
}

0 commit comments

Comments
 (0)