-
Notifications
You must be signed in to change notification settings - Fork 0
feat(meshstack): add composition building block #247
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
106832b
719f768
f893194
93702c1
8338e15
5843d72
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| --- | ||
| name: Composition Demo | ||
| supportedPlatforms: | ||
| - meshstack | ||
| description: | | ||
| Reference building block demonstrating the composition pattern: it uses the run's | ||
| ephemeral API key to create a building block definition with a building block from it, | ||
| and an empty platform with a landing zone. | ||
| # Creates only meshObjects through the meshStack API, so there is nothing to set up cloud-side. | ||
| requiresBackplane: false | ||
| --- | ||
| # Composition Demo | ||
|
|
||
| This building block is a reference implementation of the **composition** pattern: a building block | ||
| that provisions other meshObjects through the meshStack API instead of cloud resources. It creates: | ||
|
|
||
| - a `meshBuildingBlockDefinition` in the consuming workspace and a `meshBuildingBlock` from it, and | ||
| - an **empty platform** — a `meshPlatformType`, a `meshLocation`, a `meshPlatform` and a | ||
| `meshLandingZone` on it. | ||
|
|
||
| The created definition runs the hub's [`link`](../../link) building block, which provisions nothing | ||
| but a `terraform_data` and needs neither a cloud provider nor an operator. Reusing it keeps the whole | ||
| chain automatic and avoids inventing a throwaway implementation just to have something to create. | ||
| Both created building block objects are named after `link_name` (default `Link`) to keep them | ||
| distinguishable from the `Composition Demo` block that created them. | ||
|
|
||
| *Empty* is the point of the platform: it is a `CUSTOM` platform with no cloud connection config and a | ||
| landing zone whose platform properties are `{}`. meshStack ships no `CUSTOM` platform type and every | ||
| built-in type is a cloud platform whose config would need real credentials, so the module creates a | ||
| type of its own. Nothing about it can be replicated to a cloud — it exists to carry a name, a landing | ||
| zone and its creation provenance. | ||
|
|
||
| ## How it works | ||
|
|
||
| The building block definition declares `permissions`, so meshStack issues an **ephemeral API key** | ||
| scoped to the consuming workspace for the duration of each run and injects it as `MESHSTACK_ENDPOINT` | ||
| / `MESHSTACK_API_TOKEN`. The `meshstack` provider picks those up with no explicit configuration, so | ||
| `provider "meshstack" {}` is all this module needs. | ||
|
|
||
| Because every meshObject is created with that key, meshStack records it as their creation author and | ||
| surfaces **"created by building block"** provenance on each of them, linking back to the composition | ||
| that created them. That makes this module a convenient end-to-end check of that provenance without any | ||
| cloud platform involved. | ||
|
|
||
| ## Permissions | ||
|
|
||
| | Permission | Why | | ||
| |---|---| | ||
| | `BUILDINGBLOCKDEFINITION_LIST` / `_SAVE` / `_DELETE` | Manage the created building block definition | | ||
| | `BUILDINGBLOCK_LIST` / `_SAVE` / `_DELETE` | Manage the created building block | | ||
| | `PLATFORMINSTANCE_LIST` / `_SAVE` / `_DELETE` | Manage the created platform, its location and its platform type | | ||
| | `LANDINGZONE_LIST` / `_SAVE` / `_DELETE` | Manage the created landing zone | | ||
|
|
||
| The created definition itself declares no permissions, so it stays within meshStack's privilege | ||
| escalation guard (a nested definition may only request a subset of its parent's permissions). | ||
|
|
||
| ## Notes | ||
|
|
||
| - This module does not wait for the created building block's run. That run needs a terraform runner, | ||
| and a stack with a single one cannot start it before this run returns. Provenance is recorded when | ||
| the block is created, so nothing here depends on the result. | ||
| - For the same reason the created definition uses `deletion_mode = "PURGE"`. `DELETE` would schedule | ||
| a deprovisioning run on teardown that cannot start until the composition's own destroy run returns. | ||
| Purging leaks nothing, since `link` provisions no infrastructure. | ||
| - `hub_git_ref` is wired in as a static input from the composition's own `var.hub.git_ref`, so the | ||
| created definition clones the `link` module from the same hub revision. | ||
| - The created definition's version stays a **draft**. Releasing needs admin approval, which the run's | ||
| ephemeral key — a plain workspace key — cannot obtain, so `draft = false` would leave the version | ||
| `DRAFT` regardless, warn on every run, and leave `version_latest_release` null. The created | ||
| building block therefore references `version_latest`, which a draft permits because the definition | ||
| and the building block's target are the same workspace. | ||
| - A platform, location and platform type identifier is globally unique and cannot be reused once | ||
| deleted. The module therefore derives all three from `building_block_uuid`, a | ||
| `TENANT_BUILDING_BLOCK_UUID` input carrying the UUID of the building block the run belongs to, so | ||
| each building block gets identifiers of its own that survive re-runs. | ||
| - The created platform is `PRIVATE` and `UNPUBLISHED`, which keeps it out of the marketplace. | ||
| meshStack ties those two together — `UNPUBLISHED` is rejected for any other restriction — and | ||
| `PRIVATE` in turn requires `restricted_to_workspaces` to name exactly the owning workspace. | ||
|
|
||
| <!-- BEGIN_TF_DOCS --> | ||
| ## Requirements | ||
|
|
||
| | Name | Version | | ||
| |------|---------| | ||
| | <a name="requirement_meshstack"></a> [meshstack](#requirement\_meshstack) | >= 0.24.0 | | ||
|
|
||
| ## Modules | ||
|
|
||
| No modules. | ||
|
|
||
| ## Resources | ||
|
|
||
| | Name | Type | | ||
| |------|------| | ||
| | [meshstack_building_block.created](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/building_block) | resource | | ||
| | [meshstack_building_block_definition.created](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/building_block_definition) | resource | | ||
| | [meshstack_landingzone.created](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/landingzone) | resource | | ||
| | [meshstack_location.created](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/location) | resource | | ||
| | [meshstack_platform.created](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/platform) | resource | | ||
| | [meshstack_platform_type.created](https://registry.terraform.io/providers/meshcloud/meshstack/latest/docs/resources/platform_type) | resource | | ||
|
|
||
| ## Inputs | ||
|
|
||
| | Name | Description | Type | Default | Required | | ||
| |------|-------------|------|---------|:--------:| | ||
| | <a name="input_building_block_uuid"></a> [building\_block\_uuid](#input\_building\_block\_uuid) | UUID of the building block this run belongs to. Wired in as a TENANT\_BUILDING\_BLOCK\_UUID input and used to name the created platform, location and platform type, whose identifiers must be globally unique. | `string` | n/a | yes | | ||
| | <a name="input_hub_git_ref"></a> [hub\_git\_ref](#input\_hub\_git\_ref) | Hub reference the created building block definition clones its implementation from. Wired in as a static input from the composition's own `var.hub.git_ref`, so both definitions stay on the same hub revision. | `string` | n/a | yes | | ||
| | <a name="input_link_name"></a> [link\_name](#input\_link\_name) | Name given to the building block definition and building block this composition creates. | `string` | `"Link"` | no | | ||
| | <a name="input_link_url"></a> [link\_url](#input\_link\_url) | Target of the link the created building block publishes. | `string` | n/a | yes | | ||
| | <a name="input_platform_name"></a> [platform\_name](#input\_platform\_name) | Display name of the platform this composition creates. Its identifier is generated instead of taken from here, because a platform identifier cannot be reused once deleted. | `string` | `"Composition Demo Platform"` | no | | ||
| | <a name="input_workspace_identifier"></a> [workspace\_identifier](#input\_workspace\_identifier) | Workspace the created building block definition is owned by and the created building block is attached to. Wired in as a WORKSPACE\_IDENTIFIER input, so it is always the consuming workspace — the same one the run's ephemeral API key is scoped to. | `string` | n/a | yes | | ||
|
|
||
| ## Outputs | ||
|
|
||
| | Name | Description | | ||
| |------|-------------| | ||
| | <a name="output_created_building_block_definition_uuid"></a> [created\_building\_block\_definition\_uuid](#output\_created\_building\_block\_definition\_uuid) | UUID of the building block definition this composition created. | | ||
| | <a name="output_created_building_block_uuid"></a> [created\_building\_block\_uuid](#output\_created\_building\_block\_uuid) | UUID of the building block this composition created. | | ||
| | <a name="output_created_landing_zone_identifier"></a> [created\_landing\_zone\_identifier](#output\_created\_landing\_zone\_identifier) | Identifier of the landing zone this composition created. Landing zones have no UUID in the API. | | ||
| | <a name="output_created_platform_uuid"></a> [created\_platform\_uuid](#output\_created\_platform\_uuid) | UUID of the platform this composition created. | | ||
| | <a name="output_summary"></a> [summary](#output\_summary) | Markdown summary shown on the building block's detail page. | | ||
| <!-- END_TF_DOCS --> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| # Both resources below are created through the meshStack API with the run's ephemeral API key, which | ||
| # meshStack records as their creation author. That is what makes them show "created by building | ||
| # block" provenance pointing back at the building block this module runs for. | ||
|
|
||
| # Runs the hub's `link` building block rather than an implementation of its own: `link` provisions | ||
| # nothing but a terraform_data, needs no cloud provider and no operator, which makes it the cheapest | ||
| # real implementation to hand a created definition. See modules/meshstack/link. | ||
| resource "meshstack_building_block_definition" "created" { | ||
| metadata = { | ||
| owned_by_workspace = var.workspace_identifier | ||
| } | ||
|
|
||
| spec = { | ||
| display_name = var.link_name | ||
| description = "Link building block definition created by the Composition Demo building block." | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. d: parameterize this already so that it shows some dummy link, for example "this is an element of a composition" |
||
| target_type = "WORKSPACE_LEVEL" | ||
| run_transparency = true | ||
|
|
||
| readme = chomp(<<-EOT | ||
| A link building block definition created by a composition rather than by a platform engineer. | ||
|
|
||
| Ordering it publishes a link in your workspace and provisions no infrastructure. | ||
| EOT | ||
| ) | ||
| } | ||
|
|
||
| version_spec = { | ||
| # Stays a draft: releasing a version needs admin approval, which the run's ephemeral key — a | ||
| # plain workspace key — cannot obtain. Setting draft = false would leave the version DRAFT | ||
| # anyway, warn on every run, and leave version_latest_release null. The building block below can | ||
| # still be created from a draft because the definition and the target are the same workspace, | ||
| # which satisfies BuildingBlockCreationValidator.requireAccess's `selfOwning` branch. | ||
| draft = true | ||
|
|
||
| # PURGE, not DELETE: DELETE would schedule a deprovisioning run for the building block below when | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. d: I think that's not fully correct, and just a matter of correct depends_on because tofu doesn't know the BBD -> BB topology inside meshStack |
||
| # this composition is torn down, and that run cannot start until the composition's own destroy run | ||
| # returns — a deadlock wherever the terraform runner pool has a single worker. Nothing is leaked | ||
| # by purging, because the `link` implementation provisions nothing but a terraform_data. | ||
| deletion_mode = "PURGE" | ||
|
|
||
| implementation = { | ||
| terraform = { | ||
| repository_url = "https://github.com/meshcloud/meshstack-hub.git" | ||
| repository_path = "modules/meshstack/link/buildingblock" | ||
| ref_name = var.hub_git_ref | ||
| terraform_version = "1.11.0" | ||
| async = false | ||
| use_mesh_http_backend_fallback = true | ||
| } | ||
| } | ||
|
|
||
| # All static, so the created building block below has no inputs left to supply. Mirrors the | ||
| # wiring in modules/meshstack/link/meshstack_integration.tf. | ||
| inputs = { | ||
| url = { | ||
| display_name = "URL" | ||
| description = "Target of the link." | ||
| type = "STRING" | ||
| assignment_type = "STATIC" | ||
| argument = jsonencode(var.link_url) | ||
| } | ||
| title = { | ||
| display_name = "Title" | ||
| description = "Human-readable name of the linked resource." | ||
| type = "STRING" | ||
| assignment_type = "STATIC" | ||
| argument = jsonencode(var.link_name) | ||
| } | ||
| # Empty falls back to the summary the link module generates from title and url. | ||
| summary = { | ||
| display_name = "Summary" | ||
| description = "Markdown rendered for the application team after deployment." | ||
| type = "STRING" | ||
| assignment_type = "STATIC" | ||
| argument = jsonencode("") | ||
| } | ||
| } | ||
|
|
||
| outputs = { | ||
| url = { | ||
| display_name = var.link_name | ||
| type = "STRING" | ||
| assignment_type = "RESOURCE_URL" | ||
| } | ||
| summary = { | ||
| display_name = "Summary" | ||
| type = "STRING" | ||
| assignment_type = "SUMMARY" | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| resource "meshstack_building_block" "created" { | ||
| spec = { | ||
| # version_latest, not version_latest_release: the definition above stays a draft, so | ||
| # version_latest_release is null. | ||
| building_block_definition_version_ref = { | ||
| uuid = meshstack_building_block_definition.created.version_latest.uuid | ||
| content_hash = meshstack_building_block_definition.created.version_latest.content_hash | ||
| } | ||
|
|
||
| display_name = var.link_name | ||
|
|
||
| target_ref = { | ||
| kind = "meshWorkspace" | ||
| name = var.workspace_identifier | ||
| } | ||
|
|
||
| # Every input of the definition above is static, so there is nothing for this block to set. | ||
| inputs = {} | ||
| } | ||
|
|
||
| # Deliberately not waiting: this run would be waiting on a run of the same implementation type, | ||
| # which a stack with a single terraform runner cannot start until this one returns. Nothing below | ||
| # depends on the result either — provenance is recorded when the block is created. | ||
| wait_for_completion = false | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| output "created_building_block_definition_uuid" { | ||
| value = meshstack_building_block_definition.created.metadata.uuid | ||
| description = "UUID of the building block definition this composition created." | ||
| } | ||
|
|
||
| output "created_building_block_uuid" { | ||
| value = meshstack_building_block.created.metadata.uuid | ||
| description = "UUID of the building block this composition created." | ||
| } | ||
|
|
||
| output "created_platform_uuid" { | ||
| value = meshstack_platform.created.metadata.uuid | ||
| description = "UUID of the platform this composition created." | ||
| } | ||
|
|
||
| output "created_landing_zone_identifier" { | ||
| value = meshstack_landingzone.created.metadata.name | ||
| description = "Identifier of the landing zone this composition created. Landing zones have no UUID in the API." | ||
| } | ||
|
|
||
| output "summary" { | ||
| value = chomp(<<-EOT | ||
| Created a Link building block definition with a building block from it, and an empty platform with a | ||
| landing zone on it. All of them show this building block as their creator. | ||
|
|
||
| | meshObject | Name | Identifier | | ||
| |---|---|---| | ||
| | Building Block Definition | ${var.link_name} | `${meshstack_building_block_definition.created.metadata.uuid}` | | ||
| | Building Block | ${var.link_name} | `${meshstack_building_block.created.metadata.uuid}` | | ||
| | Platform Type | ${meshstack_platform_type.created.spec.display_name} | `${meshstack_platform_type.created.metadata.name}` | | ||
| | Location | ${meshstack_location.created.spec.display_name} | `${meshstack_location.created.metadata.name}` | | ||
| | Platform | ${var.platform_name} | `${meshstack_platform.created.identifier}` | | ||
| | Landing Zone | ${meshstack_landingzone.created.spec.display_name} | `${meshstack_landingzone.created.metadata.name}` | | ||
| EOT | ||
| ) | ||
| description = "Markdown summary shown on the building block's detail page." | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| # The second thing this composition creates: an *empty* platform with a landing zone on it. Empty means | ||
| # the least a platform can carry — a CUSTOM platform type, no cloud connection config, and a landing | ||
| # zone whose platform properties are `{}`. That is enough for meshStack to record and show creation | ||
| # provenance on both, and it needs no cloud credentials and no operator. | ||
| # | ||
| # As in main.tf, everything here is created with the run's ephemeral API key, which is what makes | ||
| # meshStack attribute the creation to the building block this module runs for. | ||
|
|
||
| locals { | ||
| # A platform, location and platform type identifier is globally unique and cannot be reused once | ||
| # deleted, so the objects below are named after the building block that owns them. Unlike a random | ||
| # suffix this survives re-runs without a stored value, and it makes the identifiers point back at | ||
| # their creator on sight. | ||
| suffix = substr(replace(var.building_block_uuid, "-", ""), 0, 8) | ||
| identifier = "composition-demo-${local.suffix}" | ||
| } | ||
|
|
||
| # meshStack ships no CUSTOM platform type, and every built-in type is a cloud platform whose config | ||
| # would need real credentials — so an empty platform needs a type of its own. | ||
| resource "meshstack_platform_type" "created" { | ||
| metadata = { | ||
| # Platform type identifiers are uppercase, unlike every other identifier here. | ||
| name = upper(local.identifier) | ||
| owned_by_workspace = var.workspace_identifier | ||
| } | ||
|
|
||
| spec = { | ||
| # Carries the suffix because meshStack requires platform type display names to be globally unique. | ||
| display_name = "${var.platform_name} ${upper(local.suffix)}" | ||
|
|
||
| # A 1x1 transparent PNG. The API requires an icon, and nothing here is meant to be looked at. | ||
| icon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVR42mNgAAIAAAUAAen63NgAAAAASUVORK5CYII=" | ||
| } | ||
| } | ||
|
|
||
| resource "meshstack_location" "created" { | ||
| metadata = { | ||
| name = local.identifier | ||
| owned_by_workspace = var.workspace_identifier | ||
| } | ||
|
|
||
| spec = { | ||
| display_name = "${var.platform_name} Location" | ||
| description = "Location created by the Composition Demo building block." | ||
| } | ||
| } | ||
|
|
||
| resource "meshstack_platform" "created" { | ||
| metadata = { | ||
| name = local.identifier | ||
| owned_by_workspace = var.workspace_identifier | ||
| } | ||
|
|
||
| spec = { | ||
| display_name = var.platform_name | ||
| description = "Empty platform created by the Composition Demo building block." | ||
| endpoint = "https://docs.meshcloud.io" | ||
|
|
||
| location_ref = { | ||
| name = meshstack_location.created.metadata.name | ||
| } | ||
|
|
||
| # Keeps the platform out of the marketplace, which is where an empty platform belongs. meshStack | ||
| # ties the three fields together: UNPUBLISHED is only allowed with PRIVATE, and PRIVATE requires | ||
| # `restricted_to_workspaces` to name exactly the owner. | ||
| availability = { | ||
| restriction = "PRIVATE" | ||
| publication_state = "UNPUBLISHED" | ||
| restricted_to_workspaces = [var.workspace_identifier] | ||
| } | ||
|
|
||
| config = { | ||
| custom = { | ||
| platform_type_ref = { | ||
| name = meshstack_platform_type.created.metadata.name | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| resource "meshstack_landingzone" "created" { | ||
| metadata = { | ||
| # Only unique per platform, so it can repeat the platform's identifier. | ||
| name = local.identifier | ||
| owned_by_workspace = var.workspace_identifier | ||
| } | ||
|
|
||
| spec = { | ||
| display_name = "${var.platform_name} Landing Zone" | ||
| description = "Empty landing zone created by the Composition Demo building block." | ||
|
|
||
| automate_deletion_approval = false | ||
| automate_deletion_replication = false | ||
|
|
||
| platform_ref = { | ||
| uuid = meshstack_platform.created.metadata.uuid | ||
| } | ||
|
|
||
| # A custom platform has no landing zone properties at all. | ||
| platform_properties = { | ||
| custom = {} | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # Authenticates from the MESHSTACK_ENDPOINT / MESHSTACK_API_TOKEN environment variables the runner | ||
| # injects for the run's ephemeral API key. | ||
| provider "meshstack" { | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
d: actually this is a demo of an "architecture composition" which is an advanced use case of compositions. A plain composition is sth like the AKS StarterKit that only deploys application team resources (projects, tenants, building blocks)
This is a "reference architecture" composition that also deploys platform team artifacts like BBDs, (LZs forthcomming) etc.