You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Create backlinks and forward links in GCP dynamic adapters (#2807)
# Add Bidirectional Hierarchical Linking for GCP Resources
## Overview
This PR implements comprehensive support for bidirectional linking
between hierarchical GCP resources (parent-child relationships). It
enables automatic discovery of relationships in both directions:
- **Backlinks:** Child resources can link to their parent (e.g.,
Database → Instance)
- **Forward links:** Parent resources can discover all their children
(e.g., Instance → All Databases)
## Key Changes
### 1. Framework Enhancement
**File:** `sources/gcp/shared/linker.go`
Added `IsParentToChild` flag support to enable parent-to-child forward
linking:
- Detects the `IsParentToChild` flag on blast propagation impacts
- Automatically removes the last unique attribute key (child identifier)
when creating forward links
- Changes query method from GET to SEARCH to discover all child
resources
- Fixes variable shadowing for regional/zonal scope handling
**Example:** A Spanner Instance can now link to all its databases using
SEARCH with just the instance name, without needing to know specific
database names.
### 2. Backlinks Implemented (Child → Parent via GET)
Uses the `name` field to extract parent identifiers from hierarchical
resource names:
- **BigTableAdminCluster** → BigTableAdminInstance
- **ContainerNodePool** → ContainerCluster
- **SpannerBackup** → SpannerInstance
- **SpannerDatabase** → SpannerInstance _(initial implementation)_
**How it works:** The framework extracts the parent identifier from the
child's name (e.g., `projects/p/instances/i/databases/d` → extracts `i`)
and creates a GET query to the parent resource.
### 3. Forward Links Implemented (Parent → Child via SEARCH)
Uses the `IsParentToChild: true` flag to create SEARCH queries that
discover all children:
- **BigTableAdminInstance** → BigTableAdminCluster
- **ArtifactRegistryRepository** → ArtifactRegistryDockerImage
- **ContainerCluster** → ContainerNodePool
- **RunService** → RunRevision
- **SpannerInstance** → SpannerDatabase
- **SQLAdminInstance** → SQLAdminBackupRun
**How it works:** The framework uses the parent's name to create a
SEARCH query that returns all child resources belonging to that parent.
### 4. Documentation
**File:**
`sources/gcp/dynamic/adapters/.cursor/rules/dynamic-adapter-creation.mdc`
Added comprehensive documentation (143 lines) covering:
#### Backlink Pattern
- Concept explanation and use cases
- Complete code example using Spanner Database → Instance
- Step-by-step framework behavior breakdown
- Test patterns for verification
- Guidelines on when to use this pattern
#### Forward Link Pattern
- Concept explanation and use cases
- Complete code example using Spanner Instance → Databases
- Framework behavior with `IsParentToChild` flag
- Critical requirements (child must support SEARCH)
- Test patterns for verification
- Comparison table: Backlink vs Forward Link
### 5. Tests Updated
- `big-table-admin-cluster_test.go`: Added backlink test case
- `spanner-instance_test.go`: Added forward link test case
## Validation
✅ All tests passing: `go test -race ./sources/gcp/... -v -timeout 5m`
✅ Linting clean: `golangci-lint run ./sources/gcp/... --timeout 5m`
## Impact
This PR enables complete bidirectional relationship discovery for
hierarchical GCP resources, improving:
- **Dependency tracking:** Understanding which children depend on a
parent
- **Impact analysis:** Discovering all resources affected by parent
changes
- **Infrastructure visualization:** Building complete resource graphs
with parent-child relationships
---
Would you like me to make any adjustments to this PR description?
---------
Co-authored-by: carabasdaniel <[email protected]>
GitOrigin-RevId: c8d839894d83a800f8c5d9f75019e59847bdc920
- **If no adapter exists**: Create the SDP item type definition in `sources/gcp/shared/item-types.go` and `models.go` as if the adapter exists, then link to it
180
180
- **Follow naming**: `gcp-<api>-<resource>` for new adapter types
181
181
182
+
#### Creating Backlinks from Child to Parent Resources
183
+
184
+
When a resource name contains hierarchical information (e.g., a database name includes its parent instance), you can create a backlink using the `name` field. The framework will automatically extract the parent resource identifier and create the appropriate linked item query.
185
+
186
+
**Use Case**: Child resources that reference their parent in their name structure.
187
+
188
+
**Example: Spanner Database to Instance Backlink**
189
+
190
+
A Spanner database name has the format: `projects/{project}/instances/{instance}/databases/{database}`
191
+
192
+
To create a backlink from the database to its parent instance:
193
+
194
+
```go
195
+
// In sources/gcp/shared/blast-propagations.go
196
+
SpannerDatabase: {
197
+
// ... other blast propagations ...
198
+
199
+
// This is a backlink to instance.
200
+
// Framework will extract the instance name and create the linked item query with GET
201
+
"name": {
202
+
Description: "If the Spanner Instance is deleted or updated: The Database may become invalid or inaccessible. If the Database is updated: The instance remains unaffected.",
203
+
ToSDPItemType: SpannerInstance,
204
+
BlastPropagation: impactInOnly,
205
+
},
206
+
}
207
+
```
208
+
209
+
**How It Works:**
210
+
1. The framework reads the `name` field value (e.g., `projects/my-project/instances/test-instance/databases/my-db`)
211
+
2. It extracts the parent instance identifier (`test-instance`)
212
+
3. It creates a GET query for the parent SpannerInstance
213
+
4. The linked item query will have:
214
+
- Type: `gcp-spanner-instance`
215
+
- Method: GET
216
+
- Query: `test-instance` (extracted from database name)
217
+
- Scope: Same project as the database
218
+
219
+
**Testing the Backlink:**
220
+
221
+
When adding backlink blast propagations, verify they work correctly in tests:
222
+
223
+
```go
224
+
// In adapter_test.go
225
+
{
226
+
// name field creates a backlink to the Spanner instance
- Parent resource is part of the child's name/path
241
+
- Relationship is one-way (child depends on parent, but parent doesn't depend on specific child)
242
+
- Use `impactInOnly` blast propagation (parent changes affect child, but not vice versa)
243
+
244
+
#### Creating Forward Links from Parent to Child Resources
245
+
246
+
When a parent resource needs to link to all its child resources, you can use the `IsParentToChild` flag to create a forward link using SEARCH. This is the inverse pattern of the backlink described above.
247
+
248
+
**Use Case**: Parent resources that need to discover and link to all their child resources (e.g., instance → all databases).
249
+
250
+
**Example: Spanner Instance to Databases Forward Link**
251
+
252
+
A Spanner instance needs to link to all databases that belong to it. Since the instance doesn't have the database names, we use SEARCH to find all databases for that instance.
253
+
254
+
To create a forward link from the instance to its databases:
255
+
256
+
```go
257
+
// In the adapter file (e.g., spanner-instance.go)
258
+
var spannerInstanceAdapter = registerableAdapter{
259
+
// ... other fields ...
260
+
261
+
blastPropagation: map[string]*gcpshared.Impact{
262
+
// This a link from parent to child via SEARCH
263
+
// We need to make sure that the linked item supports `SEARCH` method for the `instance` name.
264
+
"name": {
265
+
ToSDPItemType: gcpshared.SpannerDatabase,
266
+
Description: "If the Spanner Instance is deleted or updated: All associated databases may become invalid or inaccessible. If a database is updated: The instance remains unaffected.",
267
+
BlastPropagation: &sdp.BlastPropagation{
268
+
In: false,
269
+
Out: true,
270
+
},
271
+
IsParentToChild: true,
272
+
},
273
+
},
274
+
}
275
+
```
276
+
277
+
**How It Works:**
278
+
1. The framework detects `IsParentToChild: true` on the blast propagation
279
+
2. It modifies the unique attribute keys by removing the last element (the child identifier)
280
+
- For databases: `["instances", "databases"]` becomes `["instances"]`
281
+
3. It extracts only the parent identifier from the resource name (e.g., `test-instance`)
282
+
4. It creates a SEARCH query (not GET) to find all child resources
283
+
5. The linked item query will have:
284
+
- Type: `gcp-spanner-database`
285
+
- Method: SEARCH
286
+
- Query: `test-instance` (the instance name)
287
+
- Scope: Same project as the instance
288
+
289
+
**Testing the Forward Link:**
290
+
291
+
When adding parent-to-child blast propagations, verify they work correctly in tests:
292
+
293
+
```go
294
+
// In adapter_test.go (e.g., spanner-instance_test.go)
295
+
{
296
+
ExpectedType: gcpshared.SpannerDatabase.String(),
297
+
ExpectedMethod: sdp.QueryMethod_SEARCH,
298
+
ExpectedQuery: instanceName,
299
+
ExpectedScope: projectID,
300
+
ExpectedBlastPropagation: &sdp.BlastPropagation{
301
+
In: false,
302
+
Out: true,
303
+
},
304
+
}
305
+
```
306
+
307
+
**Critical Requirements:**
308
+
- **Child adapter must support SEARCH**: The child resource type must implement the SEARCH method
309
+
- **Query parameter must match**: The child's SEARCH method must accept the parent identifier as a search parameter
310
+
- **Set `IsParentToChild: true`**: This flag triggers the special SEARCH behavior
311
+
312
+
**When to Use This Pattern:**
313
+
- Parent resources that need to discover all their children (instance → databases, network → subnets)
314
+
- The parent's name/identifier is sufficient to search for children
Description: "If the Artifact Registry Repository is deleted or updated: All associated Docker Images may become invalid or inaccessible. If a Docker Image is updated: The repository remains unaffected.",
// Framework will extract the instance name and create the linked item query with GET
29
+
// NOTE: We prioritize the backlink over a forward link to BigTableAdminBackup
30
+
// because the backlink is more critical for understanding the cluster's dependencies.
31
+
"name": {
32
+
ToSDPItemType: gcpshared.BigTableAdminInstance,
33
+
Description: "If the BigTableAdmin Instance is deleted or updated: The Cluster may become invalid or inaccessible. If the Cluster is updated: The instance remains unaffected.",
// The Bigtable Instance does not contain any fields that would cause blast propagation.
25
-
blastPropagation: map[string]*gcpshared.Impact{},
24
+
blastPropagation: map[string]*gcpshared.Impact{
25
+
// Forward link from parent to child via SEARCH
26
+
// Link to all clusters in this instance (most fundamental infrastructure component)
27
+
"name": {
28
+
ToSDPItemType: gcpshared.BigTableAdminCluster,
29
+
Description: "If the BigTableAdmin Instance is deleted or updated: All associated Clusters may become invalid or inaccessible. If a Cluster is updated: The instance remains unaffected.",
Description: "If the Container Cluster is deleted or updated: All associated Node Pools may become invalid or inaccessible. If a Node Pool is updated: The cluster remains unaffected.",
// Framework will extract the cluster name and create the linked item query with GET
41
+
"name": {
42
+
ToSDPItemType: gcpshared.ContainerCluster,
43
+
Description: "If the Container Cluster is deleted or updated: The Node Pool may become invalid or inaccessible. If the Node Pool is updated: The cluster remains unaffected.",
Copy file name to clipboardExpand all lines: sources/gcp/dynamic/adapters/run-service.go
+11Lines changed: 11 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -87,6 +87,17 @@ var _ = registerableAdapter{
87
87
Description: "If the Cloud Run Service is deleted or updated: Traffic allocation to revisions will be lost. If revisions are updated: The service traffic configuration may need updates.",
Description: "If the Cloud Run Service is deleted or updated: All associated Revisions may become invalid or inaccessible. If a Revision is updated: The service remains unaffected.",
Copy file name to clipboardExpand all lines: sources/gcp/dynamic/adapters/spanner-instance.go
+11Lines changed: 11 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -31,6 +31,17 @@ var spannerInstanceAdapter = registerableAdapter{ //nolint:unused
31
31
Out: false,
32
32
},
33
33
},
34
+
// This is a link from parent to child via SEARCH
35
+
// We need to make sure that the linked item supports `SEARCH` method for the `instance` name.
36
+
"name": {
37
+
ToSDPItemType: gcpshared.SpannerDatabase,
38
+
Description: "If the Spanner Instance is deleted or updated: All associated databases may become invalid or inaccessible. If a database is updated: The instance remains unaffected.",
0 commit comments