Skip to content
Open
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
18 changes: 11 additions & 7 deletions .agents/skills/e2e-test/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,19 +135,19 @@ locals {
}
```

- Create a `meshstack_building_block_v2` resource that exercises the building block end-to-end.
- Create a `meshstack_building_block` resource that exercises the building block end-to-end.
Reference `test_context` directly (it is non-null in both modes). The provider's
`building_block_definition_version_ref` takes `{ uuid }` only — extract it explicitly.

- **Always add `depends_on = [module.<integration_module>]`** to `meshstack_building_block_v2.this`.
- **Always add `depends_on = [module.<integration_module>]`** to `meshstack_building_block.this`.
WIF federated identity providers have no Terraform dependents (nothing references their outputs),
so OpenTofu schedules their destruction in parallel with the BB delete run. This causes the BB
delete run to fail with 401s because the cloud WIF trust is already gone before the delete run
can authenticate. The explicit `depends_on` forces the BB resource (including its delete run) to
be fully destroyed before any backplane resources are torn down.

```hcl
resource "meshstack_building_block_v2" "this" {
resource "meshstack_building_block" "this" {
depends_on = [module.<integration_module>] # prevents teardown race with WIF providers
wait_for_completion = true
spec = {
Expand All @@ -158,6 +158,9 @@ resource "meshstack_building_block_v2" "this" {
kind = "meshWorkspace"
name = var.test_context.workspace
}
# inputs: one `value = jsonencode(...)` per input (jsonencode strings too, e.g.
# jsonencode("x"), jsonencode(1), jsonencode(true)). Sensitive inputs instead use
# `sensitive = { secret_value = ... }`.
inputs = { ... }
}
}
Expand Down Expand Up @@ -188,7 +191,8 @@ target_ref = {
- Name the file `<cloud>_<service>_hub.tftest.hcl` (e.g. `building_block_noop_hub.tftest.hcl`).
- Always assert `status.status == "SUCCEEDED"` as the first check.
- Assert meaningful output values (URLs, strings, booleans) to validate the building block executed
correctly.
correctly. Every output `value` is a `jsonencode`d string — read it with
`jsondecode(<res>.status.outputs["<name>"].value)` (a CODE/JSON output decodes twice).
- The same test file runs in **both** invocation modes (smoke-test via `tofu test`, foundation via
`terragrunt test`). Reference **`output.<name>`**, never `var.test_context.*` — `test_context` is
null in foundation mode and would crash the assertion.
Expand Down Expand Up @@ -255,7 +259,7 @@ node tools/debug/get-bb-run-logs.mjs "$BB_UUID"

To get the UUID from an errored test state:
```bash
tofu state show -state=errored_test.tfstate 'meshstack_building_block_v2.this' | grep uuid
tofu state show -state=errored_test.tfstate 'meshstack_building_block.this' | grep uuid
```


Expand Down Expand Up @@ -318,6 +322,6 @@ source setup-override-provider.sh
- [ ] `hub.git_ref = var.test_context.hub_git_ref` — no hardcoded `"main"`
- [ ] Version ref resolved in a `local` (`bbd_version_ref` in foundation mode, else the built module)
- [ ] `building_block_definition_version_ref = { uuid = local.version_ref.uuid }` — provider only accepts `{ uuid }`, extract it explicitly
- [ ] `meshstack_building_block_v2` has `depends_on = [module.<integration_module>]` to prevent WIF teardown race (delete run must finish before backplane resources are destroyed)
- [ ] `meshstack_building_block_v2` has `wait_for_completion = true`
- [ ] `meshstack_building_block` has `depends_on = [module.<integration_module>]` to prevent WIF teardown race (delete run must finish before backplane resources are destroyed)
- [ ] `meshstack_building_block` has `wait_for_completion = true`
- [ ] tftest asserts `status.status == "SUCCEEDED"` and key outputs (references `var.test_context.*` directly — non-null in both modes)
8 changes: 4 additions & 4 deletions modules/aks/starterkit/buildingblock/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ This documentation is intended as a reference documentation for cloud foundation

| Name | Version |
|------|---------|
| <a name="requirement_meshstack"></a> [meshstack](#requirement\_meshstack) | >= 0.21.0 |
| <a name="requirement_meshstack"></a> [meshstack](#requirement\_meshstack) | >= 0.23.0 |

## Modules

Expand All @@ -25,9 +25,9 @@ No modules.

| Name | Type |
|------|------|
| [meshstack_building_block_v2.github_actions_dev](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/building_block_v2) | resource |
| [meshstack_building_block_v2.github_actions_prod](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/building_block_v2) | resource |
| [meshstack_building_block_v2.repo](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/building_block_v2) | resource |
| [meshstack_building_block.github_actions_dev](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/building_block) | resource |
| [meshstack_building_block.github_actions_prod](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/building_block) | resource |
| [meshstack_building_block.repo](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/building_block) | resource |
| [meshstack_project.dev](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/project) | resource |
| [meshstack_project.prod](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/project) | resource |
| [meshstack_project_user_binding.creator_dev_admin](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/project_user_binding) | resource |
Expand Down
54 changes: 36 additions & 18 deletions modules/aks/starterkit/buildingblock/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ resource "meshstack_tenant_v4" "prod" {
depends_on = [meshstack_project_user_binding.creator_prod_admin]
}

resource "meshstack_building_block_v2" "repo" {
resource "meshstack_building_block" "repo" {
spec = {
building_block_definition_version_ref = {
uuid = var.github_repo_definition_version_uuid
Expand All @@ -117,31 +117,31 @@ resource "meshstack_building_block_v2" "repo" {

inputs = {
repo_name = {
value_string = local.repo_name
value = jsonencode(local.repo_name)
}
archive_repo_on_destroy = {
value_bool = var.archive_repo_on_destroy
value = jsonencode(var.archive_repo_on_destroy)
}
repo_owner = {
value_string = var.repo_admin != null ? var.repo_admin : "null"
value = jsonencode(var.repo_admin != null ? var.repo_admin : "null")
}
repo_visibility = {
value_string = var.github_repo_input_repo_visibility != null ? var.github_repo_input_repo_visibility : "private"
value = jsonencode(var.github_repo_input_repo_visibility != null ? var.github_repo_input_repo_visibility : "private")
}
use_template = {
value_bool = true
value = jsonencode(true)
}
template_owner = {
value_string = split("/", var.github_template_repo_path)[0]
value = jsonencode(split("/", var.github_template_repo_path)[0])
}
template_repo = {
value_string = split("/", var.github_template_repo_path)[1]
value = jsonencode(split("/", var.github_template_repo_path)[1])
}
}
}
}

resource "meshstack_building_block_v2" "github_actions_dev" {
resource "meshstack_building_block" "github_actions_dev" {
spec = {
building_block_definition_version_ref = {
uuid = var.github_actions_connector_definition_version_uuid
Expand All @@ -152,24 +152,24 @@ resource "meshstack_building_block_v2" "github_actions_dev" {
}
display_name = "GHA Connector Dev"
parent_building_blocks = [{
buildingblock_uuid = meshstack_building_block_v2.repo.metadata.uuid
buildingblock_uuid = meshstack_building_block.repo.metadata.uuid
definition_uuid = var.github_repo_definition_uuid
}]
inputs = {
github_environment_name = {
value_string = "development"
value = jsonencode("development")
}
additional_environment_variables = {
value_code = jsonencode({
value = jsonencode(jsonencode({
"DOMAIN_NAME" = "${local.identifier}-dev"
"AKS_NAMESPACE_NAME" = meshstack_tenant_v4.dev.spec.platform_tenant_id
})
}))
}
}
}
}

resource "meshstack_building_block_v2" "github_actions_prod" {
resource "meshstack_building_block" "github_actions_prod" {
spec = {
display_name = "GHA Connector Prod"
building_block_definition_version_ref = {
Expand All @@ -180,19 +180,37 @@ resource "meshstack_building_block_v2" "github_actions_prod" {
uuid = meshstack_tenant_v4.prod.metadata.uuid
}
parent_building_blocks = [{
buildingblock_uuid = meshstack_building_block_v2.repo.metadata.uuid
buildingblock_uuid = meshstack_building_block.repo.metadata.uuid
definition_uuid = var.github_repo_definition_uuid
}]
inputs = {
github_environment_name = {
value_string = "production"
value = jsonencode("production")
}
additional_environment_variables = {
value_code = jsonencode({
value = jsonencode(jsonencode({
"DOMAIN_NAME" = local.identifier
"AKS_NAMESPACE_NAME" = meshstack_tenant_v4.prod.spec.platform_tenant_id
})
}))
}
}
}
}

# Migrate the app-team-managed child building blocks from the deprecated
# meshstack_building_block_v2 resource to meshstack_building_block in place —
# no destroy/recreate of the live blocks.
moved {
from = meshstack_building_block_v2.repo
to = meshstack_building_block.repo
}

moved {
from = meshstack_building_block_v2.github_actions_dev
to = meshstack_building_block.github_actions_dev
}

moved {
from = meshstack_building_block_v2.github_actions_prod
to = meshstack_building_block.github_actions_prod
}
12 changes: 6 additions & 6 deletions modules/aks/starterkit/buildingblock/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ output "prod-link" {

output "github_repo_url" {
description = "URL of the created GitHub repository"
value = meshstack_building_block_v2.repo.status.outputs.repo_html_url.value_string
value = jsondecode(meshstack_building_block.repo.status.outputs.repo_html_url.value)
}

output "summary" {
Expand All @@ -23,15 +23,15 @@ output "summary" {

This starter kit has set up the following resources in workspace `${var.workspace_identifier}`:

@buildingblock[${meshstack_building_block_v2.repo.metadata.uuid}]
@buildingblock[${meshstack_building_block.repo.metadata.uuid}]

@project[${meshstack_project.dev.metadata.owned_by_workspace}.${meshstack_project.dev.metadata.name}]\
&nbsp;&nbsp;&nbsp;&nbsp;@tenant[${meshstack_tenant_v4.dev.metadata.uuid}]\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@buildingblock[${meshstack_building_block_v2.github_actions_dev.metadata.uuid}]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@buildingblock[${meshstack_building_block.github_actions_dev.metadata.uuid}]

@project[${meshstack_project.prod.metadata.owned_by_workspace}.${meshstack_project.prod.metadata.name}]\
&nbsp;&nbsp;&nbsp;&nbsp;@tenant[${meshstack_tenant_v4.prod.metadata.uuid}]\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@buildingblock[${meshstack_building_block_v2.github_actions_prod.metadata.uuid}]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@buildingblock[${meshstack_building_block.github_actions_prod.metadata.uuid}]

---

Expand All @@ -52,7 +52,7 @@ Trigger a deployment by:
- Pushing to the **main** branch (deploys to **dev**)
- Merging **main** into **release** via PR (deploys to **prod**)

View deployment status: [GitHub Actions](${meshstack_building_block_v2.repo.status.outputs.repo_html_url.value_string}/actions/workflows/k8s-deploy.yml)
View deployment status: [GitHub Actions](${jsondecode(meshstack_building_block.repo.status.outputs.repo_html_url.value)}/actions/workflows/k8s-deploy.yml)

- **Dev**: [${local.identifier}-dev.${var.apps_base_domain}](https://${local.identifier}-dev.${var.apps_base_domain})
- **Prod**: [${local.identifier}.${var.apps_base_domain}](https://${local.identifier}.${var.apps_base_domain})
Expand All @@ -66,7 +66,7 @@ View deployment status: [GitHub Actions](${meshstack_building_block_v2.repo.stat
- Merge PR from **main → release** → deploys to **prod**

### 2. Monitor
- Check workflow status in the [Actions tab](<${meshstack_building_block_v2.repo.status.outputs.repo_html_url.value_string}/actions>)
- Check workflow status in the [Actions tab](<${jsondecode(meshstack_building_block.repo.status.outputs.repo_html_url.value)}/actions>)

### 3. Access AKS Namespaces
- [Dev Namespace](/#/w/${var.workspace_identifier}/p/${meshstack_project.dev.metadata.name}/i/${meshstack_tenant_v4.dev.spec.platform_identifier}/overview/azure_kubernetes_service)
Expand Down
2 changes: 1 addition & 1 deletion modules/aks/starterkit/buildingblock/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ terraform {
required_providers {
meshstack = {
source = "meshcloud/meshstack"
version = ">= 0.21.0"
version = ">= 0.23.0"
}
}
}
9 changes: 5 additions & 4 deletions modules/aws/agentic-coding-sandbox/buildingblock/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ End users provide:

| Name | Version |
|------|---------|
| <a name="requirement_meshstack"></a> [meshstack](#requirement\_meshstack) | >= 0.7.1 |
| <a name="requirement_meshstack"></a> [meshstack](#requirement\_meshstack) | >= 0.23.0 |

## Modules

Expand All @@ -87,11 +87,12 @@ No modules.

| Name | Type |
|------|------|
| [meshstack_buildingblock.budget_alert](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/buildingblock) | resource |
| [meshstack_buildingblock.enable_eu_south_2_region](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/buildingblock) | resource |
| [meshstack_building_block.budget_alert](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/building_block) | resource |
| [meshstack_building_block.enable_eu_south_2_region](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/building_block) | resource |
| [meshstack_project.sandbox](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/project) | resource |
| [meshstack_tenant.sandbox](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/tenant) | resource |
| [meshstack_tenant_v4.sandbox](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/tenant_v4) | resource |
| [random_string.suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource |
| [meshstack_building_block_definitions.all](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/data-sources/building_block_definitions) | data source |

## Inputs

Expand Down
67 changes: 44 additions & 23 deletions modules/aws/agentic-coding-sandbox/buildingblock/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -64,64 +64,85 @@ resource "meshstack_project" "sandbox" {
}


resource "meshstack_tenant" "sandbox" {
resource "meshstack_tenant_v4" "sandbox" {
metadata = {
owned_by_workspace = meshstack_project.sandbox.metadata.owned_by_workspace
owned_by_project = meshstack_project.sandbox.metadata.name
platform_identifier = local.platform_identifier
owned_by_workspace = meshstack_project.sandbox.metadata.owned_by_workspace
owned_by_project = meshstack_project.sandbox.metadata.name
}

spec = {
platform_identifier = local.platform_identifier
landing_zone_identifier = local.landing_zone_identifier
}
}

# The meshstack_building_block resource references a definition by its version uuid, but the
# composition config provides the definition uuid + a version number. Resolve the version uuid via
# the definitions data source.
data "meshstack_building_block_definitions" "all" {}

# NOTE: must use bb v1 resource because v2 requires a tenant uuid
# but the tenant v4 api that delivers the uuid is not supported by our terraform provider yet
resource "meshstack_buildingblock" "budget_alert" {
metadata = {
definition_uuid = local.budget_alert_definition_uuid
definition_version = local.budget_alert_definition_version
tenant_identifier = "${meshstack_tenant.sandbox.metadata.owned_by_workspace}.${meshstack_tenant.sandbox.metadata.owned_by_project}.${meshstack_tenant.sandbox.metadata.platform_identifier}"
}
locals {
_bbds = data.meshstack_building_block_definitions.all.building_block_definitions

budget_alert_version_uuid = one(flatten([
for d in local._bbds : [
for v in d.versions : v.uuid if tostring(v.number) == tostring(local.budget_alert_definition_version)
] if d.metadata.uuid == local.budget_alert_definition_uuid
]))

enable_eu_south_2_region_version_uuid = one(flatten([
for d in local._bbds : [
for v in d.versions : v.uuid if tostring(v.number) == tostring(local.enable_eu_south_2_region_definition_version)
] if d.metadata.uuid == local.enable_eu_south_2_region_definition_uuid
]))
}


resource "meshstack_building_block" "budget_alert" {
spec = {
building_block_definition_version_ref = {
uuid = local.budget_alert_version_uuid
}

display_name = "Budget Alert"
target_ref = meshstack_tenant_v4.sandbox.ref

inputs = {
budget_name = {
value_string = "Agentic Coding Budget Alert"
value = jsonencode("Agentic Coding Budget Alert")
}
monthly_budget_amount = {
value_int = var.budget_amount
value = jsonencode(var.budget_amount)
}
contact_emails = {
# just a single email for now, not a comma-separated list
value_string = var.username
value = jsonencode(var.username)
}
}
}

wait_for_completion = true
}


# enable spain region for the sandbox tenant because that's the only region where Anthropic's Sonnet 4 is available
resource "meshstack_buildingblock" "enable_eu_south_2_region" {
metadata = {
definition_uuid = local.enable_eu_south_2_region_definition_uuid
definition_version = local.enable_eu_south_2_region_definition_version
tenant_identifier = "${meshstack_tenant.sandbox.metadata.owned_by_workspace}.${meshstack_tenant.sandbox.metadata.owned_by_project}.${meshstack_tenant.sandbox.metadata.platform_identifier}"
}

resource "meshstack_building_block" "enable_eu_south_2_region" {
spec = {
building_block_definition_version_ref = {
uuid = local.enable_eu_south_2_region_version_uuid
}

display_name = "Enable eu-south-2 region"
target_ref = meshstack_tenant_v4.sandbox.ref

inputs = {
region = {
value_single_select = "eu-south-2"
value = jsonencode("eu-south-2")
}
}
}

wait_for_completion = true
}

# note: this does not work until API keys get support for an ADM level permission on project role bidnings
Expand Down
Loading
Loading