From dcf292836eb8f217149a56dfda9a4d0d3195977f Mon Sep 17 00:00:00 2001
From: Simone Rodigari <32323373+SRodi@users.noreply.github.com>
Date: Tue, 28 Jan 2025 14:17:13 +0000
Subject: [PATCH 1/2] test(mc): initial multicloud IaC boilerplate GKE, AKS,
Kind (#1268)
# Description
Initial work on multi-cloud
* create modules for aks, gke, kind and retina
* use gcloud, azure, kind and helm providers
* create examples on how to use each module
* create automated tests in Go Terratest for each module example (only
initial work, since we require creds for public clouds)
* create integration test retina-kind (this can also be used as an
example for AKS, GKE and eventually EKS)
* create a Makefile for this subproject
* create GH workflow with automated tests
![diagram](https://github.com/user-attachments/assets/38ded8ec-0c31-4e5d-9bd0-8729115cf5b3)
## Related Issue
#1267
## Checklist
- [x] I have read the [contributing
documentation](https://retina.sh/docs/contributing).
- [x] I signed and signed-off the commits (`git commit -S -s ...`). See
[this
documentation](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification)
on signing commits.
- [x] I have correctly attributed the author(s) of the code.
- [x] I have tested the changes locally.
- [x] I have followed the project's style guidelines.
- [x] I have updated the documentation, if necessary.
- [x] I have added tests, if applicable.
## Screenshots (if applicable) or Testing Completed
All modules were tested and work as expected.
![Screenshot 2025-01-24 at 12 18
55](https://github.com/user-attachments/assets/2d222a13-2b12-4a11-a001-1b03a254e152)
See automated tests here
https://github.com/microsoft/retina/actions/runs/12993893617/job/36237118572
![image](https://github.com/user-attachments/assets/edafa98b-1d5d-44b0-a393-1bf7b7b70bda)
## Additional Notes
Add any additional notes or context about the pull request here.
---
Please refer to the [CONTRIBUTING.md](../CONTRIBUTING.md) file for more
information on how to contribute to this project.
---
.github/workflows/test-multicloud.yml | 31 +
test/multicloud/.gitignore | 6 +
test/multicloud/Makefile | 42 +
test/multicloud/README.md | 98 +
test/multicloud/diagrams/diagram.excalidraw | 3366 +++++++++++++++++
test/multicloud/diagrams/diagram.svg | 2 +
.../examples/aks/.terraform.lock.hcl | 20 +
test/multicloud/examples/aks/main.tf | 12 +
test/multicloud/examples/aks/providers.tf | 20 +
test/multicloud/examples/aks/variables.tf | 33 +
.../examples/gke/.terraform.lock.hcl | 20 +
test/multicloud/examples/gke/main.tf | 9 +
test/multicloud/examples/gke/outputs.tf | 14 +
test/multicloud/examples/gke/providers.tf | 15 +
test/multicloud/examples/gke/variables.tf | 19 +
.../retina-kind/.terraform.lock.hcl | 34 +
.../examples/integration/retina-kind/main.tf | 10 +
.../integration/retina-kind/outputs.tf | 19 +
.../integration/retina-kind/providers.tf | 26 +
.../retina-kind/test-integration-kind-config | 3 +
.../integration/retina-kind/test-kind-config | 3 +
.../integration/retina-kind/variables.tf | 11 +
.../examples/kind/.terraform.lock.hcl | 16 +
test/multicloud/examples/kind/main.tf | 4 +
test/multicloud/examples/kind/outputs.tf | 19 +
test/multicloud/examples/kind/providers.tf | 12 +
test/multicloud/examples/kind/variables.tf | 4 +
.../live/retina-aks/.terraform.lock.hcl | 40 +
test/multicloud/live/retina-aks/main.tf | 16 +
test/multicloud/live/retina-aks/providers.tf | 34 +
test/multicloud/live/retina-aks/variables.tf | 35 +
.../live/retina-gke/.terraform.lock.hcl | 40 +
test/multicloud/live/retina-gke/main.tf | 12 +
test/multicloud/live/retina-gke/outputs.tf | 3 +
test/multicloud/live/retina-gke/providers.tf | 30 +
test/multicloud/live/retina-gke/variables.tf | 23 +
.../live/retina-kind/.terraform.lock.hcl | 36 +
test/multicloud/live/retina-kind/main.tf | 9 +
test/multicloud/live/retina-kind/providers.tf | 26 +
test/multicloud/live/retina-kind/variables.tf | 5 +
test/multicloud/modules/aks/main.tf | 47 +
test/multicloud/modules/aks/outputs.tf | 24 +
test/multicloud/modules/aks/provider.tf | 9 +
test/multicloud/modules/aks/variables.tf | 78 +
test/multicloud/modules/gke/main.tf | 34 +
test/multicloud/modules/gke/output.tf | 14 +
test/multicloud/modules/gke/provider.tf | 9 +
test/multicloud/modules/gke/variables.tf | 19 +
test/multicloud/modules/kind/main.tf | 20 +
test/multicloud/modules/kind/output.tf | 24 +
test/multicloud/modules/kind/provider.tf | 9 +
test/multicloud/modules/kind/variables.tf | 4 +
test/multicloud/modules/retina/main.tf | 15 +
test/multicloud/modules/retina/provider.tf | 8 +
test/multicloud/modules/retina/variables.tf | 27 +
test/multicloud/test/example_aks_test.go | 38 +
test/multicloud/test/example_gke_test.go | 54 +
test/multicloud/test/example_kind_test.go | 43 +
test/multicloud/test/go.mod | 73 +
test/multicloud/test/go.sum | 192 +
.../test/integration_retina_kind_test.go | 49 +
test/multicloud/test/testUtils.go | 120 +
62 files changed, 5087 insertions(+)
create mode 100644 .github/workflows/test-multicloud.yml
create mode 100644 test/multicloud/.gitignore
create mode 100644 test/multicloud/Makefile
create mode 100644 test/multicloud/README.md
create mode 100644 test/multicloud/diagrams/diagram.excalidraw
create mode 100644 test/multicloud/diagrams/diagram.svg
create mode 100644 test/multicloud/examples/aks/.terraform.lock.hcl
create mode 100644 test/multicloud/examples/aks/main.tf
create mode 100644 test/multicloud/examples/aks/providers.tf
create mode 100644 test/multicloud/examples/aks/variables.tf
create mode 100644 test/multicloud/examples/gke/.terraform.lock.hcl
create mode 100644 test/multicloud/examples/gke/main.tf
create mode 100644 test/multicloud/examples/gke/outputs.tf
create mode 100644 test/multicloud/examples/gke/providers.tf
create mode 100644 test/multicloud/examples/gke/variables.tf
create mode 100644 test/multicloud/examples/integration/retina-kind/.terraform.lock.hcl
create mode 100644 test/multicloud/examples/integration/retina-kind/main.tf
create mode 100644 test/multicloud/examples/integration/retina-kind/outputs.tf
create mode 100644 test/multicloud/examples/integration/retina-kind/providers.tf
create mode 100644 test/multicloud/examples/integration/retina-kind/test-integration-kind-config
create mode 100644 test/multicloud/examples/integration/retina-kind/test-kind-config
create mode 100644 test/multicloud/examples/integration/retina-kind/variables.tf
create mode 100644 test/multicloud/examples/kind/.terraform.lock.hcl
create mode 100644 test/multicloud/examples/kind/main.tf
create mode 100644 test/multicloud/examples/kind/outputs.tf
create mode 100644 test/multicloud/examples/kind/providers.tf
create mode 100644 test/multicloud/examples/kind/variables.tf
create mode 100644 test/multicloud/live/retina-aks/.terraform.lock.hcl
create mode 100644 test/multicloud/live/retina-aks/main.tf
create mode 100644 test/multicloud/live/retina-aks/providers.tf
create mode 100644 test/multicloud/live/retina-aks/variables.tf
create mode 100644 test/multicloud/live/retina-gke/.terraform.lock.hcl
create mode 100644 test/multicloud/live/retina-gke/main.tf
create mode 100644 test/multicloud/live/retina-gke/outputs.tf
create mode 100644 test/multicloud/live/retina-gke/providers.tf
create mode 100644 test/multicloud/live/retina-gke/variables.tf
create mode 100644 test/multicloud/live/retina-kind/.terraform.lock.hcl
create mode 100644 test/multicloud/live/retina-kind/main.tf
create mode 100644 test/multicloud/live/retina-kind/providers.tf
create mode 100644 test/multicloud/live/retina-kind/variables.tf
create mode 100644 test/multicloud/modules/aks/main.tf
create mode 100644 test/multicloud/modules/aks/outputs.tf
create mode 100644 test/multicloud/modules/aks/provider.tf
create mode 100644 test/multicloud/modules/aks/variables.tf
create mode 100644 test/multicloud/modules/gke/main.tf
create mode 100644 test/multicloud/modules/gke/output.tf
create mode 100644 test/multicloud/modules/gke/provider.tf
create mode 100644 test/multicloud/modules/gke/variables.tf
create mode 100644 test/multicloud/modules/kind/main.tf
create mode 100644 test/multicloud/modules/kind/output.tf
create mode 100644 test/multicloud/modules/kind/provider.tf
create mode 100644 test/multicloud/modules/kind/variables.tf
create mode 100644 test/multicloud/modules/retina/main.tf
create mode 100644 test/multicloud/modules/retina/provider.tf
create mode 100644 test/multicloud/modules/retina/variables.tf
create mode 100644 test/multicloud/test/example_aks_test.go
create mode 100644 test/multicloud/test/example_gke_test.go
create mode 100644 test/multicloud/test/example_kind_test.go
create mode 100644 test/multicloud/test/go.mod
create mode 100644 test/multicloud/test/go.sum
create mode 100644 test/multicloud/test/integration_retina_kind_test.go
create mode 100644 test/multicloud/test/testUtils.go
diff --git a/.github/workflows/test-multicloud.yml b/.github/workflows/test-multicloud.yml
new file mode 100644
index 0000000000..6281341efb
--- /dev/null
+++ b/.github/workflows/test-multicloud.yml
@@ -0,0 +1,31 @@
+name: Test Multicloud
+
+on:
+ pull_request:
+ paths:
+ - 'test/multicloud/**'
+
+jobs:
+ multicloud-test:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: opentofu/setup-opentofu@v1
+ with:
+ tofu_version: 1.8.3
+
+ - name: Checkout code
+ uses: actions/checkout@v2
+
+ - name: Set up Go
+ uses: actions/setup-go@v2
+ with:
+ go-version: '1.23'
+
+ - name: Install dependencies
+ run: go mod download
+ working-directory: test/multicloud/
+
+ - name: Run tests
+ run: make test
+ working-directory: test/multicloud/
\ No newline at end of file
diff --git a/test/multicloud/.gitignore b/test/multicloud/.gitignore
new file mode 100644
index 0000000000..6f829cf6f9
--- /dev/null
+++ b/test/multicloud/.gitignore
@@ -0,0 +1,6 @@
+terraform.tfvars
+.terraform
+terraform.tfstate
+*terraform.tfstate.*
+service-key.json
+*-kind-config
diff --git a/test/multicloud/Makefile b/test/multicloud/Makefile
new file mode 100644
index 0000000000..715e86ec2f
--- /dev/null
+++ b/test/multicloud/Makefile
@@ -0,0 +1,42 @@
+PREFIX ?= retina
+STACK_NAME ?= $(PREFIX)-aks
+
+.PHONY: init plan apply quick gke aks kind destroy clean kind-kubeconfig test
+
+plan:
+ cd live/$(STACK_NAME) && \
+ tofu fmt && tofu init && tofu plan
+
+apply:
+ cd live/$(STACK_NAME) && \
+ tofu apply --auto-approve
+
+quick:
+ @make plan
+ @make apply
+
+gke: export STACK_NAME=$(PREFIX)-gke
+gke:
+ @make quick
+
+aks: export STACK_NAME=$(PREFIX)-aks
+aks:
+ @make quick
+
+kind: export STACK_NAME=$(PREFIX)-kind
+kind:
+ @make quick
+
+destroy:
+ cd live/$(STACK_NAME) && \
+ tofu destroy --auto-approve
+
+clean: destroy
+ @cd live/$(STACK_NAME) && \
+ rm -rf .terraform && rm terraform.tfstate && rm terraform.tfstate.backup
+
+kind-kubeconfig:
+ @kubectl config set-context live/$(PREFIX)-kind/mc-kind-config
+
+test:
+ @cd test && go test -v -count=1 -timeout 30m ./...
diff --git a/test/multicloud/README.md b/test/multicloud/README.md
new file mode 100644
index 0000000000..f82939a532
--- /dev/null
+++ b/test/multicloud/README.md
@@ -0,0 +1,98 @@
+# Multi Cloud Retina
+
+This project leverages [OpenTofu](https://opentofu.org/docs/intro/) Infrastructure as Code (IaC) to create Kubernetes infrastructure on multi-cloud and deploy [microsoft/retina](https://github.com/microsoft/retina) via Helm provider.
+
+![Architecture Diagram](./diagrams/diagram.svg)
+
+## Modules available
+
+* [aks](./modules/aks/)
+* [gke](./modules/gke/)
+* [kind](./modules/kind/)
+* [retina](./modules/retina/)
+
+## Prerequisites
+
+* [OpenTofu installation guide](https://opentofu.org/docs/intro/install/)
+
+* AKS:
+
+ 1. create an Azure account
+ 2. [Install az](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli)
+
+ To deploy an AKS cluster and install retina, create file `live/retina-aks/terraform.tfvars` with the Azure TenantID and SubscriptionID
+
+ ```sh
+ # example values
+ subscription_id = "d6050d84-e4dd-463d-afc7-a6ab3dc33ab7"
+ tenant_id = "ac8a4ccd-35f1-4f95-a688-f68e3d89adfc"
+ ```
+
+* GKE:
+
+ 1. create a gcloud account, project and enable billing
+ 2. create a service account and service account key
+ 3. [Enable Kubernetes Engine API](https://console.developers.google.com/apis/api/container.googleapis.com/overview?project=mc-retina)
+ 4. [Install gcloud](https://cloud.google.com/sdk/docs/install)
+
+ To deploy a GKE cluster export `GOOGLE_APPLICATION_CREDENTIALS` env variable to point to the path where your [service account key](https://cloud.google.com/iam/docs/keys-create-delete) is located.
+
+ ```sh
+ # example
+ export GOOGLE_APPLICATION_CREDENTIALS=/Users/srodi/src/retina/test/multicloud/live/retina-gke/service-key.json
+ ```
+
+* Kind:
+
+ 1. Docker installed on the host machine
+
+## Quickstart
+
+The following Make targets can be used to manage each stack lifecycle.
+
+### Create
+
+Format code, initialize OpenTofu, plan and apply the stack to create infra and deploy retina
+
+* AKS:
+
+ ```sh
+ make aks
+ ```
+
+* GKE:
+
+ ```sh
+ make gke
+ ```
+
+* Kind:
+
+ ```sh
+ make kind
+ ```
+
+### Clean up
+
+To destroy the cluster specify the `STACK_NAME` and run `make clean`.
+
+```sh
+# destroy AKS and cleanup local state files
+# set a different stack as needed (i.e. retina-gke, retina-kind)
+export STACK_NAME=retina-aks
+make clean
+```
+
+### Test
+
+The test framework is levergaing Go and [Terratest](https://terratest.gruntwork.io/docs/). To run tests:
+
+```sh
+make test
+```
+
+## Providers references
+
+* [GKE resource documentation](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/container_cluster)
+* [AKS resource documentation](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/kubernetes_cluster)
+* [Kind resource documentation](https://registry.terraform.io/providers/tehcyx/kind/latest/docs/resources/cluster)
diff --git a/test/multicloud/diagrams/diagram.excalidraw b/test/multicloud/diagrams/diagram.excalidraw
new file mode 100644
index 0000000000..8049513551
--- /dev/null
+++ b/test/multicloud/diagrams/diagram.excalidraw
@@ -0,0 +1,3366 @@
+{
+ "type": "excalidraw",
+ "version": 2,
+ "source": "https://excalidraw.com",
+ "elements": [
+ {
+ "id": "zghIcLCzAMCMHoPa6KCP5",
+ "type": "rectangle",
+ "x": 919,
+ "y": 483,
+ "width": 286,
+ "height": 426,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#ffec99",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "Zw",
+ "roundness": {
+ "type": 3
+ },
+ "seed": 618936138,
+ "version": 144,
+ "versionNonce": 1644653718,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "id": "o_HSEmb0pecqRgBJDEBRp",
+ "type": "arrow"
+ },
+ {
+ "id": "RBNxQ4pEgxmBXnHdIaBf9",
+ "type": "arrow"
+ },
+ {
+ "id": "xhoCMjoiXLgPLJUmc7WuE",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1737971092328,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "0S628Xmv8YIAFSDx8-05e",
+ "type": "rectangle",
+ "x": 1343,
+ "y": 443,
+ "width": 311,
+ "height": 503,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#a5d8ff",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "Zx",
+ "roundness": {
+ "type": 3
+ },
+ "seed": 1054405910,
+ "version": 48,
+ "versionNonce": 564948438,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "id": "HYF4G_hkcZFNdgAl0YQNb",
+ "type": "arrow"
+ },
+ {
+ "id": "-4dHrHQZE2TDSvs-vgcAo",
+ "type": "arrow"
+ },
+ {
+ "id": "o_HSEmb0pecqRgBJDEBRp",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1737971037967,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "uLVKBxcndIG-qzR2NQnMF",
+ "type": "rectangle",
+ "x": 1854.5,
+ "y": 915.5,
+ "width": 311,
+ "height": 365.00000000000006,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#b2f2bb",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "Zy",
+ "roundness": {
+ "type": 3
+ },
+ "seed": 1560720842,
+ "version": 336,
+ "versionNonce": 747983318,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "id": "-4dHrHQZE2TDSvs-vgcAo",
+ "type": "arrow"
+ },
+ {
+ "id": "RBNxQ4pEgxmBXnHdIaBf9",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1737971057148,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "R4AeEioM2INhMP9rnYnHe",
+ "type": "rectangle",
+ "x": 1856.5,
+ "y": 347.5,
+ "width": 311,
+ "height": 503,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#ffc9c9",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "Zz",
+ "roundness": {
+ "type": 3
+ },
+ "seed": 719699734,
+ "version": 234,
+ "versionNonce": 37331222,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "id": "r_IiMCnJET5n-m5aE4N6J",
+ "type": "arrow"
+ },
+ {
+ "id": "HYF4G_hkcZFNdgAl0YQNb",
+ "type": "arrow"
+ },
+ {
+ "id": "xhoCMjoiXLgPLJUmc7WuE",
+ "type": "arrow"
+ },
+ {
+ "id": "1G4RvWl5j3Eq0orLwRNb1",
+ "type": "arrow"
+ },
+ {
+ "id": "JenUnqPQU9n29FUsRjstV",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1737971550725,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "zXqDMrjv50TU6s8f0xi2s",
+ "type": "rectangle",
+ "x": 1847.078125,
+ "y": -36,
+ "width": 231.859375,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b0y",
+ "roundness": null,
+ "seed": 886375254,
+ "version": 125,
+ "versionNonce": 425608714,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "Eh9WaurLCaoIoY157lFTc"
+ },
+ {
+ "id": "sSBpGKS5X6pi-Hat4HUBJ",
+ "type": "arrow"
+ },
+ {
+ "id": "WlzkJ1a1P1Er7kfA1RP4T",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1737971176481,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "O4E1OnJgXZMVXYZqcHbbf",
+ "type": "rectangle",
+ "x": 1881.3203125,
+ "y": 58,
+ "width": 163.375,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b0z",
+ "roundness": null,
+ "seed": 558246038,
+ "version": 234,
+ "versionNonce": 1718596042,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "iafWgom0hD1fD-mok9szu"
+ },
+ {
+ "id": "sSBpGKS5X6pi-Hat4HUBJ",
+ "type": "arrow"
+ },
+ {
+ "id": "TxfM-J3QLXhdIlqgtcSp3",
+ "type": "arrow"
+ },
+ {
+ "id": "r_IiMCnJET5n-m5aE4N6J",
+ "type": "arrow"
+ },
+ {
+ "id": "J_prQC5yc7ywuZigF71Wh",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1737971184547,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "ysLRakRFsF7DlEwBVIfpv",
+ "type": "rectangle",
+ "x": 2444.4140625,
+ "y": 74,
+ "width": 225.65625,
+ "height": 60,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b12",
+ "roundness": null,
+ "seed": 1464182870,
+ "version": 847,
+ "versionNonce": 1974440970,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "UoF1VeVtOBF-B_pHGbkiD"
+ },
+ {
+ "id": "WlzkJ1a1P1Er7kfA1RP4T",
+ "type": "arrow"
+ },
+ {
+ "id": "EL7fKQS2Z0a_heFdNlDpe",
+ "type": "arrow"
+ },
+ {
+ "id": "-FO1qNsJmZSIC7MG10S17",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1737971476328,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "9pGuk9pLc645Jva07NwPP",
+ "type": "rectangle",
+ "x": 1417.08984375,
+ "y": 503,
+ "width": 143.453125,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b13",
+ "roundness": null,
+ "seed": 1482304918,
+ "version": 54,
+ "versionNonce": 1221402454,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "n6fgzgLn1UV2wrjYVoVDr"
+ }
+ ],
+ "updated": 1737970947812,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "nlTAqIR7zMcS6PGUnUiPG",
+ "type": "rectangle",
+ "x": 1420.06640625,
+ "y": 704,
+ "width": 154.921875,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b14",
+ "roundness": null,
+ "seed": 1730939606,
+ "version": 93,
+ "versionNonce": 1874844694,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "RK06UCyMyn_5XDjMl8d_D"
+ }
+ ],
+ "updated": 1737970950262,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "fz7IX_5gQKgv9w7KWCF9F",
+ "type": "rectangle",
+ "x": 1415.05078125,
+ "y": 796,
+ "width": 173.296875,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b15",
+ "roundness": null,
+ "seed": 307249174,
+ "version": 137,
+ "versionNonce": 234210442,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "kgSbIGb4Oi7kNTwp2QkrU"
+ }
+ ],
+ "updated": 1737970951221,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "oKOWo7KV9Y8J3AQR1lX-P",
+ "type": "rectangle",
+ "x": 989.09765625,
+ "y": 805,
+ "width": 153.20312500000003,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b1C",
+ "roundness": null,
+ "seed": 2062767318,
+ "version": 226,
+ "versionNonce": 700710410,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "YBOf_ZI3--rqMICNXDl5o"
+ }
+ ],
+ "updated": 1737970946099,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "xPpUhfUnNbsMPlojZlr0W",
+ "type": "rectangle",
+ "x": 980.1328125,
+ "y": 543,
+ "width": 162.078125,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [
+ "klCQzqbRxuqhJ44QC9Oku"
+ ],
+ "frameId": null,
+ "index": "b1E",
+ "roundness": null,
+ "seed": 1538824022,
+ "version": 267,
+ "versionNonce": 763874250,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "03ZfRAoP-dkIsVdwuMcGm"
+ }
+ ],
+ "updated": 1737970941082,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "sSBpGKS5X6pi-Hat4HUBJ",
+ "type": "arrow",
+ "x": 1963.00803125,
+ "y": 8.5,
+ "width": 0,
+ "height": 43.699999999999996,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b1F",
+ "roundness": {
+ "type": 2
+ },
+ "seed": 1318534294,
+ "version": 467,
+ "versionNonce": 2147339786,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737971184547,
+ "link": null,
+ "locked": false,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 43.699999999999996
+ ]
+ ],
+ "lastCommittedPoint": null,
+ "startBinding": {
+ "elementId": "zXqDMrjv50TU6s8f0xi2s",
+ "focus": -0.000001886919603150646,
+ "gap": 1,
+ "fixedPoint": null
+ },
+ "endBinding": {
+ "elementId": "O4E1OnJgXZMVXYZqcHbbf",
+ "focus": 0.0000026778882929564303,
+ "gap": 5.799999999999997,
+ "fixedPoint": null
+ },
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "elbowed": false
+ },
+ {
+ "id": "Eh9WaurLCaoIoY157lFTc",
+ "type": "text",
+ "x": 1869.5478744506836,
+ "y": -26.5,
+ "width": 186.9198760986328,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b1Z",
+ "roundness": null,
+ "seed": 570447254,
+ "version": 124,
+ "versionNonce": 1946965270,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737970467013,
+ "link": null,
+ "locked": false,
+ "text": "Multi-Cloud Project",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "zXqDMrjv50TU6s8f0xi2s",
+ "originalText": "Multi-Cloud Project",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "iafWgom0hD1fD-mok9szu",
+ "type": "text",
+ "x": 1893.917854309082,
+ "y": 67.5,
+ "width": 138.17991638183594,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b1a",
+ "roundness": null,
+ "seed": 1775055574,
+ "version": 225,
+ "versionNonce": 1441458314,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737971184547,
+ "link": null,
+ "locked": false,
+ "text": "OpenTofu IaC",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "O4E1OnJgXZMVXYZqcHbbf",
+ "originalText": "OpenTofu IaC",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "UoF1VeVtOBF-B_pHGbkiD",
+ "type": "text",
+ "x": 2499.9722213745117,
+ "y": 79,
+ "width": 114.53993225097656,
+ "height": 50,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b1d",
+ "roundness": null,
+ "seed": 404085398,
+ "version": 838,
+ "versionNonce": 911934358,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737971223954,
+ "link": null,
+ "locked": false,
+ "text": "GO Testing\nFramework",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "ysLRakRFsF7DlEwBVIfpv",
+ "originalText": "GO Testing Framework",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "n6fgzgLn1UV2wrjYVoVDr",
+ "type": "text",
+ "x": 1465.7064208984375,
+ "y": 512.5,
+ "width": 46.219970703125,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b1e",
+ "roundness": null,
+ "seed": 1827280854,
+ "version": 48,
+ "versionNonce": 2005438678,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737970219802,
+ "link": null,
+ "locked": false,
+ "text": "AKS ",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "9pGuk9pLc645Jva07NwPP",
+ "originalText": "AKS ",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "RK06UCyMyn_5XDjMl8d_D",
+ "type": "text",
+ "x": 1471.6273498535156,
+ "y": 713.5,
+ "width": 51.79998779296875,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b1f",
+ "roundness": null,
+ "seed": 784641302,
+ "version": 94,
+ "versionNonce": 1999275926,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737970228691,
+ "link": null,
+ "locked": false,
+ "text": "KIND",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "nlTAqIR7zMcS6PGUnUiPG",
+ "originalText": "KIND",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "kgSbIGb4Oi7kNTwp2QkrU",
+ "type": "text",
+ "x": 1469.239242553711,
+ "y": 805.5,
+ "width": 64.91995239257812,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b1g",
+ "roundness": null,
+ "seed": 454728278,
+ "version": 136,
+ "versionNonce": 1384911050,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737970238236,
+ "link": null,
+ "locked": false,
+ "text": "Retina",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "fz7IX_5gQKgv9w7KWCF9F",
+ "originalText": "Retina",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "YBOf_ZI3--rqMICNXDl5o",
+ "type": "text",
+ "x": 1041.7192306518555,
+ "y": 814.5,
+ "width": 47.95997619628906,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b1n",
+ "roundness": null,
+ "seed": 1108719382,
+ "version": 222,
+ "versionNonce": 1030092746,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737970255474,
+ "link": null,
+ "locked": false,
+ "text": "Helm ",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "oKOWo7KV9Y8J3AQR1lX-P",
+ "originalText": "Helm ",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "03ZfRAoP-dkIsVdwuMcGm",
+ "type": "text",
+ "x": 1033.291893005371,
+ "y": 552.5,
+ "width": 55.75996398925781,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [
+ "klCQzqbRxuqhJ44QC9Oku"
+ ],
+ "frameId": null,
+ "index": "b1p",
+ "roundness": null,
+ "seed": 2128046486,
+ "version": 267,
+ "versionNonce": 1559104842,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737970255474,
+ "link": null,
+ "locked": false,
+ "text": "Azure",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "xPpUhfUnNbsMPlojZlr0W",
+ "originalText": "Azure",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "U6X68vlOQPsistV7KGQj7",
+ "type": "rectangle",
+ "x": 982.9609375,
+ "y": 716,
+ "width": 162.078125,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [
+ "8IJVdH1tDYshWtx-RzqUy"
+ ],
+ "frameId": null,
+ "index": "b1u",
+ "roundness": null,
+ "seed": 1066099146,
+ "version": 321,
+ "versionNonce": 1510947478,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "n5AzOpyhjDk4cJALwlppo"
+ }
+ ],
+ "updated": 1737970944829,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "n5AzOpyhjDk4cJALwlppo",
+ "type": "text",
+ "x": 1038.1000061035156,
+ "y": 725.5,
+ "width": 51.79998779296875,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [
+ "8IJVdH1tDYshWtx-RzqUy"
+ ],
+ "frameId": null,
+ "index": "b1v",
+ "roundness": null,
+ "seed": 1880135818,
+ "version": 337,
+ "versionNonce": 110019274,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737970255474,
+ "link": null,
+ "locked": false,
+ "text": "KIND",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "U6X68vlOQPsistV7KGQj7",
+ "originalText": "KIND",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "6vHcegO-bhhAWthKg5DLF",
+ "type": "rectangle",
+ "x": 982.9609375,
+ "y": 632,
+ "width": 162.078125,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [
+ "dBEY2fEZd5U8JDWibmeLQ"
+ ],
+ "frameId": null,
+ "index": "b1w",
+ "roundness": null,
+ "seed": 2042518742,
+ "version": 322,
+ "versionNonce": 2099680970,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "7FCUVEMtLqp4KBg0vB5g0"
+ }
+ ],
+ "updated": 1737970943673,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "7FCUVEMtLqp4KBg0vB5g0",
+ "type": "text",
+ "x": 1000.5100250244141,
+ "y": 641.5,
+ "width": 126.97994995117188,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [
+ "dBEY2fEZd5U8JDWibmeLQ"
+ ],
+ "frameId": null,
+ "index": "b1x",
+ "roundness": null,
+ "seed": 46682646,
+ "version": 338,
+ "versionNonce": 1151342666,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737970255474,
+ "link": null,
+ "locked": false,
+ "text": "Google Cloud",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "6vHcegO-bhhAWthKg5DLF",
+ "originalText": "Google Cloud",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "cvMVx90aAz8Snv2pIMyEP",
+ "type": "rectangle",
+ "x": 1422.2734375,
+ "y": 610,
+ "width": 143.453125,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b1y",
+ "roundness": null,
+ "seed": 504440010,
+ "version": 148,
+ "versionNonce": 1612320074,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "7x5KHRGKh6towsi_8LYpB"
+ }
+ ],
+ "updated": 1737970949157,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "7x5KHRGKh6towsi_8LYpB",
+ "type": "text",
+ "x": 1472.5700073242188,
+ "y": 619.5,
+ "width": 42.8599853515625,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b1z",
+ "roundness": null,
+ "seed": 518388618,
+ "version": 144,
+ "versionNonce": 1811483594,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737970242587,
+ "link": null,
+ "locked": false,
+ "text": "GKE",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "cvMVx90aAz8Snv2pIMyEP",
+ "originalText": "GKE",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "w-30O_eesbT44MY2r9VIi",
+ "type": "text",
+ "x": 1544,
+ "y": 455,
+ "width": 76.61996459960938,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b21",
+ "roundness": null,
+ "seed": 274873098,
+ "version": 33,
+ "versionNonce": 344674902,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "id": "TxfM-J3QLXhdIlqgtcSp3",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1737970326763,
+ "link": null,
+ "locked": false,
+ "text": "Modules",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Modules",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "A19y-iFjO8jPxtueSWwhh",
+ "type": "text",
+ "x": 1090,
+ "y": 497,
+ "width": 92.65994262695312,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b23",
+ "roundness": null,
+ "seed": 1289638218,
+ "version": 41,
+ "versionNonce": 1506505930,
+ "isDeleted": false,
+ "boundElements": null,
+ "updated": 1737970297494,
+ "link": null,
+ "locked": false,
+ "text": "Providers",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Providers",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "TxfM-J3QLXhdIlqgtcSp3",
+ "type": "arrow",
+ "x": 1971.3696285377284,
+ "y": 103,
+ "width": 395.36962853772843,
+ "height": 341,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b28",
+ "roundness": {
+ "type": 2
+ },
+ "seed": 1369408406,
+ "version": 163,
+ "versionNonce": 1645571990,
+ "isDeleted": false,
+ "boundElements": null,
+ "updated": 1737971184547,
+ "link": null,
+ "locked": false,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -395.36962853772843,
+ 341
+ ]
+ ],
+ "lastCommittedPoint": null,
+ "startBinding": {
+ "elementId": "O4E1OnJgXZMVXYZqcHbbf",
+ "focus": -0.32677741038380365,
+ "gap": 1,
+ "fixedPoint": null
+ },
+ "endBinding": {
+ "elementId": "w-30O_eesbT44MY2r9VIi",
+ "focus": -0.8955844703788051,
+ "gap": 11,
+ "fixedPoint": null
+ },
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "elbowed": false
+ },
+ {
+ "id": "8AUKqnBI6Ff1R4wt0slqa",
+ "type": "rectangle",
+ "x": 1930.58984375,
+ "y": 407.5,
+ "width": 143.453125,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b29",
+ "roundness": null,
+ "seed": 1981592854,
+ "version": 231,
+ "versionNonce": 1503908374,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "eoIm4zi0FVKJOECWWL7TP"
+ }
+ ],
+ "updated": 1737970947812,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "Rg3n2f_ms7-kJ94iZVPrE",
+ "type": "rectangle",
+ "x": 1933.56640625,
+ "y": 608.5,
+ "width": 154.921875,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2A",
+ "roundness": null,
+ "seed": 1244766806,
+ "version": 270,
+ "versionNonce": 1096754902,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "k5JMWXVEMnO8AA6zcZaht"
+ }
+ ],
+ "updated": 1737970950262,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "cK83p_ezogmd5Hf_eZwz2",
+ "type": "rectangle",
+ "x": 1928.55078125,
+ "y": 700.5,
+ "width": 173.296875,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2B",
+ "roundness": null,
+ "seed": 1068951446,
+ "version": 314,
+ "versionNonce": 497421770,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "7ok5BbnOLbhPq6AgN4GYB"
+ }
+ ],
+ "updated": 1737970951221,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "eoIm4zi0FVKJOECWWL7TP",
+ "type": "text",
+ "x": 1979.2064208984375,
+ "y": 417,
+ "width": 46.219970703125,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2C",
+ "roundness": null,
+ "seed": 1994491094,
+ "version": 226,
+ "versionNonce": 1844995478,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737970406988,
+ "link": null,
+ "locked": false,
+ "text": "AKS ",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "8AUKqnBI6Ff1R4wt0slqa",
+ "originalText": "AKS ",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "k5JMWXVEMnO8AA6zcZaht",
+ "type": "text",
+ "x": 1985.1273498535156,
+ "y": 618,
+ "width": 51.79998779296875,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2D",
+ "roundness": null,
+ "seed": 1095137814,
+ "version": 272,
+ "versionNonce": 543491414,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737970406988,
+ "link": null,
+ "locked": false,
+ "text": "KIND",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "Rg3n2f_ms7-kJ94iZVPrE",
+ "originalText": "KIND",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "7ok5BbnOLbhPq6AgN4GYB",
+ "type": "text",
+ "x": 1982.739242553711,
+ "y": 710,
+ "width": 64.91995239257812,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2E",
+ "roundness": null,
+ "seed": 1977272150,
+ "version": 314,
+ "versionNonce": 1165016342,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737970406988,
+ "link": null,
+ "locked": false,
+ "text": "Retina",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "cK83p_ezogmd5Hf_eZwz2",
+ "originalText": "Retina",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "BFUrWgvUgo1Tm7uLzxqJb",
+ "type": "rectangle",
+ "x": 1935.7734375,
+ "y": 514.5,
+ "width": 143.453125,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2F",
+ "roundness": null,
+ "seed": 1156803734,
+ "version": 325,
+ "versionNonce": 987000458,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "q3iv3crLU6YUmmSjnjJ7r"
+ }
+ ],
+ "updated": 1737970949157,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "q3iv3crLU6YUmmSjnjJ7r",
+ "type": "text",
+ "x": 1986.0700073242188,
+ "y": 524,
+ "width": 42.8599853515625,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2G",
+ "roundness": null,
+ "seed": 205875670,
+ "version": 322,
+ "versionNonce": 995265750,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737970406989,
+ "link": null,
+ "locked": false,
+ "text": "GKE",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "BFUrWgvUgo1Tm7uLzxqJb",
+ "originalText": "GKE",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "0zdoqgDRvg-ebTICP9SFX",
+ "type": "text",
+ "x": 2057.5,
+ "y": 359.5,
+ "width": 87.31996154785156,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2I",
+ "roundness": null,
+ "seed": 1430875222,
+ "version": 219,
+ "versionNonce": 1470198934,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737970406989,
+ "link": null,
+ "locked": false,
+ "text": "Examples",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Examples",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "2YGILj7S2n9PeADdx2TMY",
+ "type": "rectangle",
+ "x": 1928.58984375,
+ "y": 975.5,
+ "width": 143.453125,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2N",
+ "roundness": null,
+ "seed": 739942346,
+ "version": 263,
+ "versionNonce": 2047805078,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "A_JFPEAqBW9EPietZYM8a"
+ }
+ ],
+ "updated": 1737970455777,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "mIlz4x0AjaordLAqI-DWY",
+ "type": "rectangle",
+ "x": 1931.56640625,
+ "y": 1176.5,
+ "width": 154.921875,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2O",
+ "roundness": null,
+ "seed": 1141380746,
+ "version": 302,
+ "versionNonce": 761452822,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "uv0MN_ZBtwcWuq7WETscI"
+ }
+ ],
+ "updated": 1737970455778,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "A_JFPEAqBW9EPietZYM8a",
+ "type": "text",
+ "x": 1944.996452331543,
+ "y": 985,
+ "width": 110.63990783691406,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2Q",
+ "roundness": null,
+ "seed": 791116810,
+ "version": 269,
+ "versionNonce": 249414614,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737970455778,
+ "link": null,
+ "locked": false,
+ "text": "Retina-AKS",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "2YGILj7S2n9PeADdx2TMY",
+ "originalText": "Retina-AKS",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "uv0MN_ZBtwcWuq7WETscI",
+ "type": "text",
+ "x": 1946.7973861694336,
+ "y": 1186,
+ "width": 124.45991516113281,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2R",
+ "roundness": null,
+ "seed": 1102015178,
+ "version": 318,
+ "versionNonce": 2098415190,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737970455778,
+ "link": null,
+ "locked": false,
+ "text": "Retina-KIND",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "mIlz4x0AjaordLAqI-DWY",
+ "originalText": "Retina-KIND",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "y5OM8zL9aH4eV1vQRfRA8",
+ "type": "rectangle",
+ "x": 1933.7734375,
+ "y": 1082.5,
+ "width": 143.453125,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2T",
+ "roundness": null,
+ "seed": 1572257866,
+ "version": 357,
+ "versionNonce": 1858341782,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "cgV-AqaregAAVrxZQQPP7"
+ }
+ ],
+ "updated": 1737970455778,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "cgV-AqaregAAVrxZQQPP7",
+ "type": "text",
+ "x": 1948.2400436401367,
+ "y": 1092,
+ "width": 114.51991271972656,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2U",
+ "roundness": null,
+ "seed": 508307210,
+ "version": 365,
+ "versionNonce": 279587030,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737970455778,
+ "link": null,
+ "locked": false,
+ "text": "Retina-GKE",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "y5OM8zL9aH4eV1vQRfRA8",
+ "originalText": "Retina-GKE",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "CDhoAiwZsuutyGpxSBtr1",
+ "type": "text",
+ "x": 2055.5,
+ "y": 927.5,
+ "width": 37.23997497558594,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2W",
+ "roundness": null,
+ "seed": 1795255434,
+ "version": 257,
+ "versionNonce": 80994134,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "id": "J_prQC5yc7ywuZigF71Wh",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1737970481192,
+ "link": null,
+ "locked": false,
+ "text": "Live",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Live",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "r_IiMCnJET5n-m5aE4N6J",
+ "type": "arrow",
+ "x": 1972,
+ "y": 107,
+ "width": 6.9854166666666515,
+ "height": 239.5,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2Y",
+ "roundness": {
+ "type": 2
+ },
+ "seed": 1848447382,
+ "version": 149,
+ "versionNonce": 132873418,
+ "isDeleted": false,
+ "boundElements": null,
+ "updated": 1737971184548,
+ "link": null,
+ "locked": false,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 6.9854166666666515,
+ 239.5
+ ]
+ ],
+ "lastCommittedPoint": null,
+ "startBinding": {
+ "elementId": "O4E1OnJgXZMVXYZqcHbbf",
+ "focus": -0.09965711827521635,
+ "gap": 5,
+ "fixedPoint": null
+ },
+ "endBinding": {
+ "elementId": "R4AeEioM2INhMP9rnYnHe",
+ "focus": -0.15752101431660293,
+ "gap": 1,
+ "fixedPoint": null
+ },
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "elbowed": false
+ },
+ {
+ "id": "J_prQC5yc7ywuZigF71Wh",
+ "type": "arrow",
+ "x": 1973,
+ "y": 106.99999999999989,
+ "width": 388,
+ "height": 812.0000000000001,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2Z",
+ "roundness": {
+ "type": 2
+ },
+ "seed": 26578774,
+ "version": 401,
+ "versionNonce": 876832714,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737971284718,
+ "link": null,
+ "locked": false,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 388,
+ 564.0000000000001
+ ],
+ [
+ 84,
+ 812.0000000000001
+ ]
+ ],
+ "lastCommittedPoint": null,
+ "startBinding": {
+ "elementId": "O4E1OnJgXZMVXYZqcHbbf",
+ "focus": 0.0886395475001259,
+ "gap": 5,
+ "fixedPoint": null
+ },
+ "endBinding": {
+ "elementId": "CDhoAiwZsuutyGpxSBtr1",
+ "focus": -1.262777842966161,
+ "gap": 8.5,
+ "fixedPoint": null
+ },
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "elbowed": false
+ },
+ {
+ "id": "HYF4G_hkcZFNdgAl0YQNb",
+ "type": "arrow",
+ "x": 1858,
+ "y": 607,
+ "width": 207,
+ "height": 5,
+ "angle": 0,
+ "strokeColor": "#1971c2",
+ "backgroundColor": "#a5d8ff",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2a",
+ "roundness": {
+ "type": 2
+ },
+ "seed": 1979137814,
+ "version": 57,
+ "versionNonce": 1112572054,
+ "isDeleted": false,
+ "boundElements": null,
+ "updated": 1737971124821,
+ "link": null,
+ "locked": false,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -207,
+ 5
+ ]
+ ],
+ "lastCommittedPoint": null,
+ "startBinding": {
+ "elementId": "R4AeEioM2INhMP9rnYnHe",
+ "focus": -0.01676823498239903,
+ "gap": 1,
+ "fixedPoint": null
+ },
+ "endBinding": {
+ "elementId": "0S628Xmv8YIAFSDx8-05e",
+ "focus": -0.3087739884174268,
+ "gap": 1,
+ "fixedPoint": null
+ },
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "elbowed": false
+ },
+ {
+ "id": "nm5O_cFaqDOe1J9pTEhmu",
+ "type": "text",
+ "x": 1708,
+ "y": 574,
+ "width": 125.63990783691406,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1971c2",
+ "backgroundColor": "#a5d8ff",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2b",
+ "roundness": null,
+ "seed": 183796746,
+ "version": 85,
+ "versionNonce": 62722378,
+ "isDeleted": false,
+ "boundElements": null,
+ "updated": 1737971128309,
+ "link": null,
+ "locked": false,
+ "text": "Instantiates",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Instantiates",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "VBZOWyP-GhRCxXcLTHwB_",
+ "type": "text",
+ "x": 1657.180046081543,
+ "y": 1047.5,
+ "width": 125.63990783691406,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1971c2",
+ "backgroundColor": "#a5d8ff",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2c",
+ "roundness": null,
+ "seed": 739676554,
+ "version": 124,
+ "versionNonce": 2038259082,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737971132189,
+ "link": null,
+ "locked": false,
+ "text": "Instantiates",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Instantiates",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "-4dHrHQZE2TDSvs-vgcAo",
+ "type": "arrow",
+ "x": 1853,
+ "y": 1104,
+ "width": 200,
+ "height": 187,
+ "angle": 0,
+ "strokeColor": "#1971c2",
+ "backgroundColor": "#a5d8ff",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2d",
+ "roundness": {
+ "type": 2
+ },
+ "seed": 471021322,
+ "version": 35,
+ "versionNonce": 2134311690,
+ "isDeleted": false,
+ "boundElements": null,
+ "updated": 1737971136385,
+ "link": null,
+ "locked": false,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -200,
+ -187
+ ]
+ ],
+ "lastCommittedPoint": null,
+ "startBinding": {
+ "elementId": "uLVKBxcndIG-qzR2NQnMF",
+ "focus": -0.4659911403889994,
+ "gap": 1.5,
+ "fixedPoint": null
+ },
+ "endBinding": {
+ "elementId": "0S628Xmv8YIAFSDx8-05e",
+ "focus": 0.19663384921609758,
+ "gap": 1,
+ "fixedPoint": null
+ },
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "elbowed": false
+ },
+ {
+ "id": "o_HSEmb0pecqRgBJDEBRp",
+ "type": "arrow",
+ "x": 1343,
+ "y": 673,
+ "width": 136,
+ "height": 4,
+ "angle": 0,
+ "strokeColor": "#f08c00",
+ "backgroundColor": "#a5d8ff",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2e",
+ "roundness": {
+ "type": 2
+ },
+ "seed": 144078154,
+ "version": 29,
+ "versionNonce": 1175139926,
+ "isDeleted": false,
+ "boundElements": null,
+ "updated": 1737971114288,
+ "link": null,
+ "locked": false,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -136,
+ 4
+ ]
+ ],
+ "lastCommittedPoint": null,
+ "startBinding": {
+ "elementId": "0S628Xmv8YIAFSDx8-05e",
+ "focus": 0.10182047895250676,
+ "gap": 1,
+ "fixedPoint": null
+ },
+ "endBinding": {
+ "elementId": "zghIcLCzAMCMHoPa6KCP5",
+ "focus": -0.06784021665538253,
+ "gap": 2,
+ "fixedPoint": null
+ },
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "elbowed": false
+ },
+ {
+ "id": "13azSEVgK_dcdndesOJU4",
+ "type": "text",
+ "x": 1271,
+ "y": 644,
+ "width": 47.19996643066406,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#f08c00",
+ "backgroundColor": "#a5d8ff",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2f",
+ "roundness": null,
+ "seed": 1546120586,
+ "version": 7,
+ "versionNonce": 591726474,
+ "isDeleted": false,
+ "boundElements": null,
+ "updated": 1737971117472,
+ "link": null,
+ "locked": false,
+ "text": "Uses",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Uses",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "JMh5UJvRSjgcfPdc_YCR7",
+ "type": "text",
+ "x": 1597.400016784668,
+ "y": 1134.5,
+ "width": 47.19996643066406,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#f08c00",
+ "backgroundColor": "#a5d8ff",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2g",
+ "roundness": null,
+ "seed": 1271206422,
+ "version": 3,
+ "versionNonce": 1160150,
+ "isDeleted": false,
+ "boundElements": null,
+ "updated": 1737971110710,
+ "link": null,
+ "locked": false,
+ "text": "Uses",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Uses",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "RBNxQ4pEgxmBXnHdIaBf9",
+ "type": "arrow",
+ "x": 1854,
+ "y": 1110,
+ "width": 758,
+ "height": 242,
+ "angle": 0,
+ "strokeColor": "#f08c00",
+ "backgroundColor": "#a5d8ff",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2h",
+ "roundness": {
+ "type": 2
+ },
+ "seed": 2027055370,
+ "version": 185,
+ "versionNonce": 450718806,
+ "isDeleted": false,
+ "boundElements": null,
+ "updated": 1737971102355,
+ "link": null,
+ "locked": false,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -326,
+ 51
+ ],
+ [
+ -758,
+ -191
+ ]
+ ],
+ "lastCommittedPoint": null,
+ "startBinding": {
+ "elementId": "uLVKBxcndIG-qzR2NQnMF",
+ "focus": 0.05997730828840719,
+ "gap": 1,
+ "fixedPoint": null
+ },
+ "endBinding": {
+ "elementId": "zghIcLCzAMCMHoPa6KCP5",
+ "focus": 0.6958348470250035,
+ "gap": 10,
+ "fixedPoint": null
+ },
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "elbowed": false
+ },
+ {
+ "id": "k-K-sqJPwGdOAbF7FgGcz",
+ "type": "text",
+ "x": 1512.400016784668,
+ "y": 262.5,
+ "width": 47.19996643066406,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#f08c00",
+ "backgroundColor": "#a5d8ff",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2i",
+ "roundness": null,
+ "seed": 1588338186,
+ "version": 3,
+ "versionNonce": 1246659606,
+ "isDeleted": false,
+ "boundElements": null,
+ "updated": 1737971106415,
+ "link": null,
+ "locked": false,
+ "text": "Uses",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Uses",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "xhoCMjoiXLgPLJUmc7WuE",
+ "type": "arrow",
+ "x": 1858,
+ "y": 605,
+ "width": 795,
+ "height": 311,
+ "angle": 0,
+ "strokeColor": "#f08c00",
+ "backgroundColor": "#a5d8ff",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2j",
+ "roundness": {
+ "type": 2
+ },
+ "seed": 1794620630,
+ "version": 236,
+ "versionNonce": 489375370,
+ "isDeleted": false,
+ "boundElements": null,
+ "updated": 1737971095964,
+ "link": null,
+ "locked": false,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -277,
+ -311
+ ],
+ [
+ -795,
+ -122
+ ]
+ ],
+ "lastCommittedPoint": null,
+ "startBinding": {
+ "elementId": "R4AeEioM2INhMP9rnYnHe",
+ "focus": -0.4198735871757071,
+ "gap": 1,
+ "fixedPoint": null
+ },
+ "endBinding": {
+ "elementId": "zghIcLCzAMCMHoPa6KCP5",
+ "focus": -0.8018651582326861,
+ "gap": 1,
+ "fixedPoint": null
+ },
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "elbowed": false
+ },
+ {
+ "id": "WlzkJ1a1P1Er7kfA1RP4T",
+ "type": "arrow",
+ "x": 1964,
+ "y": 9,
+ "width": 474,
+ "height": 84.63600506869611,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "#ffec99",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2k",
+ "roundness": {
+ "type": 2
+ },
+ "seed": 217245642,
+ "version": 192,
+ "versionNonce": 227064650,
+ "isDeleted": false,
+ "boundElements": null,
+ "updated": 1737971481423,
+ "link": null,
+ "locked": false,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 249,
+ 24
+ ],
+ [
+ 474,
+ 84.63600506869611
+ ]
+ ],
+ "lastCommittedPoint": null,
+ "startBinding": {
+ "elementId": "zXqDMrjv50TU6s8f0xi2s",
+ "focus": 0.6904324140279195,
+ "gap": 1,
+ "fixedPoint": null
+ },
+ "endBinding": {
+ "elementId": "ysLRakRFsF7DlEwBVIfpv",
+ "focus": -0.3604083592296213,
+ "gap": 6.4140625,
+ "fixedPoint": null
+ },
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "elbowed": false
+ },
+ {
+ "id": "0jJYOpQUuReGklXS3eCHd",
+ "type": "rectangle",
+ "x": 2433.5,
+ "y": 357.5,
+ "width": 311,
+ "height": 503,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2p",
+ "roundness": {
+ "type": 3
+ },
+ "seed": 507557578,
+ "version": 387,
+ "versionNonce": 1340323542,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "id": "EL7fKQS2Z0a_heFdNlDpe",
+ "type": "arrow"
+ },
+ {
+ "id": "1G4RvWl5j3Eq0orLwRNb1",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1737971510934,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "yXs1C5HAV_quiAwVGmj9-",
+ "type": "rectangle",
+ "x": 2507.58984375,
+ "y": 417.5,
+ "width": 143.453125,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2q",
+ "roundness": null,
+ "seed": 1465077130,
+ "version": 383,
+ "versionNonce": 1531846538,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "c-HWcR62JO098FZH-Exyy"
+ }
+ ],
+ "updated": 1737971303713,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "XKA3gCRYDhwXJMOPsnTYe",
+ "type": "rectangle",
+ "x": 2510.56640625,
+ "y": 618.5,
+ "width": 154.921875,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2r",
+ "roundness": null,
+ "seed": 1734675530,
+ "version": 422,
+ "versionNonce": 289359114,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "UPzd8AvyIY1ANthQMQL-2"
+ }
+ ],
+ "updated": 1737971303713,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "sVoinvy16hzPyLe8OLqLD",
+ "type": "rectangle",
+ "x": 2505.55078125,
+ "y": 710.5,
+ "width": 173.296875,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2s",
+ "roundness": null,
+ "seed": 1951462154,
+ "version": 466,
+ "versionNonce": 1984236170,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "r_UUPiappL6TFrgWoMu3T"
+ }
+ ],
+ "updated": 1737971303713,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "c-HWcR62JO098FZH-Exyy",
+ "type": "text",
+ "x": 2556.2064208984375,
+ "y": 427,
+ "width": 46.219970703125,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2t",
+ "roundness": null,
+ "seed": 814875082,
+ "version": 378,
+ "versionNonce": 137731658,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737971303713,
+ "link": null,
+ "locked": false,
+ "text": "AKS ",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "yXs1C5HAV_quiAwVGmj9-",
+ "originalText": "AKS ",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "UPzd8AvyIY1ANthQMQL-2",
+ "type": "text",
+ "x": 2562.1273498535156,
+ "y": 628,
+ "width": 51.79998779296875,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2u",
+ "roundness": null,
+ "seed": 1195587722,
+ "version": 424,
+ "versionNonce": 2145784778,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737971303713,
+ "link": null,
+ "locked": false,
+ "text": "KIND",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "XKA3gCRYDhwXJMOPsnTYe",
+ "originalText": "KIND",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "r_UUPiappL6TFrgWoMu3T",
+ "type": "text",
+ "x": 2559.739242553711,
+ "y": 720,
+ "width": 64.91995239257812,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2v",
+ "roundness": null,
+ "seed": 1210029898,
+ "version": 466,
+ "versionNonce": 454502730,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737971303713,
+ "link": null,
+ "locked": false,
+ "text": "Retina",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "sVoinvy16hzPyLe8OLqLD",
+ "originalText": "Retina",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "DTsWo6ztMP02RFmBj6e-D",
+ "type": "rectangle",
+ "x": 2512.7734375,
+ "y": 524.5,
+ "width": 143.453125,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2w",
+ "roundness": null,
+ "seed": 327294474,
+ "version": 477,
+ "versionNonce": 95907850,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "pRnKaRijr0r7prQVxvfOw"
+ }
+ ],
+ "updated": 1737971303713,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "pRnKaRijr0r7prQVxvfOw",
+ "type": "text",
+ "x": 2563.0700073242188,
+ "y": 534,
+ "width": 42.8599853515625,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2x",
+ "roundness": null,
+ "seed": 1181076682,
+ "version": 474,
+ "versionNonce": 706797258,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737971303713,
+ "link": null,
+ "locked": false,
+ "text": "GKE",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "DTsWo6ztMP02RFmBj6e-D",
+ "originalText": "GKE",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "K11qeT79l6Z6nKat1Vns3",
+ "type": "text",
+ "x": 2663.5,
+ "y": 371.5,
+ "width": 41.059967041015625,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2y",
+ "roundness": null,
+ "seed": 644195210,
+ "version": 430,
+ "versionNonce": 347405066,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737971373887,
+ "link": null,
+ "locked": false,
+ "text": "Unit",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Unit",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "GdXv0Fvpyz0tdxI9bskx0",
+ "type": "rectangle",
+ "x": 2830.5,
+ "y": 429.5,
+ "width": 311,
+ "height": 350,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b2z",
+ "roundness": {
+ "type": 3
+ },
+ "seed": 1407292694,
+ "version": 536,
+ "versionNonce": 1282920086,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "id": "JenUnqPQU9n29FUsRjstV",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1737971550724,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "45JovtXM2WCeBgd46-RNa",
+ "type": "rectangle",
+ "x": 2904.58984375,
+ "y": 489.5,
+ "width": 143.453125,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b30",
+ "roundness": null,
+ "seed": 1549435478,
+ "version": 516,
+ "versionNonce": 1433501270,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "KK9wBCN7chtrFnhnHDCnN"
+ }
+ ],
+ "updated": 1737971415407,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "1UpFRPtuqe5oDMaRyOIOy",
+ "type": "rectangle",
+ "x": 2907.56640625,
+ "y": 690.5,
+ "width": 154.921875,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b31",
+ "roundness": null,
+ "seed": 1506002838,
+ "version": 555,
+ "versionNonce": 551182550,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "EebjZsSTiA0RgmiXs7pw-"
+ }
+ ],
+ "updated": 1737971415407,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "KK9wBCN7chtrFnhnHDCnN",
+ "type": "text",
+ "x": 2917.7764587402344,
+ "y": 499,
+ "width": 117.07989501953125,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b33",
+ "roundness": null,
+ "seed": 630938134,
+ "version": 548,
+ "versionNonce": 245056406,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737971415407,
+ "link": null,
+ "locked": false,
+ "text": "Retina-AKS ",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "45JovtXM2WCeBgd46-RNa",
+ "originalText": "Retina-AKS ",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "EebjZsSTiA0RgmiXs7pw-",
+ "type": "text",
+ "x": 2922.7973861694336,
+ "y": 700,
+ "width": 124.45991516113281,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b34",
+ "roundness": null,
+ "seed": 1016309590,
+ "version": 594,
+ "versionNonce": 1875105302,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737971415407,
+ "link": null,
+ "locked": false,
+ "text": "Retina-KIND",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "1UpFRPtuqe5oDMaRyOIOy",
+ "originalText": "Retina-KIND",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "fnS2X4EuJY-tTrtQBIe8A",
+ "type": "rectangle",
+ "x": 2909.7734375,
+ "y": 596.5,
+ "width": 143.453125,
+ "height": 44,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b36",
+ "roundness": null,
+ "seed": 1648773590,
+ "version": 610,
+ "versionNonce": 609002326,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "82dXVu6Pnfd0kT49q4C9w"
+ }
+ ],
+ "updated": 1737971415407,
+ "link": null,
+ "locked": false
+ },
+ {
+ "id": "82dXVu6Pnfd0kT49q4C9w",
+ "type": "text",
+ "x": 2924.2400436401367,
+ "y": 606,
+ "width": 114.51991271972656,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b37",
+ "roundness": null,
+ "seed": 1305124630,
+ "version": 644,
+ "versionNonce": 13224086,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737971415407,
+ "link": null,
+ "locked": false,
+ "text": "Retina-GKE",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "fnS2X4EuJY-tTrtQBIe8A",
+ "originalText": "Retina-GKE",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "0xZrHBzEv0Ezz_B9ggGOG",
+ "type": "text",
+ "x": 3006.5,
+ "y": 443.5,
+ "width": 111.73991394042969,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "solid",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b38",
+ "roundness": null,
+ "seed": 143805526,
+ "version": 578,
+ "versionNonce": 674077066,
+ "isDeleted": false,
+ "boundElements": [
+ {
+ "id": "-FO1qNsJmZSIC7MG10S17",
+ "type": "arrow"
+ }
+ ],
+ "updated": 1737971476328,
+ "link": null,
+ "locked": false,
+ "text": "Integration",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Integration",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "EL7fKQS2Z0a_heFdNlDpe",
+ "type": "arrow",
+ "x": 2560,
+ "y": 138,
+ "width": 0,
+ "height": 217,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b39",
+ "roundness": {
+ "type": 2
+ },
+ "seed": 328428886,
+ "version": 28,
+ "versionNonce": 1900773450,
+ "isDeleted": false,
+ "boundElements": null,
+ "updated": 1737971470062,
+ "link": null,
+ "locked": false,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 217
+ ]
+ ],
+ "lastCommittedPoint": null,
+ "startBinding": {
+ "elementId": "ysLRakRFsF7DlEwBVIfpv",
+ "focus": -0.024442597978119374,
+ "gap": 4,
+ "fixedPoint": null
+ },
+ "endBinding": {
+ "elementId": "0jJYOpQUuReGklXS3eCHd",
+ "focus": -0.1864951768488746,
+ "gap": 2.5,
+ "fixedPoint": null
+ },
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "elbowed": false
+ },
+ {
+ "id": "-FO1qNsJmZSIC7MG10S17",
+ "type": "arrow",
+ "x": 2562,
+ "y": 135,
+ "width": 443,
+ "height": 297,
+ "angle": 0,
+ "strokeColor": "#1e1e1e",
+ "backgroundColor": "transparent",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b3A",
+ "roundness": {
+ "type": 2
+ },
+ "seed": 634268810,
+ "version": 62,
+ "versionNonce": 1354947210,
+ "isDeleted": false,
+ "boundElements": null,
+ "updated": 1737971477960,
+ "link": null,
+ "locked": false,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 318,
+ 94
+ ],
+ [
+ 443,
+ 297
+ ]
+ ],
+ "lastCommittedPoint": null,
+ "startBinding": {
+ "elementId": "ysLRakRFsF7DlEwBVIfpv",
+ "focus": 0.46713186808073004,
+ "gap": 1,
+ "fixedPoint": null
+ },
+ "endBinding": {
+ "elementId": "0xZrHBzEv0Ezz_B9ggGOG",
+ "focus": -0.6700273879929672,
+ "gap": 11.5,
+ "fixedPoint": null
+ },
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "elbowed": false
+ },
+ {
+ "id": "1G4RvWl5j3Eq0orLwRNb1",
+ "type": "arrow",
+ "x": 2429,
+ "y": 587,
+ "width": 262,
+ "height": 17,
+ "angle": 0,
+ "strokeColor": "#e03131",
+ "backgroundColor": "transparent",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b3B",
+ "roundness": {
+ "type": 2
+ },
+ "seed": 383119562,
+ "version": 49,
+ "versionNonce": 607055690,
+ "isDeleted": false,
+ "boundElements": null,
+ "updated": 1737971531455,
+ "link": null,
+ "locked": false,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -262,
+ 17
+ ]
+ ],
+ "lastCommittedPoint": null,
+ "startBinding": {
+ "elementId": "0jJYOpQUuReGklXS3eCHd",
+ "focus": 0.12378805454028145,
+ "gap": 4.5,
+ "fixedPoint": null
+ },
+ "endBinding": {
+ "elementId": "R4AeEioM2INhMP9rnYnHe",
+ "focus": 0.0575605699153006,
+ "gap": 1,
+ "fixedPoint": null
+ },
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "elbowed": false
+ },
+ {
+ "id": "ia3p_InHfk6eqkomGTQVy",
+ "type": "text",
+ "x": 2352,
+ "y": 558,
+ "width": 47.19996643066406,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#e03131",
+ "backgroundColor": "transparent",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b3C",
+ "roundness": null,
+ "seed": 1107965066,
+ "version": 79,
+ "versionNonce": 1846142166,
+ "isDeleted": false,
+ "boundElements": null,
+ "updated": 1737971535976,
+ "link": null,
+ "locked": false,
+ "text": "Uses",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Uses",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "RYcoP2a7h3PmEmilpAoEr",
+ "type": "text",
+ "x": 2890.400016784668,
+ "y": 971.5,
+ "width": 47.19996643066406,
+ "height": 25,
+ "angle": 0,
+ "strokeColor": "#e03131",
+ "backgroundColor": "transparent",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b3D",
+ "roundness": null,
+ "seed": 422145994,
+ "version": 82,
+ "versionNonce": 1186784906,
+ "isDeleted": false,
+ "boundElements": [],
+ "updated": 1737971541173,
+ "link": null,
+ "locked": false,
+ "text": "Uses",
+ "fontSize": 20,
+ "fontFamily": 5,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Uses",
+ "autoResize": true,
+ "lineHeight": 1.25
+ },
+ {
+ "id": "JenUnqPQU9n29FUsRjstV",
+ "type": "arrow",
+ "x": 2993,
+ "y": 780,
+ "width": 828,
+ "height": 228,
+ "angle": 0,
+ "strokeColor": "#e03131",
+ "backgroundColor": "transparent",
+ "fillStyle": "hachure",
+ "strokeWidth": 2,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "frameId": null,
+ "index": "b3E",
+ "roundness": {
+ "type": 2
+ },
+ "seed": 1211336010,
+ "version": 277,
+ "versionNonce": 439205910,
+ "isDeleted": false,
+ "boundElements": null,
+ "updated": 1737971555301,
+ "link": null,
+ "locked": false,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -77,
+ 228
+ ],
+ [
+ -828,
+ 6
+ ]
+ ],
+ "lastCommittedPoint": null,
+ "startBinding": {
+ "elementId": "GdXv0Fvpyz0tdxI9bskx0",
+ "focus": -0.3088045944123118,
+ "gap": 1,
+ "fixedPoint": null
+ },
+ "endBinding": {
+ "elementId": "R4AeEioM2INhMP9rnYnHe",
+ "focus": 0.4765988876330307,
+ "gap": 1,
+ "fixedPoint": null
+ },
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "elbowed": false
+ }
+ ],
+ "appState": {
+ "gridSize": 20,
+ "gridStep": 5,
+ "gridModeEnabled": false,
+ "viewBackgroundColor": "#ffffff"
+ },
+ "files": {}
+}
\ No newline at end of file
diff --git a/test/multicloud/diagrams/diagram.svg b/test/multicloud/diagrams/diagram.svg
new file mode 100644
index 0000000000..afa9a3c21b
--- /dev/null
+++ b/test/multicloud/diagrams/diagram.svg
@@ -0,0 +1,2 @@
+
\ No newline at end of file
diff --git a/test/multicloud/examples/aks/.terraform.lock.hcl b/test/multicloud/examples/aks/.terraform.lock.hcl
new file mode 100644
index 0000000000..3d7443dac3
--- /dev/null
+++ b/test/multicloud/examples/aks/.terraform.lock.hcl
@@ -0,0 +1,20 @@
+# This file is maintained automatically by "tofu init".
+# Manual edits may be lost in future updates.
+
+provider "registry.opentofu.org/hashicorp/azurerm" {
+ version = "4.15.0"
+ constraints = "4.15.0"
+ hashes = [
+ "h1:xE74Yb3iZZF2F1hQy4B8YVCk0gLAp99pJgZX4eIXYMg=",
+ "zh:0a104acfc45de410d9786bdbf540009dbb7db5632fe7c6846fdb5f865007d0b4",
+ "zh:186f20452ca913e84de0fc9b2dc7872c6480813afe11ea076bd60c45aa2d66d0",
+ "zh:2d540a98254188f17d64c02847e4d178b49979871a2a05283f78d336f3f25d45",
+ "zh:34fdffad86800af74dd2f4c9b3a32fb4f1463ba453bb0f634a9389f503869bc6",
+ "zh:38751b0bb8d03c34acb63dbddf6ebe01d99e6b75a9d94ae3b3fb1c60bf43c45a",
+ "zh:4234e0e56e87983d387d7323d8bb49838054c5cebeb121dbb5aa4dd3255f211f",
+ "zh:4ead322049485495d07afe2334d82a47997fc7638b9e39512000d4499dc3a782",
+ "zh:74e8a9fdfbe0bb91c6542176dc14d3e95be2c3c77cca10ae57aa67116b5a6b95",
+ "zh:9f03e13551cd68d2f7cd2d46f74ffc33f0656e0aa8c9095ed259385aaeaf4957",
+ "zh:f86fce72cbdf72f52412b9099023115bece67c5df55970c4429ef0f1bdc3c473",
+ ]
+}
diff --git a/test/multicloud/examples/aks/main.tf b/test/multicloud/examples/aks/main.tf
new file mode 100644
index 0000000000..c926605307
--- /dev/null
+++ b/test/multicloud/examples/aks/main.tf
@@ -0,0 +1,12 @@
+module "aks" {
+ source = "../../modules/aks"
+ location = var.location
+ resource_group_name = var.resource_group_name
+ prefix = var.prefix
+ labels = var.labels
+}
+
+output "kubeconfig" {
+ value = module.aks.azure_get_kubeconfig
+ sensitive = true
+}
diff --git a/test/multicloud/examples/aks/providers.tf b/test/multicloud/examples/aks/providers.tf
new file mode 100644
index 0000000000..8ec4828f25
--- /dev/null
+++ b/test/multicloud/examples/aks/providers.tf
@@ -0,0 +1,20 @@
+terraform {
+ required_version = "1.8.3"
+ required_providers {
+ azurerm = {
+ source = "hashicorp/azurerm"
+ version = "4.15.0"
+ }
+ }
+}
+
+# Initialize the Azure provider
+provider "azurerm" {
+ features {
+ resource_group {
+ prevent_deletion_if_contains_resources = false
+ }
+ }
+ subscription_id = var.subscription_id
+ tenant_id = var.tenant_id
+}
diff --git a/test/multicloud/examples/aks/variables.tf b/test/multicloud/examples/aks/variables.tf
new file mode 100644
index 0000000000..ec3e945824
--- /dev/null
+++ b/test/multicloud/examples/aks/variables.tf
@@ -0,0 +1,33 @@
+variable "subscription_id" {
+ description = "The subscription ID for the Azure account."
+ type = string
+}
+
+variable "tenant_id" {
+ description = "The tenant ID for the Azure account."
+ type = string
+}
+
+variable "location" {
+ description = "The Azure Cloud location where AKS will be deployed to."
+ type = string
+ default = "UK South"
+}
+
+variable "resource_group_name" {
+ description = "The name of the resource group."
+ type = string
+ default = "example-rg"
+}
+
+variable "prefix" {
+ description = "A prefix to add to all resources."
+ type = string
+ default = "example-mc"
+}
+
+variable "labels" {
+ description = "A map of labels to add to all resources."
+ type = map(string)
+ default = {}
+}
diff --git a/test/multicloud/examples/gke/.terraform.lock.hcl b/test/multicloud/examples/gke/.terraform.lock.hcl
new file mode 100644
index 0000000000..f110175d5e
--- /dev/null
+++ b/test/multicloud/examples/gke/.terraform.lock.hcl
@@ -0,0 +1,20 @@
+# This file is maintained automatically by "tofu init".
+# Manual edits may be lost in future updates.
+
+provider "registry.opentofu.org/hashicorp/google" {
+ version = "6.17.0"
+ constraints = "6.17.0"
+ hashes = [
+ "h1:aZkLSXbqbNThCCLAX1x0g8KTJANQAIosYq3xpy8JhFQ=",
+ "zh:0614cc52deb5914795253aecf19b4cbb5aa7e8a186839a33907ce5c35e23d537",
+ "zh:0b28ea31ec3b119aafc4c37a5992c29266c876db288dfc5bbfbde36631a533f1",
+ "zh:1b2c5df40ac55ec7c3db2b7c556ace7545fca4ccfacf605b7588a4e2be564ba8",
+ "zh:1db9a278cfddcaa7d9119acf231164438df07932bdabce95931a68bd3689cd39",
+ "zh:3729b7a9936f5ad545f7d06d11d05ce78b4bbe9941e77bf004a2b798e6a0866c",
+ "zh:3b7e35183a8d7980207ae7d082c490d7c2270f907e5de9e8393c6c4c32b07b3c",
+ "zh:581e14963dfef608285af0c08ccb5da4bdb8ae049366418d4863a4fd9fa55b74",
+ "zh:89d29f2d1269a30c6149bbaaae3cbe8fe0c6ed13874b77bf784ea6bbd73aee58",
+ "zh:c7c690c1f9fe0cbc69d4eb21727f2cf4b7ba016385184d27527d570832c393be",
+ "zh:dc1536fc325a0561bf0f606a38bed29e8dd2f4bec6d6e8a5301696d89c1c8079",
+ ]
+}
diff --git a/test/multicloud/examples/gke/main.tf b/test/multicloud/examples/gke/main.tf
new file mode 100644
index 0000000000..daf79438c6
--- /dev/null
+++ b/test/multicloud/examples/gke/main.tf
@@ -0,0 +1,9 @@
+module "gke" {
+ source = "../../modules/gke"
+ location = var.location
+ prefix = var.prefix
+ project = var.project
+ machine_type = var.machine_type
+}
+
+data "google_client_config" "current" {}
\ No newline at end of file
diff --git a/test/multicloud/examples/gke/outputs.tf b/test/multicloud/examples/gke/outputs.tf
new file mode 100644
index 0000000000..1d146ecf0f
--- /dev/null
+++ b/test/multicloud/examples/gke/outputs.tf
@@ -0,0 +1,14 @@
+output "host" {
+ value = module.gke.host
+ sensitive = true
+}
+
+output "cluster_ca_certificate" {
+ value = module.gke.cluster_ca_certificate
+ sensitive = true
+}
+
+output "access_token" {
+ value = data.google_client_config.current.access_token
+ sensitive = true
+}
\ No newline at end of file
diff --git a/test/multicloud/examples/gke/providers.tf b/test/multicloud/examples/gke/providers.tf
new file mode 100644
index 0000000000..f1423d15d9
--- /dev/null
+++ b/test/multicloud/examples/gke/providers.tf
@@ -0,0 +1,15 @@
+terraform {
+ required_version = "1.8.3"
+ required_providers {
+ google = {
+ source = "hashicorp/google"
+ version = "6.17.0"
+ }
+ }
+}
+
+# Initialize the Google provider
+provider "google" {
+ project = var.project
+ region = var.location
+}
diff --git a/test/multicloud/examples/gke/variables.tf b/test/multicloud/examples/gke/variables.tf
new file mode 100644
index 0000000000..2e2da23167
--- /dev/null
+++ b/test/multicloud/examples/gke/variables.tf
@@ -0,0 +1,19 @@
+variable "project" {
+ description = "The Google Cloud project where resources will be deployed."
+ type = string
+}
+
+variable "location" {
+ description = "The Google Cloud location where GKE will be deployed to."
+ type = string
+}
+
+variable "prefix" {
+ description = "A prefix to add to all resources."
+ type = string
+}
+
+variable "machine_type" {
+ description = "The machine type to use for the GKE nodes."
+ type = string
+}
diff --git a/test/multicloud/examples/integration/retina-kind/.terraform.lock.hcl b/test/multicloud/examples/integration/retina-kind/.terraform.lock.hcl
new file mode 100644
index 0000000000..a641109262
--- /dev/null
+++ b/test/multicloud/examples/integration/retina-kind/.terraform.lock.hcl
@@ -0,0 +1,34 @@
+# This file is maintained automatically by "tofu init".
+# Manual edits may be lost in future updates.
+
+provider "registry.opentofu.org/hashicorp/helm" {
+ version = "2.17.0"
+ constraints = "2.17.0"
+ hashes = [
+ "h1:69PnHoYrrDrm7C8+8PiSvRGPI55taqL14SvQR/FGM+g=",
+ "zh:02690815e35131a42cb9851f63a3369c216af30ad093d05b39001d43da04b56b",
+ "zh:27a62f12b29926387f4d71aeeee9f7ffa0ccb81a1b6066ee895716ad050d1b7a",
+ "zh:2d0a5babfa73604b3fefc9dab9c87f91c77fce756c2e32b294e9f1290aed26c0",
+ "zh:3976400ceba6dda4636e1d297e3097e1831de5628afa534a166de98a70d1dcbe",
+ "zh:54440ef14f342b41d75c1aded7487bfcc3f76322b75894235b47b7e89ac4bfa4",
+ "zh:6512e2ab9f2fa31cbb90d9249647b5c5798f62eb1215ec44da2cdaa24e38ad25",
+ "zh:795f327ca0b8c5368af0ed03d5d4f6da7260692b4b3ca0bd004ed542e683464d",
+ "zh:ba659e1d94f224bc3f1fd34cbb9d2663e3a8e734108e5a58eb49eda84b140978",
+ "zh:c5c8575c4458835c2acbc3d1ed5570589b14baa2525d8fbd04295c097caf41eb",
+ "zh:e0877a5dac3de138e61eefa26b2f5a13305a17259779465899880f70e11314e0",
+ ]
+}
+
+provider "registry.opentofu.org/tehcyx/kind" {
+ version = "0.7.0"
+ constraints = "0.7.0"
+ hashes = [
+ "h1:nFhFHmE5+dCd9S9dEMwnMNWzyxoVipYobkhXYoDbhgA=",
+ "zh:171a2fb0137bfbdebd56cd65afd2e0e2167315fe4cb6a07a218db40cb17339c3",
+ "zh:3260b078b7997ddfd03845326ffaeed7f678eeaaf7918430356f22e299e36f22",
+ "zh:4066ab3feb482a0dd1bfff6590d89a0ec30478f63c9d8253cfdadb4b8db2234d",
+ "zh:537af73261d53f4840d1f89d8e5835c52ddb97102e6314f6aea9b8e49c43d610",
+ "zh:d63e94d828ba0339600d992b0a6695cff939b0aaac1c39b31d38e3c4f3823674",
+ "zh:f971c617bf6b37d07a5042f13a9ab02b42d0ceb14934174eecc81abeec233c40",
+ ]
+}
diff --git a/test/multicloud/examples/integration/retina-kind/main.tf b/test/multicloud/examples/integration/retina-kind/main.tf
new file mode 100644
index 0000000000..8bd5749095
--- /dev/null
+++ b/test/multicloud/examples/integration/retina-kind/main.tf
@@ -0,0 +1,10 @@
+module "kind" {
+ source = "../../../modules/kind"
+ prefix = var.prefix
+}
+
+module "retina" {
+ depends_on = [module.kind]
+ source = "../../../modules/retina"
+ retina_version = var.retina_version
+}
diff --git a/test/multicloud/examples/integration/retina-kind/outputs.tf b/test/multicloud/examples/integration/retina-kind/outputs.tf
new file mode 100644
index 0000000000..cdc064fec7
--- /dev/null
+++ b/test/multicloud/examples/integration/retina-kind/outputs.tf
@@ -0,0 +1,19 @@
+output "host" {
+ value = module.kind.host
+ sensitive = true
+}
+
+output "cluster_ca_certificate" {
+ value = module.kind.cluster_ca_certificate
+ sensitive = true
+}
+
+output "client_certificate" {
+ value = module.kind.client_certificate
+ sensitive = true
+}
+
+output "client_key" {
+ value = module.kind.client_key
+ sensitive = true
+}
diff --git a/test/multicloud/examples/integration/retina-kind/providers.tf b/test/multicloud/examples/integration/retina-kind/providers.tf
new file mode 100644
index 0000000000..7b8baf2518
--- /dev/null
+++ b/test/multicloud/examples/integration/retina-kind/providers.tf
@@ -0,0 +1,26 @@
+terraform {
+ required_version = "1.8.3"
+ required_providers {
+ kind = {
+ source = "tehcyx/kind"
+ version = "0.7.0"
+ }
+ helm = {
+ source = "hashicorp/helm"
+ version = "2.17.0"
+ }
+ }
+}
+
+# Initialize the kind provider
+provider "kind" {}
+
+# Initialize the Helm provider
+provider "helm" {
+ kubernetes {
+ host = module.kind.host
+ client_certificate = module.kind.client_certificate
+ client_key = module.kind.client_key
+ cluster_ca_certificate = module.kind.cluster_ca_certificate
+ }
+}
\ No newline at end of file
diff --git a/test/multicloud/examples/integration/retina-kind/test-integration-kind-config b/test/multicloud/examples/integration/retina-kind/test-integration-kind-config
new file mode 100644
index 0000000000..2ebf5e0ed5
--- /dev/null
+++ b/test/multicloud/examples/integration/retina-kind/test-integration-kind-config
@@ -0,0 +1,3 @@
+apiVersion: v1
+kind: Config
+preferences: {}
diff --git a/test/multicloud/examples/integration/retina-kind/test-kind-config b/test/multicloud/examples/integration/retina-kind/test-kind-config
new file mode 100644
index 0000000000..2ebf5e0ed5
--- /dev/null
+++ b/test/multicloud/examples/integration/retina-kind/test-kind-config
@@ -0,0 +1,3 @@
+apiVersion: v1
+kind: Config
+preferences: {}
diff --git a/test/multicloud/examples/integration/retina-kind/variables.tf b/test/multicloud/examples/integration/retina-kind/variables.tf
new file mode 100644
index 0000000000..e9d9024233
--- /dev/null
+++ b/test/multicloud/examples/integration/retina-kind/variables.tf
@@ -0,0 +1,11 @@
+variable "prefix" {
+ description = "A prefix to add to all resources."
+ type = string
+ default = "mc"
+}
+
+variable "retina_version" {
+ description = "The tag to apply to all resources."
+ type = string
+ default = "v0.0.23"
+}
\ No newline at end of file
diff --git a/test/multicloud/examples/kind/.terraform.lock.hcl b/test/multicloud/examples/kind/.terraform.lock.hcl
new file mode 100644
index 0000000000..b610e36285
--- /dev/null
+++ b/test/multicloud/examples/kind/.terraform.lock.hcl
@@ -0,0 +1,16 @@
+# This file is maintained automatically by "tofu init".
+# Manual edits may be lost in future updates.
+
+provider "registry.opentofu.org/tehcyx/kind" {
+ version = "0.7.0"
+ constraints = "0.7.0"
+ hashes = [
+ "h1:nFhFHmE5+dCd9S9dEMwnMNWzyxoVipYobkhXYoDbhgA=",
+ "zh:171a2fb0137bfbdebd56cd65afd2e0e2167315fe4cb6a07a218db40cb17339c3",
+ "zh:3260b078b7997ddfd03845326ffaeed7f678eeaaf7918430356f22e299e36f22",
+ "zh:4066ab3feb482a0dd1bfff6590d89a0ec30478f63c9d8253cfdadb4b8db2234d",
+ "zh:537af73261d53f4840d1f89d8e5835c52ddb97102e6314f6aea9b8e49c43d610",
+ "zh:d63e94d828ba0339600d992b0a6695cff939b0aaac1c39b31d38e3c4f3823674",
+ "zh:f971c617bf6b37d07a5042f13a9ab02b42d0ceb14934174eecc81abeec233c40",
+ ]
+}
diff --git a/test/multicloud/examples/kind/main.tf b/test/multicloud/examples/kind/main.tf
new file mode 100644
index 0000000000..c2484af1cb
--- /dev/null
+++ b/test/multicloud/examples/kind/main.tf
@@ -0,0 +1,4 @@
+module "kind" {
+ source = "../../modules/kind"
+ prefix = var.prefix
+}
\ No newline at end of file
diff --git a/test/multicloud/examples/kind/outputs.tf b/test/multicloud/examples/kind/outputs.tf
new file mode 100644
index 0000000000..cdc064fec7
--- /dev/null
+++ b/test/multicloud/examples/kind/outputs.tf
@@ -0,0 +1,19 @@
+output "host" {
+ value = module.kind.host
+ sensitive = true
+}
+
+output "cluster_ca_certificate" {
+ value = module.kind.cluster_ca_certificate
+ sensitive = true
+}
+
+output "client_certificate" {
+ value = module.kind.client_certificate
+ sensitive = true
+}
+
+output "client_key" {
+ value = module.kind.client_key
+ sensitive = true
+}
diff --git a/test/multicloud/examples/kind/providers.tf b/test/multicloud/examples/kind/providers.tf
new file mode 100644
index 0000000000..23178d677b
--- /dev/null
+++ b/test/multicloud/examples/kind/providers.tf
@@ -0,0 +1,12 @@
+terraform {
+ required_version = "1.8.3"
+ required_providers {
+ kind = {
+ source = "tehcyx/kind"
+ version = "0.7.0"
+ }
+ }
+}
+
+# Initialize the kind provider
+provider "kind" {}
diff --git a/test/multicloud/examples/kind/variables.tf b/test/multicloud/examples/kind/variables.tf
new file mode 100644
index 0000000000..2dd9eaf4b0
--- /dev/null
+++ b/test/multicloud/examples/kind/variables.tf
@@ -0,0 +1,4 @@
+variable "prefix" {
+ description = "A prefix to add to all resources."
+ type = string
+}
\ No newline at end of file
diff --git a/test/multicloud/live/retina-aks/.terraform.lock.hcl b/test/multicloud/live/retina-aks/.terraform.lock.hcl
new file mode 100644
index 0000000000..5c20d26e2a
--- /dev/null
+++ b/test/multicloud/live/retina-aks/.terraform.lock.hcl
@@ -0,0 +1,40 @@
+# This file is maintained automatically by "tofu init".
+# Manual edits may be lost in future updates.
+
+provider "registry.opentofu.org/hashicorp/azurerm" {
+ version = "4.15.0"
+ constraints = ">= 2.0.0"
+ hashes = [
+ "h1:0YxkmS5jTUl1LIG+71sgKg/YdlgAoHNr3wyyZjJO8vY=",
+ "h1:xE74Yb3iZZF2F1hQy4B8YVCk0gLAp99pJgZX4eIXYMg=",
+ "zh:0a104acfc45de410d9786bdbf540009dbb7db5632fe7c6846fdb5f865007d0b4",
+ "zh:186f20452ca913e84de0fc9b2dc7872c6480813afe11ea076bd60c45aa2d66d0",
+ "zh:2d540a98254188f17d64c02847e4d178b49979871a2a05283f78d336f3f25d45",
+ "zh:34fdffad86800af74dd2f4c9b3a32fb4f1463ba453bb0f634a9389f503869bc6",
+ "zh:38751b0bb8d03c34acb63dbddf6ebe01d99e6b75a9d94ae3b3fb1c60bf43c45a",
+ "zh:4234e0e56e87983d387d7323d8bb49838054c5cebeb121dbb5aa4dd3255f211f",
+ "zh:4ead322049485495d07afe2334d82a47997fc7638b9e39512000d4499dc3a782",
+ "zh:74e8a9fdfbe0bb91c6542176dc14d3e95be2c3c77cca10ae57aa67116b5a6b95",
+ "zh:9f03e13551cd68d2f7cd2d46f74ffc33f0656e0aa8c9095ed259385aaeaf4957",
+ "zh:f86fce72cbdf72f52412b9099023115bece67c5df55970c4429ef0f1bdc3c473",
+ ]
+}
+
+provider "registry.opentofu.org/hashicorp/helm" {
+ version = "2.17.0"
+ constraints = "2.17.0"
+ hashes = [
+ "h1:69PnHoYrrDrm7C8+8PiSvRGPI55taqL14SvQR/FGM+g=",
+ "h1:ShIag7wqd5Rs+zYpVMpjAh+T0ozr4XGYfSTKWqceQBY=",
+ "zh:02690815e35131a42cb9851f63a3369c216af30ad093d05b39001d43da04b56b",
+ "zh:27a62f12b29926387f4d71aeeee9f7ffa0ccb81a1b6066ee895716ad050d1b7a",
+ "zh:2d0a5babfa73604b3fefc9dab9c87f91c77fce756c2e32b294e9f1290aed26c0",
+ "zh:3976400ceba6dda4636e1d297e3097e1831de5628afa534a166de98a70d1dcbe",
+ "zh:54440ef14f342b41d75c1aded7487bfcc3f76322b75894235b47b7e89ac4bfa4",
+ "zh:6512e2ab9f2fa31cbb90d9249647b5c5798f62eb1215ec44da2cdaa24e38ad25",
+ "zh:795f327ca0b8c5368af0ed03d5d4f6da7260692b4b3ca0bd004ed542e683464d",
+ "zh:ba659e1d94f224bc3f1fd34cbb9d2663e3a8e734108e5a58eb49eda84b140978",
+ "zh:c5c8575c4458835c2acbc3d1ed5570589b14baa2525d8fbd04295c097caf41eb",
+ "zh:e0877a5dac3de138e61eefa26b2f5a13305a17259779465899880f70e11314e0",
+ ]
+}
diff --git a/test/multicloud/live/retina-aks/main.tf b/test/multicloud/live/retina-aks/main.tf
new file mode 100644
index 0000000000..237471fe09
--- /dev/null
+++ b/test/multicloud/live/retina-aks/main.tf
@@ -0,0 +1,16 @@
+module "aks" {
+ source = "../../modules/aks"
+ location = var.location
+ resource_group_name = var.resource_group_name
+ prefix = var.prefix
+ labels = var.labels
+}
+
+module "retina" {
+ depends_on = [module.aks]
+ source = "../../modules/retina"
+}
+
+output "kubeconfig_command" {
+ value = module.aks.azure_get_kubeconfig
+}
diff --git a/test/multicloud/live/retina-aks/providers.tf b/test/multicloud/live/retina-aks/providers.tf
new file mode 100644
index 0000000000..264d724c95
--- /dev/null
+++ b/test/multicloud/live/retina-aks/providers.tf
@@ -0,0 +1,34 @@
+terraform {
+ required_version = "1.8.3"
+ required_providers {
+ azurerm = {
+ source = "hashicorp/azurerm"
+ version = "4.15.0"
+ }
+ helm = {
+ source = "hashicorp/helm"
+ version = "2.17.0"
+ }
+ }
+}
+
+# Initialize the Azure provider
+provider "azurerm" {
+ features {
+ resource_group {
+ prevent_deletion_if_contains_resources = false
+ }
+ }
+ subscription_id = var.subscription_id
+ tenant_id = var.tenant_id
+}
+
+# Initialize the Helm provider
+provider "helm" {
+ kubernetes {
+ host = module.aks.host
+ client_certificate = base64decode(module.aks.client_certificate)
+ client_key = base64decode(module.aks.client_key)
+ cluster_ca_certificate = base64decode(module.aks.cluster_ca_certificate)
+ }
+}
diff --git a/test/multicloud/live/retina-aks/variables.tf b/test/multicloud/live/retina-aks/variables.tf
new file mode 100644
index 0000000000..886aacc47e
--- /dev/null
+++ b/test/multicloud/live/retina-aks/variables.tf
@@ -0,0 +1,35 @@
+variable "subscription_id" {
+ description = "The subscription ID for the Azure account."
+ type = string
+}
+
+variable "tenant_id" {
+ description = "The tenant ID for the Azure account."
+ type = string
+}
+
+variable "location" {
+ description = "The Azure Cloud location where AKS will be deployed to."
+ type = string
+ default = "uksouth"
+}
+
+variable "resource_group_name" {
+ description = "The name of the resource group."
+ type = string
+ default = "mc-rg"
+}
+
+variable "prefix" {
+ description = "A prefix to add to all resources."
+ type = string
+ default = "mc"
+}
+
+variable "labels" {
+ description = "A map of labels to add to all resources."
+ type = map(string)
+ default = {
+ "env" = "test"
+ }
+}
diff --git a/test/multicloud/live/retina-gke/.terraform.lock.hcl b/test/multicloud/live/retina-gke/.terraform.lock.hcl
new file mode 100644
index 0000000000..ed3aacfe1c
--- /dev/null
+++ b/test/multicloud/live/retina-gke/.terraform.lock.hcl
@@ -0,0 +1,40 @@
+# This file is maintained automatically by "tofu init".
+# Manual edits may be lost in future updates.
+
+provider "registry.opentofu.org/hashicorp/google" {
+ version = "6.17.0"
+ constraints = ">= 6.17.0"
+ hashes = [
+ "h1:7m+L8x7ClWUFAd4uJJENXp9O4K8HtpL50434jPR9pqs=",
+ "h1:aZkLSXbqbNThCCLAX1x0g8KTJANQAIosYq3xpy8JhFQ=",
+ "zh:0614cc52deb5914795253aecf19b4cbb5aa7e8a186839a33907ce5c35e23d537",
+ "zh:0b28ea31ec3b119aafc4c37a5992c29266c876db288dfc5bbfbde36631a533f1",
+ "zh:1b2c5df40ac55ec7c3db2b7c556ace7545fca4ccfacf605b7588a4e2be564ba8",
+ "zh:1db9a278cfddcaa7d9119acf231164438df07932bdabce95931a68bd3689cd39",
+ "zh:3729b7a9936f5ad545f7d06d11d05ce78b4bbe9941e77bf004a2b798e6a0866c",
+ "zh:3b7e35183a8d7980207ae7d082c490d7c2270f907e5de9e8393c6c4c32b07b3c",
+ "zh:581e14963dfef608285af0c08ccb5da4bdb8ae049366418d4863a4fd9fa55b74",
+ "zh:89d29f2d1269a30c6149bbaaae3cbe8fe0c6ed13874b77bf784ea6bbd73aee58",
+ "zh:c7c690c1f9fe0cbc69d4eb21727f2cf4b7ba016385184d27527d570832c393be",
+ "zh:dc1536fc325a0561bf0f606a38bed29e8dd2f4bec6d6e8a5301696d89c1c8079",
+ ]
+}
+
+provider "registry.opentofu.org/hashicorp/helm" {
+ version = "2.17.0"
+ constraints = "2.17.0"
+ hashes = [
+ "h1:69PnHoYrrDrm7C8+8PiSvRGPI55taqL14SvQR/FGM+g=",
+ "h1:ShIag7wqd5Rs+zYpVMpjAh+T0ozr4XGYfSTKWqceQBY=",
+ "zh:02690815e35131a42cb9851f63a3369c216af30ad093d05b39001d43da04b56b",
+ "zh:27a62f12b29926387f4d71aeeee9f7ffa0ccb81a1b6066ee895716ad050d1b7a",
+ "zh:2d0a5babfa73604b3fefc9dab9c87f91c77fce756c2e32b294e9f1290aed26c0",
+ "zh:3976400ceba6dda4636e1d297e3097e1831de5628afa534a166de98a70d1dcbe",
+ "zh:54440ef14f342b41d75c1aded7487bfcc3f76322b75894235b47b7e89ac4bfa4",
+ "zh:6512e2ab9f2fa31cbb90d9249647b5c5798f62eb1215ec44da2cdaa24e38ad25",
+ "zh:795f327ca0b8c5368af0ed03d5d4f6da7260692b4b3ca0bd004ed542e683464d",
+ "zh:ba659e1d94f224bc3f1fd34cbb9d2663e3a8e734108e5a58eb49eda84b140978",
+ "zh:c5c8575c4458835c2acbc3d1ed5570589b14baa2525d8fbd04295c097caf41eb",
+ "zh:e0877a5dac3de138e61eefa26b2f5a13305a17259779465899880f70e11314e0",
+ ]
+}
diff --git a/test/multicloud/live/retina-gke/main.tf b/test/multicloud/live/retina-gke/main.tf
new file mode 100644
index 0000000000..de30d24493
--- /dev/null
+++ b/test/multicloud/live/retina-gke/main.tf
@@ -0,0 +1,12 @@
+module "gke" {
+ source = "../../modules/gke"
+ location = var.location
+ prefix = var.prefix
+ project = var.project
+ machine_type = var.machine_type
+}
+
+module "retina" {
+ depends_on = [module.gke]
+ source = "../../modules/retina"
+}
diff --git a/test/multicloud/live/retina-gke/outputs.tf b/test/multicloud/live/retina-gke/outputs.tf
new file mode 100644
index 0000000000..1954916e98
--- /dev/null
+++ b/test/multicloud/live/retina-gke/outputs.tf
@@ -0,0 +1,3 @@
+output "kubeconfig_command" {
+ value = module.gke.gcloud_get_kubeconfig
+}
diff --git a/test/multicloud/live/retina-gke/providers.tf b/test/multicloud/live/retina-gke/providers.tf
new file mode 100644
index 0000000000..ac23c59c0e
--- /dev/null
+++ b/test/multicloud/live/retina-gke/providers.tf
@@ -0,0 +1,30 @@
+terraform {
+ required_version = "1.8.3"
+ required_providers {
+ google = {
+ source = "hashicorp/google"
+ version = "6.17.0"
+ }
+ helm = {
+ source = "hashicorp/helm"
+ version = "2.17.0"
+ }
+ }
+}
+
+# Initialize the Google provider
+provider "google" {
+ project = var.project
+ region = var.location
+}
+
+data "google_client_config" "current" {}
+
+# Initialize the Helm provider
+provider "helm" {
+ kubernetes {
+ token = data.google_client_config.current.access_token
+ host = module.gke.host
+ cluster_ca_certificate = base64decode(module.gke.cluster_ca_certificate)
+ }
+}
\ No newline at end of file
diff --git a/test/multicloud/live/retina-gke/variables.tf b/test/multicloud/live/retina-gke/variables.tf
new file mode 100644
index 0000000000..9e00f0f537
--- /dev/null
+++ b/test/multicloud/live/retina-gke/variables.tf
@@ -0,0 +1,23 @@
+variable "project" {
+ description = "The Google Cloud project where resources will be deployed."
+ type = string
+ default = "mc-retina"
+}
+
+variable "location" {
+ description = "The Google Cloud location where GKE will be deployed to."
+ type = string
+ default = "eu-central1"
+}
+
+variable "prefix" {
+ description = "A prefix to add to all resources."
+ type = string
+ default = "mc"
+}
+
+variable "machine_type" {
+ description = "The machine type to use for the GKE nodes."
+ type = string
+ default = "e2-standard-4"
+}
diff --git a/test/multicloud/live/retina-kind/.terraform.lock.hcl b/test/multicloud/live/retina-kind/.terraform.lock.hcl
new file mode 100644
index 0000000000..afc28ace1d
--- /dev/null
+++ b/test/multicloud/live/retina-kind/.terraform.lock.hcl
@@ -0,0 +1,36 @@
+# This file is maintained automatically by "tofu init".
+# Manual edits may be lost in future updates.
+
+provider "registry.opentofu.org/hashicorp/helm" {
+ version = "2.17.0"
+ constraints = "2.17.0"
+ hashes = [
+ "h1:69PnHoYrrDrm7C8+8PiSvRGPI55taqL14SvQR/FGM+g=",
+ "h1:ShIag7wqd5Rs+zYpVMpjAh+T0ozr4XGYfSTKWqceQBY=",
+ "zh:02690815e35131a42cb9851f63a3369c216af30ad093d05b39001d43da04b56b",
+ "zh:27a62f12b29926387f4d71aeeee9f7ffa0ccb81a1b6066ee895716ad050d1b7a",
+ "zh:2d0a5babfa73604b3fefc9dab9c87f91c77fce756c2e32b294e9f1290aed26c0",
+ "zh:3976400ceba6dda4636e1d297e3097e1831de5628afa534a166de98a70d1dcbe",
+ "zh:54440ef14f342b41d75c1aded7487bfcc3f76322b75894235b47b7e89ac4bfa4",
+ "zh:6512e2ab9f2fa31cbb90d9249647b5c5798f62eb1215ec44da2cdaa24e38ad25",
+ "zh:795f327ca0b8c5368af0ed03d5d4f6da7260692b4b3ca0bd004ed542e683464d",
+ "zh:ba659e1d94f224bc3f1fd34cbb9d2663e3a8e734108e5a58eb49eda84b140978",
+ "zh:c5c8575c4458835c2acbc3d1ed5570589b14baa2525d8fbd04295c097caf41eb",
+ "zh:e0877a5dac3de138e61eefa26b2f5a13305a17259779465899880f70e11314e0",
+ ]
+}
+
+provider "registry.opentofu.org/tehcyx/kind" {
+ version = "0.7.0"
+ constraints = "0.7.0"
+ hashes = [
+ "h1:9ci1+3JBxvMRZ0pnzoVNmw7NrBCAfzqOPyhm+I8Rxag=",
+ "h1:nFhFHmE5+dCd9S9dEMwnMNWzyxoVipYobkhXYoDbhgA=",
+ "zh:171a2fb0137bfbdebd56cd65afd2e0e2167315fe4cb6a07a218db40cb17339c3",
+ "zh:3260b078b7997ddfd03845326ffaeed7f678eeaaf7918430356f22e299e36f22",
+ "zh:4066ab3feb482a0dd1bfff6590d89a0ec30478f63c9d8253cfdadb4b8db2234d",
+ "zh:537af73261d53f4840d1f89d8e5835c52ddb97102e6314f6aea9b8e49c43d610",
+ "zh:d63e94d828ba0339600d992b0a6695cff939b0aaac1c39b31d38e3c4f3823674",
+ "zh:f971c617bf6b37d07a5042f13a9ab02b42d0ceb14934174eecc81abeec233c40",
+ ]
+}
diff --git a/test/multicloud/live/retina-kind/main.tf b/test/multicloud/live/retina-kind/main.tf
new file mode 100644
index 0000000000..6f71163ccf
--- /dev/null
+++ b/test/multicloud/live/retina-kind/main.tf
@@ -0,0 +1,9 @@
+module "kind" {
+ source = "../../modules/kind"
+ prefix = var.prefix
+}
+
+module "retina" {
+ depends_on = [module.kind]
+ source = "../../modules/retina"
+}
diff --git a/test/multicloud/live/retina-kind/providers.tf b/test/multicloud/live/retina-kind/providers.tf
new file mode 100644
index 0000000000..7b8baf2518
--- /dev/null
+++ b/test/multicloud/live/retina-kind/providers.tf
@@ -0,0 +1,26 @@
+terraform {
+ required_version = "1.8.3"
+ required_providers {
+ kind = {
+ source = "tehcyx/kind"
+ version = "0.7.0"
+ }
+ helm = {
+ source = "hashicorp/helm"
+ version = "2.17.0"
+ }
+ }
+}
+
+# Initialize the kind provider
+provider "kind" {}
+
+# Initialize the Helm provider
+provider "helm" {
+ kubernetes {
+ host = module.kind.host
+ client_certificate = module.kind.client_certificate
+ client_key = module.kind.client_key
+ cluster_ca_certificate = module.kind.cluster_ca_certificate
+ }
+}
\ No newline at end of file
diff --git a/test/multicloud/live/retina-kind/variables.tf b/test/multicloud/live/retina-kind/variables.tf
new file mode 100644
index 0000000000..347514bd40
--- /dev/null
+++ b/test/multicloud/live/retina-kind/variables.tf
@@ -0,0 +1,5 @@
+variable "prefix" {
+ description = "A prefix to add to all resources."
+ type = string
+ default = "mc"
+}
\ No newline at end of file
diff --git a/test/multicloud/modules/aks/main.tf b/test/multicloud/modules/aks/main.tf
new file mode 100644
index 0000000000..a7352bd589
--- /dev/null
+++ b/test/multicloud/modules/aks/main.tf
@@ -0,0 +1,47 @@
+resource "azurerm_resource_group" "aks_rg" {
+ name = var.resource_group_name
+ location = var.location
+}
+
+resource "azurerm_kubernetes_cluster" "aks" {
+ name = "${var.prefix}-aks"
+ location = azurerm_resource_group.aks_rg.location
+ resource_group_name = azurerm_resource_group.aks_rg.name
+ dns_prefix = "${var.prefix}-aks-dns"
+ kubernetes_version = var.kubernetes_version
+
+ dynamic "default_node_pool" {
+ for_each = [var.default_node_pool]
+ content {
+ name = default_node_pool.value.name
+ node_count = default_node_pool.value.node_count
+ vm_size = default_node_pool.value.vm_size
+ os_disk_size_gb = default_node_pool.value.os_disk_size_gb
+ os_disk_type = default_node_pool.value.os_disk_type
+ max_pods = default_node_pool.value.max_pods
+ type = default_node_pool.value.type
+ node_labels = default_node_pool.value.node_labels
+ }
+ }
+
+ identity {
+ type = "SystemAssigned"
+ }
+
+ dynamic "network_profile" {
+ for_each = [var.network_profile]
+ content {
+ network_plugin = network_profile.value.network_plugin
+ network_plugin_mode = network_profile.value.network_plugin_mode
+ load_balancer_profile {
+ managed_outbound_ip_count = network_profile.value.load_balancer_profile.managed_outbound_ip_count
+ }
+ pod_cidr = network_profile.value.pod_cidr
+ service_cidr = network_profile.value.service_cidr
+ dns_service_ip = network_profile.value.dns_service_ip
+ outbound_type = network_profile.value.outbound_type
+ }
+ }
+
+ tags = var.labels
+}
diff --git a/test/multicloud/modules/aks/outputs.tf b/test/multicloud/modules/aks/outputs.tf
new file mode 100644
index 0000000000..e71ca414ff
--- /dev/null
+++ b/test/multicloud/modules/aks/outputs.tf
@@ -0,0 +1,24 @@
+output "azure_get_kubeconfig" {
+ value = "az aks get-credentials --resource-group ${azurerm_resource_group.aks_rg.name} --name ${azurerm_kubernetes_cluster.aks.name} --admin"
+ description = "Run this command to fetch the kubeconfig for your AKS cluster"
+}
+
+output "host" {
+ value = azurerm_kubernetes_cluster.aks.kube_config.0.host
+ sensitive = true
+}
+
+output "client_certificate" {
+ value = azurerm_kubernetes_cluster.aks.kube_config.0.client_certificate
+ sensitive = true
+}
+
+output "client_key" {
+ value = azurerm_kubernetes_cluster.aks.kube_config.0.client_key
+ sensitive = true
+}
+
+output "cluster_ca_certificate" {
+ value = azurerm_kubernetes_cluster.aks.kube_config.0.cluster_ca_certificate
+ sensitive = true
+}
diff --git a/test/multicloud/modules/aks/provider.tf b/test/multicloud/modules/aks/provider.tf
new file mode 100644
index 0000000000..33705925cf
--- /dev/null
+++ b/test/multicloud/modules/aks/provider.tf
@@ -0,0 +1,9 @@
+terraform {
+ required_version = "1.8.3"
+ required_providers {
+ azurerm = {
+ source = "hashicorp/azurerm"
+ version = "4.15.0"
+ }
+ }
+}
diff --git a/test/multicloud/modules/aks/variables.tf b/test/multicloud/modules/aks/variables.tf
new file mode 100644
index 0000000000..d5a5ebb597
--- /dev/null
+++ b/test/multicloud/modules/aks/variables.tf
@@ -0,0 +1,78 @@
+variable "location" {
+ description = "The VM location."
+ type = string
+ default = "UK South"
+}
+
+variable "resource_group_name" {
+ description = "The name of the resource group."
+ type = string
+}
+
+variable "prefix" {
+ description = "A prefix to add to all resources."
+ type = string
+ default = "example-vm"
+}
+
+variable "labels" {
+ description = "A map of labels to add to all resources."
+ type = map(string)
+ default = {}
+}
+
+variable "network_profile" {
+ description = "Network profile configuration"
+ type = object({
+ network_plugin = string
+ network_plugin_mode = string
+ load_balancer_profile = object({
+ managed_outbound_ip_count = number
+ })
+ pod_cidr = string
+ service_cidr = string
+ dns_service_ip = string
+ outbound_type = string
+ })
+ default = {
+ network_plugin = "azure"
+ network_plugin_mode = "overlay"
+ load_balancer_profile = {
+ managed_outbound_ip_count = 1
+ }
+ pod_cidr = "10.244.0.0/16"
+ service_cidr = "10.0.0.0/16"
+ dns_service_ip = "10.0.0.10"
+ outbound_type = "loadBalancer"
+ }
+}
+
+variable "default_node_pool" {
+ description = "Default node pool configuration"
+ type = object({
+ name = string
+ node_count = number
+ vm_size = string
+ os_disk_size_gb = number
+ os_disk_type = string
+ max_pods = number
+ type = string
+ node_labels = map(string)
+ })
+ default = {
+ name = "agentpool"
+ node_count = 2
+ vm_size = "Standard_D4ds_v5"
+ os_disk_size_gb = 128
+ os_disk_type = "Ephemeral"
+ max_pods = 110
+ type = "VirtualMachineScaleSets"
+ node_labels = {}
+ }
+}
+
+variable "kubernetes_version" {
+ description = "The version of Kubernetes to use for the AKS cluster."
+ type = string
+ default = "1.29.8"
+}
diff --git a/test/multicloud/modules/gke/main.tf b/test/multicloud/modules/gke/main.tf
new file mode 100644
index 0000000000..c4615b7858
--- /dev/null
+++ b/test/multicloud/modules/gke/main.tf
@@ -0,0 +1,34 @@
+resource "google_service_account" "default" {
+ account_id = "${var.prefix}-gke-service-account"
+ display_name = "GKE Service Account for ${var.project}"
+}
+
+resource "google_container_cluster" "gke" {
+ name = "${var.prefix}-gke-cluster"
+ location = var.location
+
+ # We can't create a cluster with no node pool defined, but we want to only use
+ # separately managed node pools. So we create the smallest possible default
+ # node pool and immediately delete it.
+ remove_default_node_pool = true
+ initial_node_count = 1
+ deletion_protection = false
+}
+
+resource "google_container_node_pool" "gke_preemptible_nodes" {
+ name = "${var.prefix}-node-pool"
+ location = var.location
+ cluster = google_container_cluster.gke.name
+ node_count = 1
+
+ node_config {
+ preemptible = true
+ machine_type = var.machine_type
+
+ # Google recommends custom service accounts that have cloud-platform scope and permissions granted via IAM Roles.
+ service_account = google_service_account.default.email
+ oauth_scopes = [
+ "https://www.googleapis.com/auth/cloud-platform"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/test/multicloud/modules/gke/output.tf b/test/multicloud/modules/gke/output.tf
new file mode 100644
index 0000000000..692cb7eddf
--- /dev/null
+++ b/test/multicloud/modules/gke/output.tf
@@ -0,0 +1,14 @@
+output "gcloud_get_kubeconfig" {
+ value = "gcloud container clusters get-credentials ${google_container_cluster.gke.name} --region ${google_container_cluster.gke.location} --project ${google_container_cluster.gke.project}"
+ description = "Run this command to fetch the kubeconfig for your GKE cluster"
+}
+
+output "host" {
+ value = "https://${google_container_cluster.gke.endpoint}"
+ sensitive = true
+}
+
+output "cluster_ca_certificate" {
+ value = google_container_cluster.gke.master_auth.0.cluster_ca_certificate
+ sensitive = true
+}
\ No newline at end of file
diff --git a/test/multicloud/modules/gke/provider.tf b/test/multicloud/modules/gke/provider.tf
new file mode 100644
index 0000000000..dbf7e91ddc
--- /dev/null
+++ b/test/multicloud/modules/gke/provider.tf
@@ -0,0 +1,9 @@
+terraform {
+ required_version = "1.8.3"
+ required_providers {
+ google = {
+ source = "hashicorp/google"
+ version = "6.17.0"
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/multicloud/modules/gke/variables.tf b/test/multicloud/modules/gke/variables.tf
new file mode 100644
index 0000000000..9971acce00
--- /dev/null
+++ b/test/multicloud/modules/gke/variables.tf
@@ -0,0 +1,19 @@
+variable "project" {
+ description = "The Google Cloud project where resources will be deployed."
+ type = string
+}
+
+variable "location" {
+ description = "The Google Cloud location where GKE will be deployed to."
+ type = string
+}
+
+variable "prefix" {
+ description = "A prefix to add to all resources."
+ type = string
+}
+
+variable "machine_type" {
+ description = "The machine type to use for the GKE nodes."
+ type = string
+}
\ No newline at end of file
diff --git a/test/multicloud/modules/kind/main.tf b/test/multicloud/modules/kind/main.tf
new file mode 100644
index 0000000000..42b66b4b7b
--- /dev/null
+++ b/test/multicloud/modules/kind/main.tf
@@ -0,0 +1,20 @@
+resource "kind_cluster" "kind" {
+ name = "${var.prefix}-kind"
+ wait_for_ready = true
+ kind_config {
+ kind = "Cluster"
+ api_version = "kind.x-k8s.io/v1alpha4"
+
+ node {
+ role = "control-plane"
+ }
+
+ node {
+ role = "worker"
+ }
+
+ node {
+ role = "worker"
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/multicloud/modules/kind/output.tf b/test/multicloud/modules/kind/output.tf
new file mode 100644
index 0000000000..1b5b5f3698
--- /dev/null
+++ b/test/multicloud/modules/kind/output.tf
@@ -0,0 +1,24 @@
+output "kubeconfig" {
+ value = kind_cluster.kind.kubeconfig
+ sensitive = true
+}
+
+output "host" {
+ value = kind_cluster.kind.endpoint
+ sensitive = true
+}
+
+output "client_certificate" {
+ value = kind_cluster.kind.client_certificate
+ sensitive = true
+}
+
+output "client_key" {
+ value = kind_cluster.kind.client_key
+ sensitive = true
+}
+
+output "cluster_ca_certificate" {
+ value = kind_cluster.kind.cluster_ca_certificate
+ sensitive = true
+}
\ No newline at end of file
diff --git a/test/multicloud/modules/kind/provider.tf b/test/multicloud/modules/kind/provider.tf
new file mode 100644
index 0000000000..9a655cac22
--- /dev/null
+++ b/test/multicloud/modules/kind/provider.tf
@@ -0,0 +1,9 @@
+terraform {
+ required_version = "1.8.3"
+ required_providers {
+ kind = {
+ source = "tehcyx/kind"
+ version = "0.7.0"
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/multicloud/modules/kind/variables.tf b/test/multicloud/modules/kind/variables.tf
new file mode 100644
index 0000000000..2dd9eaf4b0
--- /dev/null
+++ b/test/multicloud/modules/kind/variables.tf
@@ -0,0 +1,4 @@
+variable "prefix" {
+ description = "A prefix to add to all resources."
+ type = string
+}
\ No newline at end of file
diff --git a/test/multicloud/modules/retina/main.tf b/test/multicloud/modules/retina/main.tf
new file mode 100644
index 0000000000..3d0d0f3400
--- /dev/null
+++ b/test/multicloud/modules/retina/main.tf
@@ -0,0 +1,15 @@
+resource "helm_release" "retina" {
+ name = "retina"
+ repository = "oci://ghcr.io/microsoft/retina/charts"
+ chart = "retina"
+ version = var.retina_version
+ namespace = "kube-system"
+
+ dynamic "set" {
+ for_each = var.values
+ content {
+ name = set.value.name
+ value = set.value.value
+ }
+ }
+}
diff --git a/test/multicloud/modules/retina/provider.tf b/test/multicloud/modules/retina/provider.tf
new file mode 100644
index 0000000000..83de5829ac
--- /dev/null
+++ b/test/multicloud/modules/retina/provider.tf
@@ -0,0 +1,8 @@
+terraform {
+ required_providers {
+ helm = {
+ source = "hashicorp/helm"
+ version = "2.17.0"
+ }
+ }
+}
diff --git a/test/multicloud/modules/retina/variables.tf b/test/multicloud/modules/retina/variables.tf
new file mode 100644
index 0000000000..daaa2b717e
--- /dev/null
+++ b/test/multicloud/modules/retina/variables.tf
@@ -0,0 +1,27 @@
+variable "retina_version" {
+ description = "The tag to apply to all resources."
+ type = string
+ default = "v0.0.23"
+}
+
+variable "values" {
+ description = "Configuration for set blocks, this corresponds to Helm values.yaml"
+ type = list(object({
+ name = string
+ value = string
+ }))
+ default = [
+ {
+ name = "image.tag"
+ value = "v0.0.23"
+ },
+ {
+ name = "operator.tag"
+ value = "v0.0.23"
+ },
+ {
+ name = "logLevel"
+ value = "info"
+ }
+ ]
+}
diff --git a/test/multicloud/test/example_aks_test.go b/test/multicloud/test/example_aks_test.go
new file mode 100644
index 0000000000..7bbdf6fa4d
--- /dev/null
+++ b/test/multicloud/test/example_aks_test.go
@@ -0,0 +1,38 @@
+package test
+
+import (
+ "testing"
+
+ "github.com/gruntwork-io/terratest/modules/terraform"
+)
+
+func TestAKSExample(t *testing.T) {
+ t.Parallel()
+
+ opts := &terraform.Options{
+ TerraformDir: "../examples/aks",
+
+ Vars: map[string]interface{}{
+ "prefix": "test",
+ "location": "uksouth",
+ "subscription_id": "d6050d84-e4dd-463d-afc7-a6ab3dc33ab7", // TODO: replace with actual project once we get azure "public" access
+ "tenant_id": "ac8a4ccd-35f1-4f95-a688-f68e3d89adfc",
+ "resource_group_name": "test",
+ "labels": map[string]string{
+ "environment": "test",
+ "owner": "test",
+ "project": "test",
+ },
+ },
+ }
+
+ // clean up at the end of the test
+ defer terraform.Destroy(t, opts)
+
+ terraform.Init(t, opts)
+
+ // TODO: uncomment once we get creds for azure "public"
+ // terraform.Apply(t, opts)
+
+ // TODO: add actual tests here
+}
diff --git a/test/multicloud/test/example_gke_test.go b/test/multicloud/test/example_gke_test.go
new file mode 100644
index 0000000000..2f9980d0e8
--- /dev/null
+++ b/test/multicloud/test/example_gke_test.go
@@ -0,0 +1,54 @@
+package test
+
+import (
+ "testing"
+
+ "github.com/gruntwork-io/terratest/modules/terraform"
+)
+
+func TestGKEExample(t *testing.T) {
+ t.Parallel()
+
+ opts := &terraform.Options{
+ TerraformDir: "../examples/gke",
+
+ Vars: map[string]interface{}{
+ "prefix": "test",
+ "location": "europe-west2", // London
+ "project": "mc-retina", // TODO: replace with actual project once we get gcloud access
+ "machine_type": "e2-standard-4",
+ },
+ }
+
+ // clean up at the end of the test
+ defer terraform.Destroy(t, opts)
+
+ terraform.Init(t, opts)
+
+ // TODO: uncomment once we get creds for gcloud
+ // terraform.Apply(t, opts)
+
+ // // get outputs
+ // caCert := terraform.Output(t, opts, "cluster_ca_certificate")
+ // host := terraform.Output(t, opts, "host")
+ // token := terraform.Output(t, opts, "access_token")
+
+ // caCertString, err := decodeBase64(caCert)
+ // if err != nil {
+ // t.Fatalf("Failed to decode ca cert: %v", err)
+ // }
+
+ // // build the REST config
+ // restConfig := createRESTConfigWithBearer(caCertString, token, host)
+
+ // // create a Kubernetes clientset
+ // clientSet, err := buildClientSet(restConfig)
+ // if err != nil {
+ // t.Fatalf("Failed to create Kubernetes clientset: %v", err)
+ // }
+
+ // // test the cluster is accessible
+ // testClusterAccess(t, clientSet)
+
+ // // TODO: add more tests here
+}
diff --git a/test/multicloud/test/example_kind_test.go b/test/multicloud/test/example_kind_test.go
new file mode 100644
index 0000000000..f16affa4dd
--- /dev/null
+++ b/test/multicloud/test/example_kind_test.go
@@ -0,0 +1,43 @@
+package test
+
+import (
+ "testing"
+
+ "github.com/gruntwork-io/terratest/modules/terraform"
+)
+
+func TestKindExample(t *testing.T) {
+ t.Parallel()
+
+ opts := &terraform.Options{
+ TerraformDir: "../examples/kind",
+
+ Vars: map[string]interface{}{
+ "prefix": "test",
+ },
+ }
+
+ // clean up at the end of the test
+ defer terraform.Destroy(t, opts)
+
+ terraform.Init(t, opts)
+ terraform.Apply(t, opts)
+
+ // get outputs
+ caCert := terraform.Output(t, opts, "cluster_ca_certificate")
+ clientCert := terraform.Output(t, opts, "client_certificate")
+ clientKey := terraform.Output(t, opts, "client_key")
+ host := terraform.Output(t, opts, "host")
+
+ // build the REST config
+ restConfig := createRESTConfigWithClientCert(caCert, clientCert, clientKey, host)
+
+ // create a Kubernetes clientset
+ clientSet, err := buildClientSet(restConfig)
+ if err != nil {
+ t.Fatalf("Failed to create Kubernetes clientset: %v", err)
+ }
+
+ // test the cluster is accessible
+ testClusterAccess(t, clientSet)
+}
diff --git a/test/multicloud/test/go.mod b/test/multicloud/test/go.mod
new file mode 100644
index 0000000000..e768135864
--- /dev/null
+++ b/test/multicloud/test/go.mod
@@ -0,0 +1,73 @@
+module github.com/microsoft/retina/test/multicloud/test
+
+go 1.23.4
+
+require (
+ github.com/agext/levenshtein v1.2.3 // indirect
+ github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
+ github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
+ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
+ github.com/emicklei/go-restful/v3 v3.11.0 // indirect
+ github.com/fxamacker/cbor/v2 v2.7.0 // indirect
+ github.com/go-logr/logr v1.4.2 // indirect
+ github.com/go-openapi/jsonpointer v0.21.0 // indirect
+ github.com/go-openapi/jsonreference v0.20.2 // indirect
+ github.com/go-openapi/swag v0.23.0 // indirect
+ github.com/gogo/protobuf v1.3.2 // indirect
+ github.com/golang/protobuf v1.5.4 // indirect
+ github.com/google/gnostic-models v0.6.8 // indirect
+ github.com/google/go-cmp v0.6.0 // indirect
+ github.com/google/gofuzz v1.2.0 // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/gruntwork-io/terratest v0.48.1 // indirect
+ github.com/hashicorp/errwrap v1.0.0 // indirect
+ github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
+ github.com/hashicorp/go-getter/v2 v2.2.3 // indirect
+ github.com/hashicorp/go-multierror v1.1.1 // indirect
+ github.com/hashicorp/go-safetemp v1.0.0 // indirect
+ github.com/hashicorp/go-version v1.7.0 // indirect
+ github.com/hashicorp/hcl/v2 v2.22.0 // indirect
+ github.com/hashicorp/terraform-json v0.23.0 // indirect
+ github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a // indirect
+ github.com/josharian/intern v1.0.0 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/klauspost/compress v1.16.5 // indirect
+ github.com/mailru/easyjson v0.7.7 // indirect
+ github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326 // indirect
+ github.com/mitchellh/go-homedir v1.1.0 // indirect
+ github.com/mitchellh/go-testing-interface v1.14.1 // indirect
+ github.com/mitchellh/go-wordwrap v1.0.1 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+ github.com/pkg/errors v0.9.1 // indirect
+ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+ github.com/stretchr/testify v1.9.0 // indirect
+ github.com/tmccombs/hcl2json v0.6.4 // indirect
+ github.com/ulikunitz/xz v0.5.10 // indirect
+ github.com/x448/float16 v0.8.4 // indirect
+ github.com/zclconf/go-cty v1.15.0 // indirect
+ golang.org/x/crypto v0.29.0 // indirect
+ golang.org/x/mod v0.21.0 // indirect
+ golang.org/x/net v0.31.0 // indirect
+ golang.org/x/oauth2 v0.24.0 // indirect
+ golang.org/x/sync v0.9.0 // indirect
+ golang.org/x/sys v0.27.0 // indirect
+ golang.org/x/term v0.26.0 // indirect
+ golang.org/x/text v0.20.0 // indirect
+ golang.org/x/time v0.8.0 // indirect
+ golang.org/x/tools v0.26.0 // indirect
+ google.golang.org/protobuf v1.35.1 // indirect
+ gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
+ gopkg.in/inf.v0 v0.9.1 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+ k8s.io/api v0.32.1 // indirect
+ k8s.io/apimachinery v0.32.1 // indirect
+ k8s.io/client-go v0.32.1 // indirect
+ k8s.io/klog/v2 v2.130.1 // indirect
+ k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
+ k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
+ sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
+ sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
+ sigs.k8s.io/yaml v1.4.0 // indirect
+)
diff --git a/test/multicloud/test/go.sum b/test/multicloud/test/go.sum
new file mode 100644
index 0000000000..9990bc6862
--- /dev/null
+++ b/test/multicloud/test/go.sum
@@ -0,0 +1,192 @@
+github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
+github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
+github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
+github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
+github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
+github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
+github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
+github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
+github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
+github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
+github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
+github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
+github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
+github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
+github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
+github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gruntwork-io/terratest v0.48.1 h1:pnydDjkWbZCUYXvQkr24y21fBo8PfJC5hRGdwbl1eXM=
+github.com/gruntwork-io/terratest v0.48.1/go.mod h1:U2EQW4Odlz75XJUH16Kqkr9c93p+ZZtkpVez7GkZFa4=
+github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
+github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
+github.com/hashicorp/go-getter/v2 v2.2.3 h1:6CVzhT0KJQHqd9b0pK3xSP0CM/Cv+bVhk+jcaRJ2pGk=
+github.com/hashicorp/go-getter/v2 v2.2.3/go.mod h1:hp5Yy0GMQvwWVUmwLs3ygivz1JSLI323hdIE9J9m7TY=
+github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
+github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
+github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
+github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
+github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
+github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/hashicorp/hcl/v2 v2.22.0 h1:hkZ3nCtqeJsDhPRFz5EA9iwcG1hNWGePOTw6oyul12M=
+github.com/hashicorp/hcl/v2 v2.22.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
+github.com/hashicorp/terraform-json v0.23.0 h1:sniCkExU4iKtTADReHzACkk8fnpQXrdD2xoR+lppBkI=
+github.com/hashicorp/terraform-json v0.23.0/go.mod h1:MHdXbBAbSg0GvzuWazEGKAn/cyNfIB7mN6y7KJN6y2c=
+github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o=
+github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
+github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326 h1:ofNAzWCcyTALn2Zv40+8XitdzCgXY6e9qvXwN9W0YXg=
+github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
+github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
+github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
+github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
+github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/tmccombs/hcl2json v0.6.4 h1:/FWnzS9JCuyZ4MNwrG4vMrFrzRgsWEOVi+1AyYUVLGw=
+github.com/tmccombs/hcl2json v0.6.4/go.mod h1:+ppKlIW3H5nsAsZddXPy2iMyvld3SHxyjswOZhavRDk=
+github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
+github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
+github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
+github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/zclconf/go-cty v1.15.0 h1:tTCRWxsexYUmtt/wVxgDClUe+uQusuI443uL6e+5sXQ=
+github.com/zclconf/go-cty v1.15.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
+golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
+golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
+golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
+golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
+golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
+golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
+golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
+golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
+golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
+golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
+golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
+golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
+golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
+golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
+golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
+google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
+gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc=
+k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k=
+k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs=
+k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
+k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU=
+k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg=
+k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
+k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
+k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
+k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
+k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
+k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
+sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
+sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
+sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
+sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
+sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
diff --git a/test/multicloud/test/integration_retina_kind_test.go b/test/multicloud/test/integration_retina_kind_test.go
new file mode 100644
index 0000000000..110f6dc191
--- /dev/null
+++ b/test/multicloud/test/integration_retina_kind_test.go
@@ -0,0 +1,49 @@
+package test
+
+import (
+ "testing"
+
+ "github.com/gruntwork-io/terratest/modules/terraform"
+)
+
+func TestRetinaKindIntegration(t *testing.T) {
+ t.Parallel()
+
+ opts := &terraform.Options{
+ TerraformDir: "../examples/integration/retina-kind",
+
+ Vars: map[string]interface{}{
+ "prefix": "test-integration",
+ "retina_version": "v0.0.24",
+ },
+ }
+
+ // clean up at the end of the test
+ defer terraform.Destroy(t, opts)
+
+ terraform.Init(t, opts)
+ terraform.Apply(t, opts)
+
+ // get outputs
+ caCert := terraform.Output(t, opts, "cluster_ca_certificate")
+ clientCert := terraform.Output(t, opts, "client_certificate")
+ clientKey := terraform.Output(t, opts, "client_key")
+ host := terraform.Output(t, opts, "host")
+
+ // build the REST config
+ restConfig := createRESTConfigWithClientCert(caCert, clientCert, clientKey, host)
+
+ // create a Kubernetes clientset
+ clientSet, err := buildClientSet(restConfig)
+ if err != nil {
+ t.Fatalf("Failed to create Kubernetes clientset: %v", err)
+ }
+
+ // test the cluster is accessible
+ testClusterAccess(t, clientSet)
+
+ // check the retina pods logs for errors
+ checkRetinaLogs(t, clientSet)
+
+ // TODO: add more tests here
+}
diff --git a/test/multicloud/test/testUtils.go b/test/multicloud/test/testUtils.go
new file mode 100644
index 0000000000..dd1e24924e
--- /dev/null
+++ b/test/multicloud/test/testUtils.go
@@ -0,0 +1,120 @@
+package test
+
+import (
+ "bufio"
+ "context"
+ "encoding/base64"
+ "fmt"
+ "io"
+ "strings"
+ "testing"
+
+ v1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/client-go/kubernetes"
+ "k8s.io/client-go/rest"
+)
+
+func buildClientSet(config *rest.Config) (*kubernetes.Clientset, error) {
+ // Create a Kubernetes client
+ clientset, err := kubernetes.NewForConfig(config)
+ if err != nil {
+ return nil, err
+ }
+ return clientset, nil
+}
+
+// Create a Bearer token REST config
+func createRESTConfigWithBearer(caCert, bearerToken, host string) *rest.Config {
+ config := &rest.Config{
+ Host: host,
+ BearerToken: bearerToken,
+ TLSClientConfig: rest.TLSClientConfig{
+ CAData: []byte(caCert),
+ },
+ }
+ return config
+}
+
+// Create REST config with client cert and key
+func createRESTConfigWithClientCert(caCert, clientCert, clientKey, host string) *rest.Config {
+ config := &rest.Config{
+ Host: host,
+ TLSClientConfig: rest.TLSClientConfig{
+ CAData: []byte(caCert),
+ CertData: []byte(clientCert),
+ KeyData: []byte(clientKey),
+ },
+ }
+ return config
+}
+
+func testClusterAccess(t *testing.T, clientset *kubernetes.Clientset) {
+ // Test the cluster is accessible by listing nodes
+ _, err := clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
+ if err != nil {
+ t.Fatalf("Failed to list nodes: %v", err)
+ }
+ // Test the cluster is accessible by listing namespaces
+ _, err = clientset.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{})
+ if err != nil {
+ t.Fatalf("Failed to list namespaces: %v", err)
+ }
+}
+
+func checkLogsForErrors(logs io.ReadCloser) error {
+ scanner := bufio.NewScanner(logs)
+ for scanner.Scan() {
+ line := scanner.Text()
+ // print a debug line
+ fmt.Printf("Log line: %s\n", line)
+ // Check if the line contains the word "error"
+ if strings.Contains(strings.ToLower(line), "error") {
+ // create a new error with the log line
+ return fmt.Errorf("Error found in logs: %s", line)
+ }
+ }
+ // Check for any scanner errors
+ if err := scanner.Err(); err != nil {
+ return err
+ }
+ return nil
+}
+
+func checkRetinaLogs(t *testing.T, clientset *kubernetes.Clientset) {
+ // Get the logs for the retina pods
+ pods, err := clientset.CoreV1().Pods("kube-system").List(context.TODO(), metav1.ListOptions{
+ LabelSelector: "k8s-app=retina",
+ })
+ if err != nil {
+ t.Fatalf("Failed to list pods: %v", err)
+ }
+ // Stream the logs for each pod
+ for _, pod := range pods.Items {
+ // Get the logs for the pod
+ req := clientset.CoreV1().Pods("kube-system").GetLogs(pod.Name, &v1.PodLogOptions{})
+ // Stream the logs
+ logs, err := req.Stream(context.Background())
+ if err != nil {
+ t.Fatalf("Failed to get logs for pod %s: %v", pod.Name, err)
+ }
+ // Check the logs for errors
+ err = checkLogsForErrors(logs)
+ if err != nil {
+ t.Fatalf("Failed to check logs for errors: %v", err)
+ }
+ // Close the logs stream
+ logs.Close()
+ }
+}
+
+// function to convert base64 encoded string to plain text
+func decodeBase64(encoded string) (string, error) {
+ // decode the base64 encoded string
+ decoded, err := base64.StdEncoding.DecodeString(encoded)
+ if err != nil {
+ return "", err
+ }
+ // return the decoded string
+ return string(decoded), nil
+}
From ca5f3623a39dbbf1261eba606e4e1a208d7dad84 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 29 Jan 2025 10:30:42 +0000
Subject: [PATCH 2/2] build(deps): bump the go_modules group across 1 directory
with 2 updates (#1279)
Bumps the go_modules group with 2 updates in the /test/multicloud/test
directory: [golang.org/x/crypto](https://github.com/golang/crypto) and
[golang.org/x/net](https://github.com/golang/net).
Updates `golang.org/x/crypto` from 0.29.0 to 0.31.0
Commits
b4f1988
ssh: make the public key cache a 1-entry FIFO cache
7042ebc
openpgp/clearsign: just use rand.Reader in tests
3e90321
go.mod: update golang.org/x dependencies
8c4e668
x509roots/fallback: update bundle
- See full diff in compare
view
Updates `golang.org/x/net` from 0.31.0 to 0.33.0
Commits
dfc720d
go.mod: update golang.org/x dependencies
8e66b04
html: use strings.EqualFold instead of lowering ourselves
b935f7b
html: avoid endless loop on error token
9af49ef
route: remove unused sizeof* consts
6705db9
quic: clean up crypto streams when dropping packet protection keys
4ef7588
quic: handle ACK frame in packet which drops number space
552d8ac
Revert "route: change from syscall to x/sys/unix"
13a7c01
Revert "route: remove unused sizeof* consts on freebsd"
285e1cf
go.mod: update golang.org/x dependencies
d0a1049
route: remove unused sizeof* consts on freebsd
- Additional commits viewable in compare
view
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore ` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore ` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore ` will
remove the ignore condition of the specified dependency and ignore
conditions
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/microsoft/retina/network/alerts).
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
test/multicloud/test/go.mod | 23 +++++++++--------
test/multicloud/test/go.sum | 50 ++++++++++++++++++++++++-------------
2 files changed, 45 insertions(+), 28 deletions(-)
diff --git a/test/multicloud/test/go.mod b/test/multicloud/test/go.mod
index e768135864..e6c1f26850 100644
--- a/test/multicloud/test/go.mod
+++ b/test/multicloud/test/go.mod
@@ -2,6 +2,13 @@ module github.com/microsoft/retina/test/multicloud/test
go 1.23.4
+require (
+ github.com/gruntwork-io/terratest v0.48.1
+ k8s.io/api v0.32.1
+ k8s.io/apimachinery v0.32.1
+ k8s.io/client-go v0.32.1
+)
+
require (
github.com/agext/levenshtein v1.2.3 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
@@ -19,7 +26,6 @@ require (
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.6.0 // indirect
- github.com/gruntwork-io/terratest v0.48.1 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-getter/v2 v2.2.3 // indirect
@@ -47,23 +53,20 @@ require (
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/zclconf/go-cty v1.15.0 // indirect
- golang.org/x/crypto v0.29.0 // indirect
+ golang.org/x/crypto v0.31.0 // indirect
golang.org/x/mod v0.21.0 // indirect
- golang.org/x/net v0.31.0 // indirect
+ golang.org/x/net v0.33.0 // indirect
golang.org/x/oauth2 v0.24.0 // indirect
- golang.org/x/sync v0.9.0 // indirect
- golang.org/x/sys v0.27.0 // indirect
- golang.org/x/term v0.26.0 // indirect
- golang.org/x/text v0.20.0 // indirect
+ golang.org/x/sync v0.10.0 // indirect
+ golang.org/x/sys v0.28.0 // indirect
+ golang.org/x/term v0.27.0 // indirect
+ golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.8.0 // indirect
golang.org/x/tools v0.26.0 // indirect
google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
- k8s.io/api v0.32.1 // indirect
- k8s.io/apimachinery v0.32.1 // indirect
- k8s.io/client-go v0.32.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
diff --git a/test/multicloud/test/go.sum b/test/multicloud/test/go.sum
index 9990bc6862..0506d6d8ef 100644
--- a/test/multicloud/test/go.sum
+++ b/test/multicloud/test/go.sum
@@ -6,7 +6,6 @@ github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1U
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -24,6 +23,10 @@ github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
+github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
+github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
+github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M=
+github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
@@ -36,6 +39,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
+github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gruntwork-io/terratest v0.48.1 h1:pnydDjkWbZCUYXvQkr24y21fBo8PfJC5hRGdwbl1eXM=
@@ -67,8 +72,11 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
@@ -87,12 +95,19 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
+github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
+github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
+github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
+github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -112,49 +127,47 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/zclconf/go-cty v1.15.0 h1:tTCRWxsexYUmtt/wVxgDClUe+uQusuI443uL6e+5sXQ=
github.com/zclconf/go-cty v1.15.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
+github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
+github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
-golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
+golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
+golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
-golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
-golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
+golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
+golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
-golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
+golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
-golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
-golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
+golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
+golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
+golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
-golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
+golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
+golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
-golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -164,6 +177,7 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=