Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
334 changes: 318 additions & 16 deletions Cargo.lock

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,11 @@ resolver = "2"
members = [
"setup-deploy-keys",
"ansible/roles/dev-desktop/files/team_login",
"terraform/docs-rs/fastly-compute-docs-rs",
]

[profile.fastly-release]
inherits = "release"
debug = 1
codegen-units = 1
lto = "fat"
41 changes: 41 additions & 0 deletions terraform/docs-rs/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions terraform/docs-rs/_terraform.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ terraform {
source = "hashicorp/random"
version = "~> 3.6.2"
}
fastly = {
source = "fastly/fastly"
version = "~> 8.4"
}
}

backend "s3" {
Expand Down
5 changes: 5 additions & 0 deletions terraform/docs-rs/fastly-compute-docs-rs/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[build]
target = "wasm32-wasip1"

[term]
color = "always"
4 changes: 4 additions & 0 deletions terraform/docs-rs/fastly-compute-docs-rs/.fastlyignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/target
**/*.rs.bk
/bin
/pkg
4 changes: 4 additions & 0 deletions terraform/docs-rs/fastly-compute-docs-rs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/target
**/*.rs.bk
/bin/*.wasm
/pkg
8 changes: 8 additions & 0 deletions terraform/docs-rs/fastly-compute-docs-rs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "fastly-compute-project"
version = "0.1.0"
edition = "2024"
publish = false

[dependencies]
fastly = "0.11.0"
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env bash

# Build script used by Terraform to build the function when planning.
#
# This script is called by Terraform to build the function when a user runs
# `terraform plan`. This ensures that the function is always up-to-date, and
# prevents users from accidentally uploading a stale version of the WASM module.
#
# Terraform expects the script to output a valid JSON object.
#
# https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/data_source

# Enable strict mode for Bash
# http://redsymbol.net/articles/unofficial-bash-strict-mode/
set -euo pipefail
IFS=$'\n\t'

script_path=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
project_path=$(cd "${script_path}" && cd ".." && pwd)
project_name="${project_path##*/}"

cd "${project_path}" && fastly compute build &>/dev/null

# Return a valid JSON object that Terraform can consume
echo "{\"path\": \"./${project_name}/pkg/fastly-compute-docs-rs.tar.gz\"}"
21 changes: 21 additions & 0 deletions terraform/docs-rs/fastly-compute-docs-rs/fastly.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# This file describes a Fastly Compute package. To learn more visit:
# https://www.fastly.com/documentation/reference/compute/fastly-toml

authors = [""]
cloned_from = "https://github.com/fastly/compute-starter-kit-rust-default"
description = "Compute@Edge function for docs.rs"
language = "rust"
manifest_version = 3
name = "fastly-compute-docs-rs"
service_id = ""

[local_server]

[scripts]
# workaround to build with custom profile and copy wasm to expected location
build = """
cargo build --profile fastly-release &&
TARGET_DIR=$(cargo metadata --format-version=1 | jq -r .target_directory) &&
mkdir -p ${TARGET_DIR}/wasm32-wasip1/release &&
command cp -f ${TARGET_DIR}/wasm32-wasip1/fastly-release/fastly-compute-project.wasm ${TARGET_DIR}/wasm32-wasip1/release/fastly-compute-project.wasm
"""
4 changes: 4 additions & 0 deletions terraform/docs-rs/fastly-compute-docs-rs/rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[toolchain]
channel = "1.90"
targets = [ "wasm32-wasip1" ]
profile = "default"
34 changes: 34 additions & 0 deletions terraform/docs-rs/fastly-compute-docs-rs/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use fastly::{
Error, Request, Response,
http::{Method, StatusCode},
};

#[fastly::main]
fn main(mut req: Request) -> Result<Response, Error> {
match req.get_method() {
&Method::GET | &Method::HEAD | &Method::OPTIONS => {
// Set default TTL to 1 year.
// Note: This might override origin Cache-Control if not careful,
// but CloudFront's default_ttl implies a default when missing.
// Fastly's set_ttl overrides the TTL for the cache object.
req.set_ttl(31536000);
}
&Method::PUT | &Method::POST | &Method::PATCH | &Method::DELETE => {
// Do not cache other methods
req.set_pass(true);
}
_ => {
return Ok(Response::from_status(StatusCode::METHOD_NOT_ALLOWED));
}
}

// Prevent indexing by search engines
// Send request to backend
let mut resp = req.send("docs_rs_origin")?;

// Prevent indexing by search engines
// TODO: remove this when we are ready to go live with fastly
resp.set_header("X-Robots-Tag", "noindex, nofollow");

Ok(resp)
}
65 changes: 65 additions & 0 deletions terraform/docs-rs/fastly.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
locals {
fastly_domain_name = "fastly-${local.domain_name}"
static_fastly_weight = 0
}

