@intentius/behold 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +757 -26
- package/LICENSE +190 -0
- package/README.md +251 -187
- package/THIRD_PARTY.md +75 -0
- package/demos.json +80 -9
- package/dist/cli.js +5479 -1673
- package/example-argo-estate/package-lock.json +6 -6
- package/example-carve/app/package-lock.json +3 -3
- package/example-choudoufu-estate/.behold.json +8 -0
- package/example-choudoufu-estate/README.md +48 -0
- package/example-choudoufu-estate/monolith/behaviour.live.json +495 -0
- package/example-choudoufu-estate/monolith/carve.json +12 -0
- package/example-choudoufu-estate/monolith/main.tf +127 -0
- package/example-choudoufu-estate/scripts/choudoufu-down.sh +8 -0
- package/example-choudoufu-estate/scripts/choudoufu-up.sh +62 -0
- package/example-choudoufu-estate/team-a/behaviour.live.json +179 -0
- package/example-choudoufu-estate/team-a/main.tf +58 -0
- package/example-choudoufu-estate/team-b/behaviour.live.json +179 -0
- package/example-choudoufu-estate/team-b/main.tf +73 -0
- package/example-choudoufu-estate/team-c/behaviour.live.json +179 -0
- package/example-choudoufu-estate/team-c/main.tf +51 -0
- package/example-flux-estate/package-lock.json +6 -6
- package/example-k8s/package-lock.json +6 -6
- package/example-terraform-estate/README.md +109 -0
- package/example-terraform-estate/backends/backend.s3.tf +10 -0
- package/example-terraform-estate/envs/dev/README.md +5 -0
- package/example-terraform-estate/envs/prod/data.tf +18 -0
- package/example-terraform-estate/envs/prod/network.tf +80 -0
- package/example-terraform-estate/envs/prod/outputs.tf +13 -0
- package/example-terraform-estate/envs/prod/provider.tf +10 -0
- package/example-terraform-estate/envs/prod/services.tf +21 -0
- package/example-terraform-estate/envs/prod/storage.tf +31 -0
- package/example-terraform-estate/envs/prod/variables.tf +17 -0
- package/example-terraform-estate/envs/prod/versions.tf +10 -0
- package/example-terraform-estate/modules/service/main.tf +58 -0
- package/example-terraform-estate/modules/service/outputs.tf +7 -0
- package/example-terraform-estate/modules/service/variables.tf +16 -0
- package/example-terraform-estate/modules/service/versions.tf +10 -0
- package/example-terraform-estate/platform/iam.tf +40 -0
- package/example-terraform-estate/platform/kms.tf +16 -0
- package/example-terraform-estate/platform/outputs.tf +7 -0
- package/example-terraform-estate/platform/variables.tf +10 -0
- package/example-terraform-estate/platform/versions.tf +10 -0
- package/example-writes/package-lock.json +3 -3
- package/licenses/cncf-artwork-LICENSE.md +13 -0
- package/licenses/iTerm2-Color-Schemes-LICENSE +31 -0
- package/licenses/kubernetes-icons-LICENSE +201 -0
- package/package.json +25 -7
- package/web/app.js +1309 -41
- package/web/behaviour-scale.js +380 -0
- package/web/behaviour-scale.test.js +307 -0
- package/web/cross-env.js +58 -0
- package/web/cross-env.test.js +61 -0
- package/web/index.html +62 -2
- package/web/pending.js +67 -0
- package/web/pending.test.js +62 -0
- package/web/read-cost.js +49 -0
- package/web/read-cost.test.js +76 -0
- package/web/refresh-queue.js +26 -0
- package/web/refresh-queue.test.js +54 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"engine": "behold-fixture",
|
|
4
|
+
"version": "0",
|
|
5
|
+
"at": {
|
|
6
|
+
"traffic": "100 rps, p50"
|
|
7
|
+
}
|
|
8
|
+
},
|
|
9
|
+
"entities": {
|
|
10
|
+
"aws_iam_role.team_c": {
|
|
11
|
+
"at": {
|
|
12
|
+
"traffic": "100 rps, p50"
|
|
13
|
+
},
|
|
14
|
+
"cost": {
|
|
15
|
+
"perHour": 0.0004,
|
|
16
|
+
"currency": "USD"
|
|
17
|
+
},
|
|
18
|
+
"headroom": {
|
|
19
|
+
"cpu": 0.9,
|
|
20
|
+
"latency": 0.85
|
|
21
|
+
},
|
|
22
|
+
"errorRate": 0.001,
|
|
23
|
+
"resilience": {
|
|
24
|
+
"failure": "one zone lost",
|
|
25
|
+
"verdict": "survives"
|
|
26
|
+
},
|
|
27
|
+
"provenance": {
|
|
28
|
+
"engine": "behold-fixture",
|
|
29
|
+
"version": "0",
|
|
30
|
+
"tolerance": "fixture",
|
|
31
|
+
"basis": "modeled"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"aws_iam_role_policy.team_c_inline": {
|
|
35
|
+
"at": {
|
|
36
|
+
"traffic": "100 rps, p50"
|
|
37
|
+
},
|
|
38
|
+
"cost": {
|
|
39
|
+
"perHour": 0.0002,
|
|
40
|
+
"currency": "USD"
|
|
41
|
+
},
|
|
42
|
+
"headroom": {
|
|
43
|
+
"cpu": 0.84,
|
|
44
|
+
"latency": 0.76
|
|
45
|
+
},
|
|
46
|
+
"errorRate": 0.001,
|
|
47
|
+
"resilience": {
|
|
48
|
+
"failure": "one zone lost",
|
|
49
|
+
"verdict": "survives"
|
|
50
|
+
},
|
|
51
|
+
"provenance": {
|
|
52
|
+
"engine": "behold-fixture",
|
|
53
|
+
"version": "0",
|
|
54
|
+
"tolerance": "fixture",
|
|
55
|
+
"basis": "modeled"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"aws_iam_policy.team_c": {
|
|
59
|
+
"at": {
|
|
60
|
+
"traffic": "100 rps, p50"
|
|
61
|
+
},
|
|
62
|
+
"cost": {
|
|
63
|
+
"perHour": 0.0002,
|
|
64
|
+
"currency": "USD"
|
|
65
|
+
},
|
|
66
|
+
"headroom": {
|
|
67
|
+
"cpu": 0.78,
|
|
68
|
+
"latency": 0.67
|
|
69
|
+
},
|
|
70
|
+
"errorRate": 0.001,
|
|
71
|
+
"resilience": {
|
|
72
|
+
"failure": "one zone lost",
|
|
73
|
+
"verdict": "survives"
|
|
74
|
+
},
|
|
75
|
+
"provenance": {
|
|
76
|
+
"engine": "behold-fixture",
|
|
77
|
+
"version": "0",
|
|
78
|
+
"tolerance": "fixture",
|
|
79
|
+
"basis": "modeled"
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"aws_iam_role_policy_attachment.team_c": {
|
|
83
|
+
"at": {
|
|
84
|
+
"traffic": "100 rps, p50"
|
|
85
|
+
},
|
|
86
|
+
"cost": {
|
|
87
|
+
"perHour": 0.0001,
|
|
88
|
+
"currency": "USD"
|
|
89
|
+
},
|
|
90
|
+
"headroom": {
|
|
91
|
+
"cpu": 0.72,
|
|
92
|
+
"latency": 0.58
|
|
93
|
+
},
|
|
94
|
+
"errorRate": 0.001,
|
|
95
|
+
"resilience": {
|
|
96
|
+
"failure": "one zone lost",
|
|
97
|
+
"verdict": "survives"
|
|
98
|
+
},
|
|
99
|
+
"provenance": {
|
|
100
|
+
"engine": "behold-fixture",
|
|
101
|
+
"version": "0",
|
|
102
|
+
"tolerance": "fixture",
|
|
103
|
+
"basis": "modeled"
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"aws_cloudwatch_log_group.team_c_0": {
|
|
107
|
+
"at": {
|
|
108
|
+
"traffic": "100 rps, p50"
|
|
109
|
+
},
|
|
110
|
+
"cost": {
|
|
111
|
+
"perHour": 0.0416,
|
|
112
|
+
"currency": "USD"
|
|
113
|
+
},
|
|
114
|
+
"headroom": {
|
|
115
|
+
"cpu": 0.66,
|
|
116
|
+
"latency": 0.49
|
|
117
|
+
},
|
|
118
|
+
"errorRate": 0.0004,
|
|
119
|
+
"resilience": {
|
|
120
|
+
"failure": "one zone lost",
|
|
121
|
+
"verdict": "survives"
|
|
122
|
+
},
|
|
123
|
+
"provenance": {
|
|
124
|
+
"engine": "behold-fixture",
|
|
125
|
+
"version": "0",
|
|
126
|
+
"tolerance": "fixture",
|
|
127
|
+
"basis": "modeled"
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
"aws_cloudwatch_log_group.team_c_1": {
|
|
131
|
+
"at": {
|
|
132
|
+
"traffic": "100 rps, p50"
|
|
133
|
+
},
|
|
134
|
+
"cost": {
|
|
135
|
+
"perHour": 0.0312,
|
|
136
|
+
"currency": "USD"
|
|
137
|
+
},
|
|
138
|
+
"headroom": {
|
|
139
|
+
"cpu": 0.6,
|
|
140
|
+
"latency": 0.85
|
|
141
|
+
},
|
|
142
|
+
"errorRate": 0.0004,
|
|
143
|
+
"resilience": {
|
|
144
|
+
"failure": "one zone lost",
|
|
145
|
+
"verdict": "survives"
|
|
146
|
+
},
|
|
147
|
+
"provenance": {
|
|
148
|
+
"engine": "behold-fixture",
|
|
149
|
+
"version": "0",
|
|
150
|
+
"tolerance": "fixture",
|
|
151
|
+
"basis": "modeled"
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
"aws_cloudwatch_log_group.team_c_2": {
|
|
155
|
+
"at": {
|
|
156
|
+
"traffic": "100 rps, p50"
|
|
157
|
+
},
|
|
158
|
+
"cost": {
|
|
159
|
+
"perHour": 0.0208,
|
|
160
|
+
"currency": "USD"
|
|
161
|
+
},
|
|
162
|
+
"headroom": {
|
|
163
|
+
"cpu": 0.54,
|
|
164
|
+
"latency": 0.76
|
|
165
|
+
},
|
|
166
|
+
"errorRate": 0.0004,
|
|
167
|
+
"resilience": {
|
|
168
|
+
"failure": "one zone lost",
|
|
169
|
+
"verdict": "survives"
|
|
170
|
+
},
|
|
171
|
+
"provenance": {
|
|
172
|
+
"engine": "behold-fixture",
|
|
173
|
+
"version": "0",
|
|
174
|
+
"tolerance": "fixture",
|
|
175
|
+
"basis": "modeled"
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
terraform {
|
|
2
|
+
required_providers {
|
|
3
|
+
aws = {
|
|
4
|
+
source = "hashicorp/aws"
|
|
5
|
+
version = "= 6.58.0"
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
live {
|
|
9
|
+
estate = "tlmig-sample-team-c"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
provider "aws" {
|
|
14
|
+
region = "us-east-1"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
resource "aws_iam_role" "team_c" {
|
|
18
|
+
name = "tlmig-sample-team-c-role"
|
|
19
|
+
assume_role_policy = jsonencode({ Version = "2012-10-17", Statement = [{ Effect = "Allow", Principal = { Service = "ec2.amazonaws.com" }, Action = "sts:AssumeRole" }] })
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
resource "aws_iam_role_policy" "team_c_inline" {
|
|
23
|
+
name = "tlmig-sample-team-c-inline"
|
|
24
|
+
role = aws_iam_role.team_c.id
|
|
25
|
+
policy = jsonencode({ Version = "2012-10-17", Statement = [{ Effect = "Allow", Action = ["logs:CreateLogStream"], Resource = "*" }] })
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
resource "aws_iam_policy" "team_c" {
|
|
29
|
+
name = "tlmig-sample-team-c-policy"
|
|
30
|
+
policy = jsonencode({ Version = "2012-10-17", Statement = [{ Effect = "Allow", Action = ["logs:PutLogEvents"], Resource = "*" }] })
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
resource "aws_iam_role_policy_attachment" "team_c" {
|
|
34
|
+
role = aws_iam_role.team_c.name
|
|
35
|
+
policy_arn = aws_iam_policy.team_c.arn
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
resource "aws_cloudwatch_log_group" "team_c_0" {
|
|
39
|
+
name = "/tlmig-sample/team-c/svc-0"
|
|
40
|
+
retention_in_days = 1
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
resource "aws_cloudwatch_log_group" "team_c_1" {
|
|
44
|
+
name = "/tlmig-sample/team-c/svc-1"
|
|
45
|
+
retention_in_days = 1
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
resource "aws_cloudwatch_log_group" "team_c_2" {
|
|
49
|
+
name = "/tlmig-sample/team-c/svc-2"
|
|
50
|
+
retention_in_days = 1
|
|
51
|
+
}
|
|
@@ -561,9 +561,9 @@
|
|
|
561
561
|
}
|
|
562
562
|
},
|
|
563
563
|
"node_modules/@kubernetes/client-node/node_modules/js-yaml": {
|
|
564
|
-
"version": "5.4.
|
|
565
|
-
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.4.
|
|
566
|
-
"integrity": "sha512-
|
|
564
|
+
"version": "5.4.2",
|
|
565
|
+
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.4.2.tgz",
|
|
566
|
+
"integrity": "sha512-m+aqu+LwO1O6sIopafj8HUVl5aawITwZQe/yHpMCKjaWBaA/d07B/QdMb3529REftiU+RMMHL3Vlsw3hON7vWg==",
|
|
567
567
|
"funding": [
|
|
568
568
|
{
|
|
569
569
|
"type": "github",
|
|
@@ -1113,9 +1113,9 @@
|
|
|
1113
1113
|
}
|
|
1114
1114
|
},
|
|
1115
1115
|
"node_modules/js-yaml": {
|
|
1116
|
-
"version": "4.3.
|
|
1117
|
-
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.
|
|
1118
|
-
"integrity": "sha512-
|
|
1116
|
+
"version": "4.3.2",
|
|
1117
|
+
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz",
|
|
1118
|
+
"integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==",
|
|
1119
1119
|
"funding": [
|
|
1120
1120
|
{
|
|
1121
1121
|
"type": "github",
|
|
@@ -555,9 +555,9 @@
|
|
|
555
555
|
}
|
|
556
556
|
},
|
|
557
557
|
"node_modules/@kubernetes/client-node/node_modules/js-yaml": {
|
|
558
|
-
"version": "5.4.
|
|
559
|
-
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.4.
|
|
560
|
-
"integrity": "sha512-
|
|
558
|
+
"version": "5.4.2",
|
|
559
|
+
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.4.2.tgz",
|
|
560
|
+
"integrity": "sha512-m+aqu+LwO1O6sIopafj8HUVl5aawITwZQe/yHpMCKjaWBaA/d07B/QdMb3529REftiU+RMMHL3Vlsw3hON7vWg==",
|
|
561
561
|
"funding": [
|
|
562
562
|
{
|
|
563
563
|
"type": "github",
|
|
@@ -1187,9 +1187,9 @@
|
|
|
1187
1187
|
}
|
|
1188
1188
|
},
|
|
1189
1189
|
"node_modules/js-yaml": {
|
|
1190
|
-
"version": "4.3.
|
|
1191
|
-
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.
|
|
1192
|
-
"integrity": "sha512-
|
|
1190
|
+
"version": "4.3.2",
|
|
1191
|
+
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz",
|
|
1192
|
+
"integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==",
|
|
1193
1193
|
"funding": [
|
|
1194
1194
|
{
|
|
1195
1195
|
"type": "github",
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# example-terraform-estate
|
|
2
|
+
|
|
3
|
+
A multi-root Terraform estate, declared only. No cloud account, no Docker, no
|
|
4
|
+
`terraform` binary — behold reads the HCL through chant's terraform lexicon and
|
|
5
|
+
draws what it finds.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npx @intentius/behold demo terraform-estate
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## What it declares
|
|
12
|
+
|
|
13
|
+
Two roots that are applied separately, plus the shared module both are built
|
|
14
|
+
on. 25 resources, drawing 37 cards and 31 edges at the `resources` zoom.
|
|
15
|
+
|
|
16
|
+
| directory | what it is |
|
|
17
|
+
|---|---|
|
|
18
|
+
| `envs/prod` | The environment root: a VPC with three subnets, a route table and an edge security group, an artifacts bucket with its versioning/public-access/encryption siblings, and two calls to the shared service module. |
|
|
19
|
+
| `platform` | The estate-wide root, applied separately: the permissions boundary every service role attaches, a deploy role, the KMS key and alias, an audit log group. It declares **no provider block** — it inherits one where it is used. |
|
|
20
|
+
| `modules/service` | The shared module `envs/prod` calls twice. An IAM role and inline policy, a log group, a work queue with its dead-letter queue, a task security group. |
|
|
21
|
+
| `backends` | A backend fragment, copied into a root when one is initialised. |
|
|
22
|
+
| `envs/dev` | Not built yet. A README and no `.tf`. |
|
|
23
|
+
|
|
24
|
+
## What each directory is there to show
|
|
25
|
+
|
|
26
|
+
Discovery has to tell five things apart, and this estate is built to be each of
|
|
27
|
+
them. A root is a directory with a `.tf` declaring a line-start `terraform {` or
|
|
28
|
+
`provider "` block beside a `resource`, `data` or `module` block — a regex probe
|
|
29
|
+
at depth, never an HCL parse.
|
|
30
|
+
|
|
31
|
+
**`envs/prod` is the obvious root.** Provider block, resources, module calls.
|
|
32
|
+
|
|
33
|
+
**`platform` is the root that has no provider block.** The naive reading of "a
|
|
34
|
+
root configures a provider" would miss it. It is a root because it declares
|
|
35
|
+
resources beside a `terraform` block, and it is applied on its own.
|
|
36
|
+
|
|
37
|
+
**`modules/service` is the case that defeats the probe.** Its `versions.tf` is
|
|
38
|
+
byte-for-byte `envs/prod/versions.tf` — the same `required_version`, the same
|
|
39
|
+
`required_providers`. Nothing in the file's content says which of the two is a
|
|
40
|
+
root. What separates them is that this one sits under a `modules/` segment,
|
|
41
|
+
Terraform's own standard module structure, and the roots that call it draw its
|
|
42
|
+
blocks already. Drawing it again would double every card in it.
|
|
43
|
+
|
|
44
|
+
**`backends` has a `terraform` block and nothing to draw.** No resource, no
|
|
45
|
+
data source, no module call. It is reported as skipped rather than drawn as an
|
|
46
|
+
empty box — a box with nothing in it is a question, and the note answers it.
|
|
47
|
+
|
|
48
|
+
**`envs/dev` is neither drawn nor reported.** It holds no `.tf` at all, so
|
|
49
|
+
there is nothing here that looks like Terraform and nothing to say about it.
|
|
50
|
+
Silence is the right answer; reporting it would be noise about every directory
|
|
51
|
+
in every repository.
|
|
52
|
+
|
|
53
|
+
The graph's status line says all of this:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
2 roots — prod, platform; skipped backends (no resource, data or module block —
|
|
57
|
+
nothing to draw), modules/service (called as a module, never applied on its own)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## The edge that is not drawn
|
|
61
|
+
|
|
62
|
+
`platform` declares the KMS key and its alias. `envs/prod` reads it back:
|
|
63
|
+
|
|
64
|
+
```hcl
|
|
65
|
+
data "aws_kms_key" "estate" {
|
|
66
|
+
key_id = "alias/${var.name}-estate"
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
That is a real cross-root reference, and it is how a multi-root estate is
|
|
71
|
+
actually wired — two roots applied separately, neither a module of the other,
|
|
72
|
+
joined by a name one publishes and the other looks up.
|
|
73
|
+
|
|
74
|
+
**behold draws no edge for it**, and that is deliberate. Both ends carry the
|
|
75
|
+
same unresolved `${var.name}` interpolation, so matching them would be a
|
|
76
|
+
coincidence of variable naming rather than a relationship either root states.
|
|
77
|
+
It was measured on a real estate and refused ([#381]). The data source card says
|
|
78
|
+
what it reads instead, which is a claim the estate does make.
|
|
79
|
+
|
|
80
|
+
Module calls *do* draw: each `module` block expands into its own contents under
|
|
81
|
+
`module.checkout/…`, with the reference edges chant's lexicon resolves from the
|
|
82
|
+
HCL.
|
|
83
|
+
|
|
84
|
+
## It needs the lexicon
|
|
85
|
+
|
|
86
|
+
behold parses no HCL and ships no HCL parser. The reading goes through
|
|
87
|
+
`@intentius/chant-lexicon-terraform`, which loads `@cdktf/hcl2json` underneath —
|
|
88
|
+
a ~1.8 MB wasm blob. Both are declared as optional peers and behold does not
|
|
89
|
+
install them, so that every user who serves an ordinary chant project does not
|
|
90
|
+
carry an HCL parser they will never run:
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
npm install --no-save @intentius/chant-lexicon-terraform @cdktf/hcl2json
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Without them the demo refuses with that exact line and says where it looked.
|
|
97
|
+
|
|
98
|
+
## Not the carve demo
|
|
99
|
+
|
|
100
|
+
`behold demo carve` also involves Terraform and answers a different question.
|
|
101
|
+
This estate asks **what is in my Terraform** — draw it, box it by root, tell the
|
|
102
|
+
roots from the modules. The carve demo asks **what would it cost to leave**:
|
|
103
|
+
`chant carve advise` ranks each resource by peelability and the walkthrough
|
|
104
|
+
carves one across the line.
|
|
105
|
+
|
|
106
|
+
Nothing here is being migrated. There is no chant project beside it, no
|
|
107
|
+
peelability score on any card, and no stepper.
|
|
108
|
+
|
|
109
|
+
[#381]: https://github.com/INTENTIUS/behold/issues/381
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# A backend fragment, copied into a root when one is initialised. It declares
|
|
2
|
+
# a `terraform` block and nothing to draw, which is why discovery reports it as
|
|
3
|
+
# skipped rather than drawing an empty box for it.
|
|
4
|
+
terraform {
|
|
5
|
+
backend "s3" {
|
|
6
|
+
bucket = "waterline-tfstate"
|
|
7
|
+
key = "prod/terraform.tfstate"
|
|
8
|
+
region = "us-east-1"
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
data "aws_caller_identity" "current" {}
|
|
2
|
+
|
|
3
|
+
data "aws_region" "current" {}
|
|
4
|
+
|
|
5
|
+
# The cross-root read, and what behold does with it.
|
|
6
|
+
#
|
|
7
|
+
# `platform` declares this key and its alias; this root reads it back by alias
|
|
8
|
+
# rather than by a reference Terraform could resolve, because the two roots are
|
|
9
|
+
# applied separately and neither is a module of the other. That is how a real
|
|
10
|
+
# multi-root estate is wired, and it is also why NO EDGE is drawn between them:
|
|
11
|
+
# both ends carry the same unresolved `${var.name}` interpolation, so a match
|
|
12
|
+
# would be a coincidence of variable naming rather than a stated relationship.
|
|
13
|
+
# behold measured that and refused it (INTENTIUS/behold#381). What you get
|
|
14
|
+
# instead is this card, saying exactly what it reads — which is a claim the
|
|
15
|
+
# estate actually makes.
|
|
16
|
+
data "aws_kms_key" "estate" {
|
|
17
|
+
key_id = "alias/${var.name}-estate"
|
|
18
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
resource "aws_vpc" "main" {
|
|
2
|
+
cidr_block = var.vpc_cidr
|
|
3
|
+
enable_dns_hostnames = true
|
|
4
|
+
enable_dns_support = true
|
|
5
|
+
|
|
6
|
+
tags = { Name = "${var.name}-vpc" }
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
resource "aws_internet_gateway" "main" {
|
|
10
|
+
vpc_id = aws_vpc.main.id
|
|
11
|
+
|
|
12
|
+
tags = { Name = "${var.name}-igw" }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
resource "aws_subnet" "public_a" {
|
|
16
|
+
vpc_id = aws_vpc.main.id
|
|
17
|
+
cidr_block = cidrsubnet(var.vpc_cidr, 8, 0)
|
|
18
|
+
availability_zone = "${var.region}a"
|
|
19
|
+
map_public_ip_on_launch = true
|
|
20
|
+
|
|
21
|
+
tags = { Name = "${var.name}-public-a" }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
resource "aws_subnet" "public_b" {
|
|
25
|
+
vpc_id = aws_vpc.main.id
|
|
26
|
+
cidr_block = cidrsubnet(var.vpc_cidr, 8, 1)
|
|
27
|
+
availability_zone = "${var.region}b"
|
|
28
|
+
map_public_ip_on_launch = true
|
|
29
|
+
|
|
30
|
+
tags = { Name = "${var.name}-public-b" }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
resource "aws_subnet" "private_a" {
|
|
34
|
+
vpc_id = aws_vpc.main.id
|
|
35
|
+
cidr_block = cidrsubnet(var.vpc_cidr, 8, 10)
|
|
36
|
+
availability_zone = "${var.region}a"
|
|
37
|
+
|
|
38
|
+
tags = { Name = "${var.name}-private-a" }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
resource "aws_route_table" "public" {
|
|
42
|
+
vpc_id = aws_vpc.main.id
|
|
43
|
+
|
|
44
|
+
route {
|
|
45
|
+
cidr_block = "0.0.0.0/0"
|
|
46
|
+
gateway_id = aws_internet_gateway.main.id
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
tags = { Name = "${var.name}-public" }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
resource "aws_route_table_association" "public_a" {
|
|
53
|
+
subnet_id = aws_subnet.public_a.id
|
|
54
|
+
route_table_id = aws_route_table.public.id
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
resource "aws_route_table_association" "public_b" {
|
|
58
|
+
subnet_id = aws_subnet.public_b.id
|
|
59
|
+
route_table_id = aws_route_table.public.id
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
resource "aws_security_group" "edge" {
|
|
63
|
+
name = "${var.name}-edge"
|
|
64
|
+
description = "Ingress for the public tier."
|
|
65
|
+
vpc_id = aws_vpc.main.id
|
|
66
|
+
|
|
67
|
+
ingress {
|
|
68
|
+
from_port = 443
|
|
69
|
+
to_port = 443
|
|
70
|
+
protocol = "tcp"
|
|
71
|
+
cidr_blocks = ["0.0.0.0/0"]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
egress {
|
|
75
|
+
from_port = 0
|
|
76
|
+
to_port = 0
|
|
77
|
+
protocol = "-1"
|
|
78
|
+
cidr_blocks = ["0.0.0.0/0"]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
output "vpc_id" {
|
|
2
|
+
description = "The network everything in this environment sits in."
|
|
3
|
+
value = aws_vpc.main.id
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
output "artifacts_bucket" {
|
|
7
|
+
value = aws_s3_bucket.artifacts.bucket
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
output "service_role_arns" {
|
|
11
|
+
description = "One per service module, for the platform root to reference back."
|
|
12
|
+
value = [module.checkout.role_arn, module.ledger.role_arn]
|
|
13
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module "checkout" {
|
|
2
|
+
source = "../../modules/service"
|
|
3
|
+
|
|
4
|
+
name = "${var.name}-checkout"
|
|
5
|
+
subnet_ids = [aws_subnet.private_a.id]
|
|
6
|
+
boundary_arn = data.aws_iam_policy.boundary.arn
|
|
7
|
+
log_kms_key_arn = data.aws_kms_key.estate.arn
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
module "ledger" {
|
|
11
|
+
source = "../../modules/service"
|
|
12
|
+
|
|
13
|
+
name = "${var.name}-ledger"
|
|
14
|
+
subnet_ids = [aws_subnet.private_a.id]
|
|
15
|
+
boundary_arn = data.aws_iam_policy.boundary.arn
|
|
16
|
+
log_kms_key_arn = data.aws_kms_key.estate.arn
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
data "aws_iam_policy" "boundary" {
|
|
20
|
+
name = "${var.name}-boundary"
|
|
21
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
resource "aws_s3_bucket" "artifacts" {
|
|
2
|
+
bucket = "${var.name}-artifacts"
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
resource "aws_s3_bucket_versioning" "artifacts" {
|
|
6
|
+
bucket = aws_s3_bucket.artifacts.id
|
|
7
|
+
|
|
8
|
+
versioning_configuration {
|
|
9
|
+
status = "Enabled"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
resource "aws_s3_bucket_public_access_block" "artifacts" {
|
|
14
|
+
bucket = aws_s3_bucket.artifacts.id
|
|
15
|
+
|
|
16
|
+
block_public_acls = true
|
|
17
|
+
block_public_policy = true
|
|
18
|
+
ignore_public_acls = true
|
|
19
|
+
restrict_public_buckets = true
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
resource "aws_s3_bucket_server_side_encryption_configuration" "artifacts" {
|
|
23
|
+
bucket = aws_s3_bucket.artifacts.id
|
|
24
|
+
|
|
25
|
+
rule {
|
|
26
|
+
apply_server_side_encryption_by_default {
|
|
27
|
+
sse_algorithm = "aws:kms"
|
|
28
|
+
kms_master_key_id = data.aws_kms_key.estate.arn
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
variable "region" {
|
|
2
|
+
description = "The region every resource in this root is created in."
|
|
3
|
+
type = string
|
|
4
|
+
default = "us-east-1"
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
variable "name" {
|
|
8
|
+
description = "Prefix for every resource this root names."
|
|
9
|
+
type = string
|
|
10
|
+
default = "waterline-prod"
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
variable "vpc_cidr" {
|
|
14
|
+
description = "The network this environment sits in."
|
|
15
|
+
type = string
|
|
16
|
+
default = "10.40.0.0/16"
|
|
17
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
resource "aws_iam_role" "this" {
|
|
2
|
+
name = "${var.name}-role"
|
|
3
|
+
permissions_boundary = var.boundary_arn
|
|
4
|
+
|
|
5
|
+
assume_role_policy = jsonencode({
|
|
6
|
+
Version = "2012-10-17"
|
|
7
|
+
Statement = [{
|
|
8
|
+
Effect = "Allow"
|
|
9
|
+
Principal = { Service = "ecs-tasks.amazonaws.com" }
|
|
10
|
+
Action = "sts:AssumeRole"
|
|
11
|
+
}]
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
resource "aws_iam_role_policy" "this" {
|
|
16
|
+
name = "${var.name}-inline"
|
|
17
|
+
role = aws_iam_role.this.id
|
|
18
|
+
|
|
19
|
+
policy = jsonencode({
|
|
20
|
+
Version = "2012-10-17"
|
|
21
|
+
Statement = [{
|
|
22
|
+
Effect = "Allow"
|
|
23
|
+
Action = ["sqs:ReceiveMessage", "sqs:DeleteMessage", "sqs:GetQueueAttributes"]
|
|
24
|
+
Resource = aws_sqs_queue.work.arn
|
|
25
|
+
}]
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
resource "aws_cloudwatch_log_group" "this" {
|
|
30
|
+
name = "/service/${var.name}"
|
|
31
|
+
retention_in_days = 14
|
|
32
|
+
kms_key_id = var.log_kms_key_arn
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
resource "aws_sqs_queue" "work" {
|
|
36
|
+
name = "${var.name}-work"
|
|
37
|
+
visibility_timeout_seconds = 60
|
|
38
|
+
redrive_policy = jsonencode({
|
|
39
|
+
deadLetterTargetArn = aws_sqs_queue.dead.arn
|
|
40
|
+
maxReceiveCount = 5
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
resource "aws_sqs_queue" "dead" {
|
|
45
|
+
name = "${var.name}-dead"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
resource "aws_security_group" "task" {
|
|
49
|
+
name = "${var.name}-task"
|
|
50
|
+
description = "Egress only; the task reaches the queue and the log group."
|
|
51
|
+
|
|
52
|
+
egress {
|
|
53
|
+
from_port = 0
|
|
54
|
+
to_port = 0
|
|
55
|
+
protocol = "-1"
|
|
56
|
+
cidr_blocks = ["0.0.0.0/0"]
|
|
57
|
+
}
|
|
58
|
+
}
|