Skip to content

Commit 1dd3fb5

Browse files
committed
various changes
1 parent c68b363 commit 1dd3fb5

File tree

4 files changed

+157
-71
lines changed

4 files changed

+157
-71
lines changed

.github/workflows/tofu.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
- name: Tofu Validate
3838
run: tofu validate -no-color
3939
- name: Tofu Format Check
40-
run: tofu fmt -check
40+
run: tofu fmt -check -recursive
4141

4242
plan:
4343
name: "Plan"
@@ -53,7 +53,6 @@ jobs:
5353
run: |
5454
{ PLAN=$(tofu plan -var-file="env-staging.tfvars" -no-color | tee /dev/fd/5 || true); } 5>&1
5555
echo "<details><summary>OpenTofu Plan (Staging)</summary><code>$PLAN</code></details>" >> $GITHUB_STEP_SUMMARY
56-
continue-on-error: true
5756
- name: Tofu Init (Prod)
5857
run: tofu init -var-file="env-prod.tfvars" -input=false
5958
- name: Tofu Plan (Prod)

README.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
This repository provides [GitOps](https://www.gitops.tech/) inspired configuration for our DNS records, CDN configuration, and other services used by the Playful Programming site.
2+
3+
Configuration is written with [OpenTofu](https://opentofu.org/). This is meant to run in GitHub Actions and stores its state in S3-compatible storage (in Fastly).
4+
5+
## Getting Started
6+
7+
- Either build the included [devcontainer](https://code.visualstudio.com/docs/devcontainers/containers) or [Install OpenTofu](https://opentofu.org/docs/intro/install/)
8+
- Run `tofu init`
9+
10+
Before pushing any changes, check the configuration with `tofu fmt` and `tofu validate`. You won't be able to run `tofu plan` without using our secrets (which you should not have). When you open a PR, the [tofu.yml](https://github.com/playfulprogramming/playfulprogramming-infra/blob/main/.github/workflows/tofu.yml) workflow will show the planned changes in the job summary.
11+
12+
There are two environments, with configuration in `env-staging.tfvars` and `env-prod.tfvars`. The staging environment deploys on any push to `main`, and waits for testing and approval before deploying to prod.
13+
14+
## Architecture
15+
16+
This is the current state of our overall dependencies:
17+
18+
```mermaid
19+
flowchart TD
20+
WebUser@{ shape: rounded, label: "Website User" }
21+
22+
subgraph "Cloudinary"
23+
ImageOptimization["Image Optimization"]
24+
end
25+
26+
subgraph "GitHub Actions"
27+
WebDeploy["Web Deploy"]
28+
HoofDeploy["Hoof Deploy"]
29+
OpenTofu["Infra Deploy (OpenTofu)"]
30+
end
31+
32+
subgraph "Fastly"
33+
CDN
34+
OpenTofuObjectStorage["OpenTofu Object Storage"]
35+
end
36+
37+
subgraph "Porkbun"
38+
DNS
39+
end
40+
41+
subgraph "Fly.io"
42+
WebMachine["playful-web"]
43+
HoofMachine["hoof"]
44+
Postgres
45+
Redis
46+
Tigris["Tigris Object Storage"]
47+
end
48+
49+
Orama["Orama Search Index"]
50+
51+
WebDeploy -.-> Orama
52+
WebDeploy -.-> WebMachine
53+
WebDeploy --> HoofMachine
54+
HoofDeploy -.-> HoofMachine
55+
56+
WebUser --> Orama
57+
WebUser --> CDN
58+
WebUser --> Tigris
59+
WebUser --> ImageOptimization --> WebMachine
60+
61+
CDN --> WebMachine
62+
63+
HoofMachine --> Tigris
64+
HoofMachine --> Postgres
65+
HoofMachine --> Redis
66+
67+
OpenTofu --> OpenTofuObjectStorage
68+
OpenTofu -.-> CDN
69+
OpenTofu -.-> DNS
70+
```

main.tf

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ provider "porkbun" {
88
}
99

1010
module "playful-web" {
11-
source = "./modules/playful-web"
12-
domain = var.playful_web_domain
13-
host = var.playful_web_host
11+
source = "./modules/playful-web"
12+
domain = var.playful_web_domain
13+
host = var.playful_web_host
14+
noindex = var.env != "prod"
1415
}

modules/playful-web/main.tf

Lines changed: 82 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,40 @@
11
variable "domain" {
2-
type = string
2+
type = string
33
description = "The user-facing root domain (this is 'playfulprogramming.com' in prod)"
44
}
55

66
variable "host" {
7-
type = string
7+
type = string
88
description = "The backend hostname that requests are proxied to"
99
}
1010

11+
variable "noindex" {
12+
type = bool
13+
description = "Whether a noindex header should be appended to every response"
14+
}
15+
1116
resource "fastly_service_vcl" "cdn" {
12-
activate = true
13-
comment = "Managed by Tofu"
14-
http3 = true
15-
name = "Playful Programming's website"
16-
stage = false
17-
18-
domain {
19-
name = var.domain
20-
}
21-
22-
backend {
23-
address = var.host
24-
name = "Host 1"
25-
port = 443
26-
prefer_ipv6 = true
27-
use_ssl = true
28-
}
29-
30-
gzip {
31-
content_types = [
32-
"text/html",
17+
activate = true
18+
comment = "Managed by Tofu"
19+
http3 = true
20+
name = "Playful Programming's website"
21+
stage = false
22+
23+
domain {
24+
name = var.domain
25+
}
26+
27+
backend {
28+
address = var.host
29+
name = "Host 1"
30+
port = 443
31+
prefer_ipv6 = true
32+
use_ssl = true
33+
}
34+
35+
gzip {
36+
content_types = [
37+
"text/html",
3338
"application/x-javascript",
3439
"text/css",
3540
"application/javascript",
@@ -47,8 +52,8 @@ resource "fastly_service_vcl" "cdn" {
4752
"image/vnd.microsoft.icon",
4853
"text/plain",
4954
"text/xml",
50-
]
51-
extensions = [
55+
]
56+
extensions = [
5257
"css",
5358
"js",
5459
"html",
@@ -59,41 +64,54 @@ resource "fastly_service_vcl" "cdn" {
5964
"json",
6065
"svg",
6166
]
62-
name = "Generated by default compression policy"
63-
}
64-
65-
header {
66-
action = "set"
67-
destination = "http.Strict-Transport-Security"
68-
ignore_if_set = false
69-
name = "Generated by force TLS and enable HSTS"
70-
priority = 100
71-
source = "\"max-age=300\""
72-
type = "response"
73-
}
74-
75-
product_enablement {
76-
bot_management = false
77-
brotli_compression = false
78-
domain_inspector = false
79-
image_optimizer = false
80-
log_explorer_insights = false
81-
origin_inspector = true
82-
websockets = false
83-
}
84-
85-
request_setting {
86-
bypass_busy_wait = false
87-
force_miss = false
88-
force_ssl = true
89-
max_stale_age = 0
90-
name = "Generated by force TLS and enable HSTS"
91-
timer_support = false
92-
}
67+
name = "Generated by default compression policy"
68+
}
69+
70+
header {
71+
action = "set"
72+
destination = "http.Strict-Transport-Security"
73+
ignore_if_set = false
74+
name = "Generated by force TLS and enable HSTS"
75+
priority = 100
76+
source = "\"max-age=300\""
77+
type = "response"
78+
}
79+
80+
dynamic "header" {
81+
for_each = var.noindex ? [1] : []
82+
content {
83+
action = "set"
84+
destination = "http.X-Robots-Tag"
85+
ignore_if_set = false
86+
name = "Prevent indexing in staging"
87+
priority = 100
88+
source = "\"noindex\""
89+
type = "response"
90+
}
91+
}
92+
93+
product_enablement {
94+
bot_management = false
95+
brotli_compression = false
96+
domain_inspector = false
97+
image_optimizer = false
98+
log_explorer_insights = false
99+
origin_inspector = true
100+
websockets = false
101+
}
102+
103+
request_setting {
104+
bypass_busy_wait = false
105+
force_miss = false
106+
force_ssl = true
107+
max_stale_age = 0
108+
name = "Generated by force TLS and enable HSTS"
109+
timer_support = false
110+
}
93111
}
94112

95113
resource "fastly_tls_subscription" "main" {
96-
domains = [var.domain, "*.${var.domain}"]
114+
domains = [var.domain, "*.${var.domain}"]
97115
certificate_authority = "certainly"
98116
}
99117

@@ -108,16 +126,16 @@ resource "porkbun_dns_record" "domain_validation" {
108126
], 0)...
109127
}
110128

111-
domain = regex("[^.]*\\.[^.]*$", each.value[0].record_name)
129+
domain = regex("[^.]*\\.[^.]*$", each.value[0].record_name)
112130
subdomain = substr(
113131
each.value[0].record_name,
114132
0,
115133
length(each.value[0].record_name) - length(regex("[^.]*\\.[^.]*$", each.value[0].record_name)) - 1
116134
)
117-
type = each.value[0].record_type
118-
content = each.value[0].record_value
119-
ttl = 600
120-
prio = 10
135+
type = each.value[0].record_type
136+
content = each.value[0].record_value
137+
ttl = 600
138+
prio = 10
121139
}
122140

123141
resource "fastly_tls_subscription_validation" "main" {
@@ -131,14 +149,12 @@ data "fastly_tls_configuration" "default_tls" {
131149
}
132150

133151
resource "porkbun_dns_record" "apex" {
134-
count = 4 # Works around fastly_tls_configuration not resolving until after an apply (assuming fastly will provide exactly 4 records)
152+
for_each = toset([for record in data.fastly_tls_configuration.default_tls.dns_records : record.record_value if record.record_type == "A"])
135153

136154
domain = var.domain
137155
subdomain = ""
138156
type = "A"
139-
content = [
140-
for record in data.fastly_tls_configuration.default_tls.dns_records : record.record_value if record.record_type == "A"
141-
][count.index]
157+
content = each.value
142158
ttl = 600
143159
prio = 10
144160
}

0 commit comments

Comments
 (0)