data "external" "package" {
program = ["bash", "terraform-external-build.sh"]
working_dir = "./fastly-compute-docs-rs/bin"
}

data "fastly_package_hash" "package" {
filename = data.external.package.result.path
}

resource "fastly_service_compute" "docs_rs" {
name = local.domain_name

domain {
name = local.fastly_domain_name
}

# commenting this to avoid conflicts
# TODO: uncomment this
# domain {
# name = local.domain_name
# }

backend {
name = "docs_rs_origin"

address = local.origin
override_host = local.origin

use_ssl = true
port = 443
ssl_cert_hostname = local.origin
}

package {
filename = data.external.package.result.path
source_code_hash = data.fastly_package_hash.package.hash
}
}

module "fastly_tls_subscription_globalsign" {
source = "../fastly-tls-subscription"

certificate_authority = "globalsign"
aws_route53_zone_id = data.aws_route53_zone.webapp.id

domains = [
local.fastly_domain_name,
# TODO: uncomment this
# local.domain_name
]
}

resource "aws_route53_record" "fastly_domain" {
name = local.fastly_domain_name
type = "CNAME"
zone_id = data.aws_route53_zone.webapp.id
allow_overwrite = true
records = module.fastly_tls_subscription_globalsign.destinations
ttl = 60
}
14 changes: 14 additions & 0 deletions terraform/fastly-tls-subscription/_terraform.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
terraform {
required_version = "~> 1.0"

required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.64"
}
fastly = {
source = "fastly/fastly"
version = "~> 8.4"
}
}
}
34 changes: 34 additions & 0 deletions terraform/fastly-tls-subscription/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
resource "fastly_tls_subscription" "subscription" {
certificate_authority = var.certificate_authority
domains = var.domains
}

resource "aws_route53_record" "tls_validation" {
depends_on = [fastly_tls_subscription.subscription]

for_each = {
# The following `for` expression (due to the outer {}) will produce an object with key/value pairs:
# - The 'key' is the domain name we've configured (e.g. fastly-static.crates.io)
# - The 'value' is a specific 'challenge' object whose record_name matches the domain
# (e.g. record_name is _acme-challenge.fastly-static.crates.io).
for domain in fastly_tls_subscription.subscription.domains :
# `element()` returns the first object in the list which should be the relevant 'challenge' object we need
domain => element([
for obj in fastly_tls_subscription.subscription.managed_dns_challenges :
# We use an `if` conditional to filter the list to a single element
obj if obj.record_name == "_acme-challenge.${domain}"
], 0)
}

name = each.value.record_name
type = each.value.record_type
zone_id = var.aws_route53_zone_id
allow_overwrite = true
records = [each.value.record_value]
ttl = 60
}

resource "fastly_tls_subscription_validation" "subscription" {
depends_on = [aws_route53_record.tls_validation]
subscription_id = fastly_tls_subscription.subscription.id
}
17 changes: 17 additions & 0 deletions terraform/fastly-tls-subscription/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
locals {
# It is currently not possible to get the CNAME for TLS-enabled hostnames as a
# Terraform resource. But the ACME HTTP challenge redirects production traffic
# to Fastly, for which it uses the CNAME that we are looking for.
#
# The below snippet is a hack to get the CNAME for the static domain from the
# HTTP challenge, until Fastly exposes it in the Terraform provider.
address_pools = flatten([
for record in fastly_tls_subscription.subscription.managed_http_challenges :
record.record_values if record.record_type == "CNAME"
])
}

output "destinations" {
# Prefix address pools for Fastly to enable IPv6 support
value = [for pool in local.address_pools : "dualstack.${pool}"]
}
20 changes: 20 additions & 0 deletions terraform/fastly-tls-subscription/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
variable "certificate_authority" {
type = string
description = "The certificate authority to use"
validation {
condition = contains(["lets-encrypt", "globalsign"], var.certificate_authority)
error_message = "The certificate authority must be either 'lets-encrypt' or 'globalsign'."
}
}

variable "domains" {
type = list(string)
default = []
description = "The list of domains to add to the certificate"
}

variable "aws_route53_zone_id" {
type = string
description = "The AWS Route53 zone in which to create the DNS records"
}