@intentius/behold 0.7.0 → 0.9.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 +94 -2
- package/README.md +133 -2
- package/demos.json +25 -1
- package/dist/cli.js +3176 -330
- package/example-argo-estate/README.md +71 -0
- package/example-argo-estate/app-a/chant.config.ts +18 -0
- package/example-argo-estate/app-a/manifests/app.yaml +54 -0
- package/example-argo-estate/app-a/package.json +13 -0
- package/example-argo-estate/app-a/src/app.ts +35 -0
- package/example-argo-estate/app-a/tsconfig.json +1 -0
- package/example-argo-estate/app-b/chant.config.ts +18 -0
- package/example-argo-estate/app-b/manifests/app.yaml +54 -0
- package/example-argo-estate/app-b/package.json +13 -0
- package/example-argo-estate/app-b/src/app.ts +34 -0
- package/example-argo-estate/app-b/tsconfig.json +1 -0
- package/example-argo-estate/control-plane/chant.config.ts +29 -0
- package/example-argo-estate/control-plane/package.json +13 -0
- package/example-argo-estate/control-plane/src/argo.ts +69 -0
- package/example-argo-estate/control-plane/tsconfig.json +1 -0
- package/example-argo-estate/package-lock.json +1949 -0
- package/example-argo-estate/package.json +10 -0
- package/example-carve/README.md +194 -0
- package/example-carve/app/chant.config.ts +6 -0
- package/example-carve/app/package-lock.json +1075 -0
- package/example-carve/app/package.json +13 -0
- package/example-carve/app/src/carved.ts +30 -0
- package/example-carve/app/tsconfig.json +1 -0
- package/example-carve/carve-report.json +872 -0
- package/example-carve/legacy-tf/cdn.tf +23 -0
- package/example-carve/legacy-tf/compute.tf +63 -0
- package/example-carve/legacy-tf/floci-override.tf.disabled +61 -0
- package/example-carve/legacy-tf/modules/cdn/main.tf +72 -0
- package/example-carve/legacy-tf/naming.tf +10 -0
- package/example-carve/legacy-tf/network.tf +119 -0
- package/example-carve/legacy-tf/observability.tf +18 -0
- package/example-carve/legacy-tf/outputs.tf +16 -0
- package/example-carve/legacy-tf/storage.tf +33 -0
- package/example-carve/legacy-tf/terraform.tfstate +602 -0
- package/example-carve/legacy-tf/versions.tf +40 -0
- package/example-flux-estate/README.md +22 -12
- package/example-flux-estate/app-a/package.json +2 -2
- package/example-flux-estate/app-a/src/app.ts +2 -1
- package/example-flux-estate/app-b/chant.config.ts +4 -3
- package/example-flux-estate/app-b/package.json +2 -2
- package/example-flux-estate/app-b/src/app.ts +5 -3
- package/example-flux-estate/control-plane/package.json +2 -2
- package/example-flux-estate/control-plane/src/flux.ts +10 -2
- package/example-flux-estate/package-lock.json +17 -17
- package/example-k8s/README.md +16 -2
- package/example-k8s/base/deployment.yaml +21 -0
- package/example-k8s/base/kustomization.yaml +3 -0
- package/example-k8s/base/service.yaml +12 -0
- package/example-k8s/chant.config.ts +10 -0
- package/example-k8s/overlays/dev/kustomization.yaml +6 -0
- package/example-k8s/package-lock.json +18 -18
- package/example-k8s/package.json +3 -3
- package/example-writes/package-lock.json +14 -14
- package/example-writes/package.json +3 -3
- package/package.json +9 -6
- package/web/app.js +1027 -42
- package/web/carve-steps.js +610 -0
- package/web/carve-steps.test.js +233 -0
- package/web/demos.js +71 -0
- package/web/demos.test.js +83 -0
- package/web/icons/cncf/argo.svg +1 -0
- package/web/icons/cncf/flux.svg +1 -0
- package/web/icons/cncf/helm.svg +1 -0
- package/web/icons/k8s/c-role.svg +1 -0
- package/web/icons/k8s/cm.svg +1 -0
- package/web/icons/k8s/crb.svg +1 -0
- package/web/icons/k8s/crd.svg +1 -0
- package/web/icons/k8s/cronjob.svg +1 -0
- package/web/icons/k8s/deploy.svg +1 -0
- package/web/icons/k8s/ds.svg +1 -0
- package/web/icons/k8s/ep.svg +1 -0
- package/web/icons/k8s/group.svg +1 -0
- package/web/icons/k8s/hpa.svg +1 -0
- package/web/icons/k8s/ing.svg +1 -0
- package/web/icons/k8s/job.svg +1 -0
- package/web/icons/k8s/limits.svg +1 -0
- package/web/icons/k8s/netpol.svg +1 -0
- package/web/icons/k8s/ns.svg +1 -0
- package/web/icons/k8s/pod.svg +1 -0
- package/web/icons/k8s/psp.svg +1 -0
- package/web/icons/k8s/pv.svg +1 -0
- package/web/icons/k8s/pvc.svg +1 -0
- package/web/icons/k8s/quota.svg +1 -0
- package/web/icons/k8s/rb.svg +1 -0
- package/web/icons/k8s/role.svg +1 -0
- package/web/icons/k8s/rs.svg +1 -0
- package/web/icons/k8s/sa.svg +1 -0
- package/web/icons/k8s/sc.svg +1 -0
- package/web/icons/k8s/secret.svg +1 -0
- package/web/icons/k8s/sts.svg +1 -0
- package/web/icons/k8s/svc.svg +1 -0
- package/web/icons/k8s/user.svg +1 -0
- package/web/icons/k8s/vol.svg +1 -0
- package/web/index.html +285 -85
- package/web/json-view.js +334 -0
- package/web/json-view.test.js +218 -0
- package/web/layout-store.js +278 -0
- package/web/layout-store.test.js +388 -0
- package/web/panel.js +28 -0
- package/web/theme.js +114 -6
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# The one module in the estate, so module scoring shows up on camera. A module
|
|
2
|
+
# is opaque to the advisor: it is one node, ranked as a tier-2 composite, and
|
|
3
|
+
# its internals (modules/cdn/) are never descended into.
|
|
4
|
+
#
|
|
5
|
+
# The `aliases` line is the bridge patch left over from last month's carve. That
|
|
6
|
+
# hostname used to come from `aws_ssm_parameter.assets_cdn_domain` in this
|
|
7
|
+
# directory; chant owns the parameter now (../app/src/carved.ts), and the
|
|
8
|
+
# surviving Terraform reads it back through a data source. This is exactly the
|
|
9
|
+
# rewrite `carve bridge` generates for every inbound edge a carve cuts, sitting
|
|
10
|
+
# in the estate a month later and working.
|
|
11
|
+
|
|
12
|
+
data "aws_ssm_parameter" "assets_cdn_domain" {
|
|
13
|
+
name = "/acme/platform/prod/assets-cdn-domain"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module "cdn" {
|
|
17
|
+
source = "./modules/cdn"
|
|
18
|
+
|
|
19
|
+
name_suffix = random_pet.suffix.id
|
|
20
|
+
origin_domain_name = "assets.acme-platform.example.com"
|
|
21
|
+
aliases = [data.aws_ssm_parameter.assets_cdn_domain.value]
|
|
22
|
+
price_class = "PriceClass_100"
|
|
23
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# The "next month" candidates.
|
|
2
|
+
#
|
|
3
|
+
# The lambda is a tier-2 map with five outbound references — the role, the
|
|
4
|
+
# bucket, both subnets and the security group. None of them block a carve
|
|
5
|
+
# (outbound edges become deferred deploy-time inputs, not immediate patches),
|
|
6
|
+
# but five of them is real work, so it lands in the middle band honestly.
|
|
7
|
+
#
|
|
8
|
+
# The role is the mirror image: one inbound edge, from the lambda. That is what
|
|
9
|
+
# an inbound edge costs, and it is why the role scores lower than the bucket
|
|
10
|
+
# despite being just as small.
|
|
11
|
+
|
|
12
|
+
resource "aws_iam_role" "api" {
|
|
13
|
+
name = "acme-platform-api-${random_pet.suffix.id}"
|
|
14
|
+
|
|
15
|
+
assume_role_policy = jsonencode({
|
|
16
|
+
Version = "2012-10-17"
|
|
17
|
+
Statement = [
|
|
18
|
+
{
|
|
19
|
+
Effect = "Allow"
|
|
20
|
+
Action = "sts:AssumeRole"
|
|
21
|
+
Principal = {
|
|
22
|
+
Service = "lambda.amazonaws.com"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
managed_policy_arns = [
|
|
29
|
+
"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
|
|
30
|
+
"arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
tags = {
|
|
34
|
+
Component = "api"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
resource "aws_lambda_function" "api" {
|
|
39
|
+
function_name = "acme-platform-api"
|
|
40
|
+
role = aws_iam_role.api.arn
|
|
41
|
+
handler = "index.handler"
|
|
42
|
+
runtime = "nodejs22.x"
|
|
43
|
+
filename = "build/api.zip"
|
|
44
|
+
memory_size = 512
|
|
45
|
+
timeout = 15
|
|
46
|
+
|
|
47
|
+
environment {
|
|
48
|
+
variables = {
|
|
49
|
+
# The one edge into the bucket, and the only patch the carve needs.
|
|
50
|
+
ASSETS_BUCKET = aws_s3_bucket.assets.bucket
|
|
51
|
+
LOG_LEVEL = "info"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
vpc_config {
|
|
56
|
+
subnet_ids = [aws_subnet.private_a.id, aws_subnet.private_b.id]
|
|
57
|
+
security_group_ids = [aws_security_group.lambda.id]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
tags = {
|
|
61
|
+
Component = "api"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Floci endpoints for the `--live` tier of the carve walkthrough (#254).
|
|
2
|
+
#
|
|
3
|
+
# INERT BY DEFAULT, and deliberately so. Terraform only loads files ending in
|
|
4
|
+
# `.tf`, and `chant carve advise` only parses `.tf` files directly under the
|
|
5
|
+
# estate directory — so as long as this file keeps its `.disabled` suffix it is
|
|
6
|
+
# a text file and nothing more. The offline tier (synthetic terraform.tfstate,
|
|
7
|
+
# zero dependencies beyond this directory) is unaffected by its presence.
|
|
8
|
+
#
|
|
9
|
+
# To arm it for the live tier, copy it into place next to the other .tf files:
|
|
10
|
+
#
|
|
11
|
+
# cp floci-override.tf.disabled floci_override.tf
|
|
12
|
+
#
|
|
13
|
+
# Terraform merges any `*_override.tf` file over the base configuration, so the
|
|
14
|
+
# `provider "aws"` block below replaces the one in versions.tf without editing
|
|
15
|
+
# versions.tf. Remove `floci_override.tf` and the estate is offline again.
|
|
16
|
+
#
|
|
17
|
+
# WARNING: with this armed, `terraform apply` writes a real state file over
|
|
18
|
+
# terraform.tfstate. Work on a copy of this directory, not the committed one —
|
|
19
|
+
# the synthetic state is a fixture the offline tier depends on.
|
|
20
|
+
#
|
|
21
|
+
# The `--live` tier is bounded by what Floci emulates. S3 and CloudWatch Logs —
|
|
22
|
+
# the two types the walkthrough actually carves — are covered (chant's
|
|
23
|
+
# `just carve-emit-e2e` is the prior art). The VPC, lambda and CloudFront half
|
|
24
|
+
# of this estate is scenery for the advisor's grey band; expect to apply the
|
|
25
|
+
# carve targets with `-target` rather than the whole estate in one go, and see
|
|
26
|
+
# ../README.md for which beats the live tier turns into footage.
|
|
27
|
+
|
|
28
|
+
provider "aws" {
|
|
29
|
+
region = "us-east-2"
|
|
30
|
+
|
|
31
|
+
access_key = "test"
|
|
32
|
+
secret_key = "test"
|
|
33
|
+
|
|
34
|
+
skip_credentials_validation = true
|
|
35
|
+
skip_metadata_api_check = true
|
|
36
|
+
skip_requesting_account_id = true
|
|
37
|
+
skip_region_validation = true
|
|
38
|
+
s3_use_path_style = true
|
|
39
|
+
|
|
40
|
+
default_tags {
|
|
41
|
+
tags = {
|
|
42
|
+
Environment = "prod"
|
|
43
|
+
ManagedBy = "terraform"
|
|
44
|
+
Team = "platform"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# Scratch Floci, booted by the demo and deleted after — never an existing
|
|
49
|
+
# `floci*` container. Port 4566 is the same endpoint behold's writes demo
|
|
50
|
+
# and chant's carve-emit e2e use.
|
|
51
|
+
endpoints {
|
|
52
|
+
cloudfront = "http://localhost:4566"
|
|
53
|
+
ec2 = "http://localhost:4566"
|
|
54
|
+
iam = "http://localhost:4566"
|
|
55
|
+
lambda = "http://localhost:4566"
|
|
56
|
+
logs = "http://localhost:4566"
|
|
57
|
+
s3 = "http://localhost:4566"
|
|
58
|
+
ssm = "http://localhost:4566"
|
|
59
|
+
sts = "http://localhost:4566"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# A deliberately small local module, so `module.cdn` resolves without a registry
|
|
2
|
+
# download and `terraform init` works offline. The advisor never reads this file:
|
|
3
|
+
# `carve advise --from ../..` lists only the `.tf` files directly under the
|
|
4
|
+
# estate directory, which is the same scoping Terraform itself uses for a module.
|
|
5
|
+
|
|
6
|
+
variable "name_suffix" {
|
|
7
|
+
type = string
|
|
8
|
+
description = "Shared random suffix, so the distribution comment matches the rest of the estate."
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
variable "origin_domain_name" {
|
|
12
|
+
type = string
|
|
13
|
+
description = "Hostname the distribution pulls assets from."
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
variable "aliases" {
|
|
17
|
+
type = list(string)
|
|
18
|
+
description = "Public hostnames the distribution answers on."
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
variable "price_class" {
|
|
22
|
+
type = string
|
|
23
|
+
default = "PriceClass_100"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
resource "aws_cloudfront_distribution" "this" {
|
|
27
|
+
enabled = true
|
|
28
|
+
comment = "acme-platform-assets-${var.name_suffix}"
|
|
29
|
+
aliases = var.aliases
|
|
30
|
+
price_class = var.price_class
|
|
31
|
+
|
|
32
|
+
origin {
|
|
33
|
+
origin_id = "assets"
|
|
34
|
+
domain_name = var.origin_domain_name
|
|
35
|
+
|
|
36
|
+
custom_origin_config {
|
|
37
|
+
http_port = 80
|
|
38
|
+
https_port = 443
|
|
39
|
+
origin_protocol_policy = "https-only"
|
|
40
|
+
origin_ssl_protocols = ["TLSv1.2"]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
default_cache_behavior {
|
|
45
|
+
target_origin_id = "assets"
|
|
46
|
+
viewer_protocol_policy = "redirect-to-https"
|
|
47
|
+
allowed_methods = ["GET", "HEAD"]
|
|
48
|
+
cached_methods = ["GET", "HEAD"]
|
|
49
|
+
|
|
50
|
+
forwarded_values {
|
|
51
|
+
query_string = false
|
|
52
|
+
|
|
53
|
+
cookies {
|
|
54
|
+
forward = "none"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
restrictions {
|
|
60
|
+
geo_restriction {
|
|
61
|
+
restriction_type = "none"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
viewer_certificate {
|
|
66
|
+
cloudfront_default_certificate = true
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
output "domain_name" {
|
|
71
|
+
value = aws_cloudfront_distribution.this.domain_name
|
|
72
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# The long tail. `random_pet` is not an AWS resource at all, so there is nothing
|
|
2
|
+
# for it to map to and the advisor scores it 0 without inventing a story. Half
|
|
3
|
+
# the estate's names hang off it, which is the honest punchline of the demo:
|
|
4
|
+
# some Terraform stays Terraform indefinitely, and a migration that cannot say
|
|
5
|
+
# that out loud is selling something.
|
|
6
|
+
|
|
7
|
+
resource "random_pet" "suffix" {
|
|
8
|
+
length = 2
|
|
9
|
+
separator = "-"
|
|
10
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# The grey anchor.
|
|
2
|
+
#
|
|
3
|
+
# Five surviving resources point at the VPC, so carving it would mean patching
|
|
4
|
+
# five references in one go — the advisor bands it "leave in Terraform" and it
|
|
5
|
+
# is right. The subnets and the security group sit just under the line for the
|
|
6
|
+
# same reason, plus a data-source lookup each. This block stays in Terraform,
|
|
7
|
+
# and that is the correct answer, not a failure of the tool.
|
|
8
|
+
|
|
9
|
+
resource "aws_vpc" "main" {
|
|
10
|
+
cidr_block = "10.42.0.0/16"
|
|
11
|
+
enable_dns_support = true
|
|
12
|
+
enable_dns_hostnames = true
|
|
13
|
+
|
|
14
|
+
tags = {
|
|
15
|
+
Name = "acme-platform-${random_pet.suffix.id}"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
data "aws_availability_zones" "available" {
|
|
20
|
+
state = "available"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
resource "aws_subnet" "private_a" {
|
|
24
|
+
vpc_id = aws_vpc.main.id
|
|
25
|
+
cidr_block = "10.42.1.0/24"
|
|
26
|
+
availability_zone = data.aws_availability_zones.available.names[0]
|
|
27
|
+
|
|
28
|
+
tags = {
|
|
29
|
+
Name = "acme-platform-private-a-${random_pet.suffix.id}"
|
|
30
|
+
Tier = "private"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
resource "aws_subnet" "private_b" {
|
|
35
|
+
vpc_id = aws_vpc.main.id
|
|
36
|
+
cidr_block = "10.42.2.0/24"
|
|
37
|
+
availability_zone = data.aws_availability_zones.available.names[1]
|
|
38
|
+
|
|
39
|
+
tags = {
|
|
40
|
+
Name = "acme-platform-private-b-${random_pet.suffix.id}"
|
|
41
|
+
Tier = "private"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# The managed prefix list for S3 in this region — how the lambda reaches the
|
|
46
|
+
# assets bucket without leaving the VPC.
|
|
47
|
+
data "aws_prefix_list" "s3" {
|
|
48
|
+
name = "com.amazonaws.us-east-2.s3"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
resource "aws_security_group" "lambda" {
|
|
52
|
+
name = "acme-platform-lambda-${random_pet.suffix.id}"
|
|
53
|
+
description = "Egress-only group for the acme-platform API lambda"
|
|
54
|
+
vpc_id = aws_vpc.main.id
|
|
55
|
+
|
|
56
|
+
egress {
|
|
57
|
+
description = "S3 via the regional gateway prefix list"
|
|
58
|
+
from_port = 443
|
|
59
|
+
to_port = 443
|
|
60
|
+
protocol = "tcp"
|
|
61
|
+
prefix_list_ids = [data.aws_prefix_list.s3.id]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
egress {
|
|
65
|
+
description = "SSM interface endpoint, inside the VPC"
|
|
66
|
+
from_port = 443
|
|
67
|
+
to_port = 443
|
|
68
|
+
protocol = "tcp"
|
|
69
|
+
cidr_blocks = ["10.42.0.0/16"]
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
tags = {
|
|
73
|
+
Name = "acme-platform-lambda"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# The API lambda reads /acme/platform/prod/* out of Parameter Store over this
|
|
78
|
+
# endpoint rather than the public SSM API.
|
|
79
|
+
resource "aws_vpc_endpoint" "ssm" {
|
|
80
|
+
vpc_id = aws_vpc.main.id
|
|
81
|
+
service_name = "com.amazonaws.us-east-2.ssm"
|
|
82
|
+
vpc_endpoint_type = "Interface"
|
|
83
|
+
subnet_ids = [aws_subnet.private_a.id, aws_subnet.private_b.id]
|
|
84
|
+
security_group_ids = [aws_security_group.lambda.id]
|
|
85
|
+
private_dns_enabled = true
|
|
86
|
+
|
|
87
|
+
tags = {
|
|
88
|
+
Name = "acme-platform-ssm"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# No native mapping at all — the advisor scores it 0 and says so rather than
|
|
93
|
+
# guessing. The second half of the long tail, next to random_pet below.
|
|
94
|
+
resource "aws_network_acl" "private" {
|
|
95
|
+
vpc_id = aws_vpc.main.id
|
|
96
|
+
subnet_ids = [aws_subnet.private_a.id, aws_subnet.private_b.id]
|
|
97
|
+
|
|
98
|
+
ingress {
|
|
99
|
+
protocol = "tcp"
|
|
100
|
+
rule_no = 100
|
|
101
|
+
action = "allow"
|
|
102
|
+
cidr_block = "10.42.0.0/16"
|
|
103
|
+
from_port = 0
|
|
104
|
+
to_port = 65535
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
egress {
|
|
108
|
+
protocol = "-1"
|
|
109
|
+
rule_no = 100
|
|
110
|
+
action = "allow"
|
|
111
|
+
cidr_block = "0.0.0.0/0"
|
|
112
|
+
from_port = 0
|
|
113
|
+
to_port = 0
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
tags = {
|
|
117
|
+
Name = "acme-platform-private"
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# The free first move. Nothing references it and it references nothing, so the
|
|
2
|
+
# advisor scores it 100 — a clean tier-1 map with an empty boundary. The
|
|
3
|
+
# walkthrough names it and then carves the bucket instead: there is always a
|
|
4
|
+
# zero-cost resource to start with, and starting there proves nothing more than
|
|
5
|
+
# that the machinery runs.
|
|
6
|
+
#
|
|
7
|
+
# (The API lambda's own log group is not here any more — it was carved into
|
|
8
|
+
# ../app/src/carved.ts last month.)
|
|
9
|
+
|
|
10
|
+
resource "aws_cloudwatch_log_group" "worker" {
|
|
11
|
+
name = "/acme/platform/worker"
|
|
12
|
+
retention_in_days = 30
|
|
13
|
+
|
|
14
|
+
tags = {
|
|
15
|
+
Name = "acme-platform-worker"
|
|
16
|
+
Component = "worker"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Outputs are invisible to `chant carve advise` — it builds its graph from
|
|
2
|
+
# `resource` and `module` blocks only. Nothing here references the bucket, so
|
|
3
|
+
# the walkthrough's one-patch story stays true; if it did, the patch would be
|
|
4
|
+
# yours to write, not the bridge's.
|
|
5
|
+
|
|
6
|
+
output "api_function_name" {
|
|
7
|
+
value = aws_lambda_function.api.function_name
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
output "vpc_id" {
|
|
11
|
+
value = aws_vpc.main.id
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
output "private_subnet_ids" {
|
|
15
|
+
value = [aws_subnet.private_a.id, aws_subnet.private_b.id]
|
|
16
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# The star of the walkthrough.
|
|
2
|
+
#
|
|
3
|
+
# One inbound edge: the API lambda reads the bucket name out of its environment.
|
|
4
|
+
# That single reference is the whole cut — one `data "aws_s3_bucket"` block in
|
|
5
|
+
# the surviving Terraform and the carve is bridged. The two sub-resources below
|
|
6
|
+
# share the bucket's name, so the advisor folds them into its carve set and
|
|
7
|
+
# inlines them rather than counting them as boundary work.
|
|
8
|
+
|
|
9
|
+
resource "aws_s3_bucket" "assets" {
|
|
10
|
+
bucket = "acme-platform-assets-prod"
|
|
11
|
+
|
|
12
|
+
tags = {
|
|
13
|
+
Name = "acme-platform-assets"
|
|
14
|
+
Purpose = "static-assets"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
resource "aws_s3_bucket_versioning" "assets" {
|
|
19
|
+
bucket = aws_s3_bucket.assets.id
|
|
20
|
+
|
|
21
|
+
versioning_configuration {
|
|
22
|
+
status = "Enabled"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
resource "aws_s3_bucket_public_access_block" "assets" {
|
|
27
|
+
bucket = aws_s3_bucket.assets.id
|
|
28
|
+
|
|
29
|
+
block_public_acls = true
|
|
30
|
+
block_public_policy = true
|
|
31
|
+
ignore_public_acls = true
|
|
32
|
+
restrict_public_buckets = true
|
|
33
|
+
}
|