|
| 1 | +# Resource Processing Flows |
| 2 | + |
| 3 | +This document describes every code path in the `build`, `test`, and `teardown` commands based on which anchors are present in a resource's `.iql` file. |
| 4 | + |
| 5 | +## Anchor Reference |
| 6 | + |
| 7 | +| Anchor | Purpose | |
| 8 | +|--------|---------| |
| 9 | +| `exists` | Check if resource exists (returns `count` or a named field) | |
| 10 | +| `statecheck` | Verify resource properties match desired state | |
| 11 | +| `create` | Create the resource | |
| 12 | +| `update` | Update the resource (patch document) | |
| 13 | +| `createorupdate` | Always execute (skip exists/statecheck) | |
| 14 | +| `exports` | Extract values for downstream resources | |
| 15 | +| `delete` | Remove the resource | |
| 16 | + |
| 17 | +## Exists Query Variants |
| 18 | + |
| 19 | +The `exists` query has two modes based on the returned column name: |
| 20 | + |
| 21 | +**Count mode** — returns `count`, existence is `count > 0`: |
| 22 | +```sql |
| 23 | +SELECT count(*) as count FROM awscc.s3.buckets WHERE Identifier = '{{ bucket_name }}' AND region = '{{ region }}'; |
| 24 | +``` |
| 25 | + |
| 26 | +**Field capture mode** — returns a named field (e.g. `vpc_id`), captured as `this.<field>` for downstream queries: |
| 27 | +```sql |
| 28 | +SELECT split_part(ResourceARN, '/', 2) as vpc_id FROM awscc.tagging.tagged_resources WHERE ...; |
| 29 | +``` |
| 30 | + |
| 31 | +When `null` or empty is returned in field capture mode, the resource is treated as non-existent. |
| 32 | + |
| 33 | +--- |
| 34 | + |
| 35 | +## Build Flows |
| 36 | + |
| 37 | +### Flow A: `createorupdate` + `exports` |
| 38 | + |
| 39 | +Used for resources that should always be applied (e.g. routes, gateway attachments). |
| 40 | + |
| 41 | +```mermaid |
| 42 | +graph LR |
| 43 | + A[createorupdate] --> B[exports proxy?] |
| 44 | + B -->|rows returned| C[done ✓] |
| 45 | + B -->|no rows| D[FAIL ✗] |
| 46 | +``` |
| 47 | + |
| 48 | +**Anchors:** `createorupdate`, optionally `exports` |
| 49 | +**Examples:** `example_inet_route`, `example_inet_gw_attachment` (old pattern) |
| 50 | + |
| 51 | +--- |
| 52 | + |
| 53 | +### Flow B: `exists`(count) + `statecheck` + `create`/`update` + `exports` |
| 54 | + |
| 55 | +Classic pattern for resources identified by a known name/identifier. |
| 56 | + |
| 57 | +```mermaid |
| 58 | +graph LR |
| 59 | + A[exists count] -->|0| B[create] |
| 60 | + A -->|>0| C[statecheck] |
| 61 | + C -->|pass| D[exports] |
| 62 | + C -->|fail| E[update] |
| 63 | + B --> F[post-create exists] |
| 64 | + F --> G[statecheck] |
| 65 | + G --> D |
| 66 | + E --> H[post-update exists] |
| 67 | + H --> I[statecheck] |
| 68 | + I --> D |
| 69 | + D --> J[done ✓] |
| 70 | +``` |
| 71 | + |
| 72 | +**Anchors:** `exists`(count), `statecheck`, `create`, optionally `update`, optionally `exports` |
| 73 | +**Examples:** `databricks_account_credentials`, `aws_s3_workspace_bucket_policy` |
| 74 | + |
| 75 | +--- |
| 76 | + |
| 77 | +### Flow C: `exists`(field) + `statecheck` + `create` + `exports` |
| 78 | + |
| 79 | +Pattern for awscc resources using tagging for identification. The exists query returns a resource-specific field (e.g. `vpc_id`) which is captured as `this.<field>` and used in statecheck and exports. |
| 80 | + |
| 81 | +```mermaid |
| 82 | +graph LR |
| 83 | + A[exists field] -->|null| B[create] |
| 84 | + A -->|value| C["this.field = value"] |
| 85 | + C --> D["statecheck(this.field)"] |
| 86 | + D -->|pass| E["exports(this.field)"] |
| 87 | + D -->|fail| F["update(this.field)"] |
| 88 | + B --> G[post-create exists] |
| 89 | + G --> H["this.field = value"] |
| 90 | + H --> I["statecheck(this.field)"] |
| 91 | + I --> E |
| 92 | + F --> J[post-update exists] |
| 93 | + J --> K["statecheck(this.field)"] |
| 94 | + K --> E |
| 95 | + E --> L[done ✓] |
| 96 | +``` |
| 97 | + |
| 98 | +**Anchors:** `exists`(field), `statecheck`, `create`, optionally `update`, `exports` |
| 99 | +**Examples:** `example_vpc`, `example_subnet`, `example_inet_gateway`, `example_route_table`, `example_security_group`, `example_web_server` |
| 100 | + |
| 101 | +--- |
| 102 | + |
| 103 | +### Flow D: `exists`(count) + `exports`-as-proxy (no statecheck) |
| 104 | + |
| 105 | +The exports query doubles as a statecheck — if it returns rows, the resource is in the desired state. |
| 106 | + |
| 107 | +```mermaid |
| 108 | +graph LR |
| 109 | + A[exists count] -->|0| B[create] |
| 110 | + A -->|>0| C[exports proxy] |
| 111 | + C -->|rows returned| D[done ✓] |
| 112 | + C -->|no rows| E[update] |
| 113 | + B --> F[post-create exists] |
| 114 | + F --> G[exports proxy] |
| 115 | + G --> D |
| 116 | + E --> H[post-update exists] |
| 117 | + H --> I[exports proxy] |
| 118 | + I --> D |
| 119 | +``` |
| 120 | + |
| 121 | +**Anchors:** `exists`(count), `create`, optionally `update`, `exports` |
| 122 | +**Examples:** `aws_s3_workspace_bucket`, `databricks_storage_configuration` |
| 123 | + |
| 124 | +--- |
| 125 | + |
| 126 | +### Flow E: `exists`(field) + `exports` (no statecheck) |
| 127 | + |
| 128 | +Field capture from exists, exports uses `this.<field>`. Exports acts as statecheck proxy. |
| 129 | + |
| 130 | +```mermaid |
| 131 | +graph LR |
| 132 | + A[exists field] -->|null| B[create] |
| 133 | + A -->|value| C["this.field = value"] |
| 134 | + C --> D["exports proxy(this.field)"] |
| 135 | + D -->|rows returned| E[done ✓] |
| 136 | + D -->|no rows| F[update] |
| 137 | + B --> G[post-create exists] |
| 138 | + G --> H["this.field = value"] |
| 139 | + H --> I["exports proxy(this.field)"] |
| 140 | + I --> E |
| 141 | +``` |
| 142 | + |
| 143 | +**Anchors:** `exists`(field), `create`, `exports` |
| 144 | +**Examples:** `example_subnet_rt_assn` |
| 145 | + |
| 146 | +--- |
| 147 | + |
| 148 | +### Flow F: `exists`(field) only (no statecheck, no exports) |
| 149 | + |
| 150 | +Minimal pattern — exists confirms the resource, the captured field is the only output. |
| 151 | + |
| 152 | +```mermaid |
| 153 | +graph LR |
| 154 | + A[exists field] -->|null| B[create] |
| 155 | + A -->|value| C[done ✓] |
| 156 | + B --> D[post-create exists] |
| 157 | + D -->|value| C |
| 158 | +``` |
| 159 | + |
| 160 | +**Anchors:** `exists`(field), `create` |
| 161 | +**Examples:** Simple resources where existence is sufficient |
| 162 | + |
| 163 | +--- |
| 164 | + |
| 165 | +## Test Flows |
| 166 | + |
| 167 | +Test runs the same exists → statecheck/exports-proxy sequence as build, but never creates or updates. If a resource doesn't exist or fails statecheck, the test fails. |
| 168 | + |
| 169 | +```mermaid |
| 170 | +graph LR |
| 171 | + A[exists] -->|not found| B[FAIL ✗] |
| 172 | + A -->|found| C[statecheck or exports proxy] |
| 173 | + C -->|pass| D[exports] |
| 174 | + C -->|fail| B |
| 175 | + D --> E[done ✓] |
| 176 | +``` |
| 177 | + |
| 178 | +--- |
| 179 | + |
| 180 | +## Teardown Flows |
| 181 | + |
| 182 | +Teardown processes resources in reverse manifest order. First collects all exports (running exists to capture `this.*` fields), then deletes. |
| 183 | + |
| 184 | +```mermaid |
| 185 | +graph LR |
| 186 | + A[collect exports phase] --> B[exists per resource] |
| 187 | + B --> C[exports per resource] |
| 188 | + C --> D[reverse order delete phase] |
| 189 | + D --> E[exists check] |
| 190 | + E -->|found| F[delete] |
| 191 | + E -->|not found| G[skip] |
| 192 | + F --> H[post-delete exists] |
| 193 | + H -->|gone| I[done ✓] |
| 194 | + H -->|still there| I |
| 195 | +``` |
| 196 | + |
| 197 | +During teardown export collection, missing exports are set to `<unknown>` rather than failing — the stack may be partially deployed. |
| 198 | + |
| 199 | +--- |
| 200 | + |
| 201 | +## `this.*` Variable Lifecycle |
| 202 | + |
| 203 | +When an exists query returns a named field (not `count`), the value is captured as a resource-scoped variable: |
| 204 | + |
| 205 | +| Phase | Variable | Available to | |
| 206 | +|-------|----------|-------------| |
| 207 | +| exists returns `vpc_id` | `this.vpc_id` → `example_vpc.vpc_id` | statecheck, exports, delete for this resource | |
| 208 | +| exports returns `vpc_id` | `vpc_id` and `example_vpc.vpc_id` | all subsequent resources | |
| 209 | + |
| 210 | +The `this.*` prefix is syntactic sugar — `{{ this.vpc_id }}` in `example_vpc`'s queries is preprocessed to `{{ example_vpc.vpc_id }}`. |
| 211 | + |
| 212 | +--- |
| 213 | + |
| 214 | +## RETURNING * Optimization |
| 215 | + |
| 216 | +When a `create` query includes `RETURNING *`, the Cloud Control API returns the operation metadata immediately. If `return_vals` is configured in the manifest, specified fields are captured as `this.*` variables, eliminating the need for a post-create exists re-run. |
| 217 | + |
| 218 | +```yaml |
| 219 | +resources: |
| 220 | + - name: example_vpc |
| 221 | + return_vals: |
| 222 | + create: |
| 223 | + - Identifier: vpc_id # rename Identifier → this.vpc_id |
| 224 | + - ErrorCode # capture as this.ErrorCode |
| 225 | +``` |
0 commit comments