template-tags-hierarchy · git:20260308.e35ea0e · 2026-03-08 · sha256 ef8b4ae31a92903b
template-tags-hierarchy git:20260308.e35ea0eA
Immutable. This exact content never changes and is served at /api/v1/blob/ef8b4ae31a92903b.
---
description: Conventions for platform tag and hierarchy Jinja templates (src/templates/*-tags.yaml, *-hierarchy.yaml)
globs: src/templates/*-tags.yaml,src/templates/*-hierarchy.yaml
alwaysApply: false
---
# Tag & Hierarchy Template Contract
All platform templates (`gcp`, `aws`, `azure`, `kubernetes`) follow the same
structural rules. When adding a new provider or modifying an existing one,
enforce every rule below.
## Hierarchy template (*-hierarchy.yaml)
The hierarchy defines the resourcePath segments. Every entry MUST have a
corresponding tag of the same name in the tags template.
### Leaf entry rule
```jinja
{% if qualifiers and qualifiers.resource is defined %}
- resource_name
{% elif match_resource.resource_type is defined %}
- resource_type
{% endif %}
```
- **Resource-scoped** (qualifiers include `resource`): leaf is `resource_name`.
- **Group-scoped** (qualifiers do NOT include `resource`): leaf is `resource_type`.
- NEVER fall back to `resource_name` via `match_resource.name` for group-scoped
SLXs — that picks one arbitrary child and produces a misleading path.
## Tags template (*-tags.yaml)
### Required tags (all platforms)
Every tags template MUST emit these tags when values are available:
| Tag | When emitted |
|-----|-------------|
| `platform` | Always |
| `resource_type` | Always (from `match_resource.resource_type.name`; for K8s CRDs use `kind \| lower`) |
| `resource_name` | Always (from qualifiers or `match_resource.name` fallback) |
| `child_resource` | For each entry in `child_resource_names`, unless it equals `resource_name` |
### resource_name emission rule
Always emit `resource_name` as a tag. Prefer qualifier values, fall back to
`match_resource.name`:
```jinja
{% if qualifiers and qualifiers.resource is defined %}
- name: resource_name
value: '{{ qualifiers.resource | string }}'
{% elif qualifiers and qualifiers.resource_group is defined %}
- name: resource_name
value: '{{ qualifiers.resource_group | string }}'
{% elif match_resource.name is defined %}
- name: resource_name
value: '{{ match_resource.name | string }}'
{% endif %}
```
The `match_resource.name` fallback ensures group-scoped SLXs still have a
representative resource_name tag. The child_resource dedup logic filters
out the matching child so it is not listed twice. The HIERARCHY (not the
tag) controls whether resource_name or resource_type appears in the path.
### child_resource deduplication
Always normalize BOTH sides of the comparison identically before comparing:
```jinja
{% set _resource_name_value = (...) | default('') | string | replace(":", "_") | replace("/", "_") %}
{% for cr in child_resource_names | default([]) %}
{% set _cr_value = cr | string | replace(":", "_") | replace("/", "_") %}
{% if _cr_value != _resource_name_value %}
- name: child_resource
value: '{{ _cr_value }}'
{% endif %}
{% endfor %}
```
Both `_resource_name_value` and `_cr_value` MUST apply the same `replace`
filters. Asymmetric normalization defeats deduplication.
### Kubernetes CRD resource_type
For Kubernetes custom resources, `match_resource.resource_type.name` is the
generic `"custom"`. The tags template MUST use `match_resource.kind | lower`
instead to produce a meaningful type (e.g. `kustomization`, `helmrelease`):
```jinja
{% if match_resource.resource_type.name == "custom" and match_resource.kind is defined %}
- name: resource_type
value: '{{ match_resource.kind | lower | string }}'
{% elif match_resource.resource_type %}
- name: resource_type
value: '{{ match_resource.resource_type.name | string }}'
{% endif %}
```
### Kubernetes CRD resource_name
CRD resources are registered with their simple K8s metadata name (e.g.
`online-boutique-prod`), NOT the composite `plural_group_version_name`.
The composite is only used for the internal `qualified_name` registry key.
Do NOT construct or expect composite CRD names in templates.
### Platform-specific tags
Provider-specific tags (e.g. `[aws]key`, `[k8s]label`, `[azure]tag`,
`gcp_label_*`) are appended AFTER the standard tags above.
## resourcePath (computed at render time)
`resourcePath` is NEVER set in templates. It is always derived from
`hierarchy + tags` by `compute_resource_path_from_hierarchy()` in
`src/renderers/render_output_items.py`. Do not add `resourcePath` or
`qualified_name` logic to tag/hierarchy templates.
## Adding a new platform
1. Create `src/templates/<platform>-hierarchy.yaml` following the leaf rule.
2. Create `src/templates/<platform>-tags.yaml` emitting all required tags.
3. Ensure `resource_type` is always emitted as a tag.
4. Include the `child_resource` deduplication loop.
5. Add platform-specific enrichment tags after the standard block.