Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create Fastly service for static.crates.io #155

Merged
merged 10 commits into from
Dec 21, 2022

Conversation

jdno
Copy link
Member

@jdno jdno commented Nov 21, 2022

This is a first pass at a Fastly service for static.crates.io (tracking issue rust-lang/infra-team#62).

let status_code = response.get_status().as_u16();

if status_code >= 500 {
warn!(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The setup for logging is not complete yet, and we are not streaming logs. So this warning will not show up anywhere.

@jdno
Copy link
Member Author

jdno commented Nov 28, 2022

Just a heads-up that I'll be setting up terragrunt for the crates-io module, and then I will rebase this PR.

@jdno
Copy link
Member Author

jdno commented Dec 13, 2022

I've rebased the pull request on a very recent commit in master and moved the resources into the new terragrunt structure.

@jdno
Copy link
Member Author

jdno commented Dec 13, 2022

The following things still need to be done:

  • Set up weighted routing for DNS records
    • Rename the domain name of the CloudFront distribution (e.g. cloudfront-${var.static_domain_name})
    • Add a variable to configure the weights
    • Create a DNS record for var.static_domain_name for each CDN
  • Set TTL explicitly
  • Set up log streaming
    • Figure out where to send the logs
    • Configure the log destination
  • Add some tests (if it's possible to unit test the function)

My preference would be to merge this pull request and then work through these items in separate branches. This pull request is long enough as it is, and it'll be easier to review the changes one-by-one.

@jdno jdno marked this pull request as ready for review December 14, 2022 12:29
@jdno
Copy link
Member Author

jdno commented Dec 14, 2022

This feels like a good place to stop and merge.

The configuration only sets up Fastly right now, which will make it possible to deploy it to production as well and test there without touching any existing resources.

All changes beyond this point will interact with the CloudFront distribution or with DNS, which we probably want to review and test separately.

@@ -0,0 +1,123 @@
# The Fastly service must be deployed in two steps, since some resources depend
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised by this - isn't terraform supposed to be able to handle dependencies like this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes and no.

The problem here is that the newly created resources are used as keys in a for_each statement:

Error: Invalid for_each argument

  on fastly-index.tf line 74, in resource "aws_route53_record" "index_tls_validation":
  74:   for_each = {
  75:     for challenge in fastly_tls_subscription.index.managed_dns_challenges :
  76:     trimprefix(challenge.record_name, "_acme-challenge.") => challenge
  77:   }
    ├────────────────
    │ fastly_tls_subscription.index.managed_dns_challenges is a set of object, known only after apply

The "for_each" map includes keys derived from resource attributes that
cannot be determined until apply, and so Terraform cannot determine the
full set of keys that will identify the instances of this resource.

When working with unknown values in for_each, it's better to define the map
keys statically in your configuration and place apply-time results only in
the map values.

Alternatively, you could use the -target planning option to first apply
only the resources that the for_each value depends on, and then apply a
second time to fully converge.

The same problem exists when rewriting this block to use count:

Error: Invalid count argument

  on fastly-index.tf line 72, in resource "aws_route53_record" "index_tls_validation":
  72:   count = length(fastly_tls_subscription.index.managed_dns_challenges)

The "count" value depends on resource attributes that cannot be determined
until apply, so Terraform cannot predict how many instances will be
created. To work around this, use the -target argument to first apply only
the resources that the count depends on.

The only potential workaround I've found is splitting the configuration into two modules, but those would still need to be applied after each other. We can do that with dependencies in Terragrunt, but that would mean we need a state for the fastly_tls_subscription and a state for the validation.

crates-io-staging
├── crates-io
├── fastly-tls-subscription
└── fastly-tls-validation

One could then apply all changes by running terragrunt run-all apply from the crates-io-staging directory, which will resolve the dependencies and apply the changes in the right order.

Thinking more about this, I kinda like this approach. It makes the dependency explicit in the tooling, and not in the documentation. What do you think?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've put together a quick proof-of-concept here: jdno@28bb026 Haven't tried actually running it, though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without too much experience here, I can't have a strong opinion, but the suggestion feels better than commenting out code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused by this, how's AWS validation working then? It also generates the DNS records dynamically.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the relevant part of a new aws_acm_certificate in a Terraform plan:

resource "aws_acm_certificate" "cert" {
  domain_validation_options = [
    {
      domain_name           = "test.jdno.dev"
      resource_record_name  = (known after apply)
      resource_record_type  = (known after apply)
      resource_record_value = (known after apply)
    },
  ]

And here is the fastly_tls_subscription:

resource "fastly_tls_subscription" "subscription" {
   managed_dns_challenges  = (known after apply)

The reason why AWS works is that the number of domain_validation_options with their respective domain_name, i.e. the key for the for_each statement, is known when planning.

We can create a feature request to get this added to terraform-provider-fastly, but in the meantime we are stuck with either running the initial apply twice or using dependencies.

Opinions, @pietroalbini and @rylev?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've created a feature request for the Fastly provider: fastly/terraform-provider-fastly#627

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooh ok, your explaination on the issue (unfortunately) makes a lot of sense. Ugh. I'd go with the two applies for now. Also, can you flag the issue with our Fastly contact to see if it can be prioritized on their end?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forwarded both issues to our contact and will post an update in the team meeting when I know more about the priorities.

@@ -0,0 +1,394 @@
# This file is automatically @generated by Cargo.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this lockfile actually used? The dependencies should be included in the one at the root of the repo due to the workspace.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wasn't until I added the external data source. But since Terragrunt copies the Rust crate into a temporary build environment, we cannot provide a stable path to include the crate in the workspace.

Comment on lines 11 to 29
Build the function:

```shell
cd compute-static
fastly compute build
```

Export an API token for Fastly and then run Terraform:

```shell
export FASTLY_API_KEY=""
terraform plan
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm worried that you have to remember to run fastly compute build to get the right configuration, and if you don't changes will be silently not applied. Can we use the external data source to invoke the compilation as part of planning?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I had to add a shell scripts that runs fastly compute build and returns a valid JSON object, though.

@pietroalbini
Copy link
Member

  • Set up log streaming
    • Figure out where to send the logs
    • Configure the log destination

Note that we do not collect logs from CloudFront either as of today.

@jdno
Copy link
Member Author

jdno commented Dec 19, 2022

Log streaming might be interesting for debugging our functions. Not so much for request logs.

The `managed_dns_challenge` attribute has been deprecated by Fastly.
It's replacement is only known after the `fastly_tls_subscription` has
been applied, which requires us to deploy the Fastly service in two
steps.
The CNAME for the Fastly service cannot be accessed through a Terraform
resource. As a hack, we can get it from the ACME HTTP challenge.
The Fastly function is now built whenever `terraform plan` is run. This
This ensures that the function is always up to date, and prevents users
from accidentally uploading a stale version of the WASM module.

This is implemented using an external data source [^1] inside Terraform.
The data source calls a script that compiles the function and returns
the path to the artifact.

[^1]: https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/data_source
@jdno jdno merged commit 5757d44 into rust-lang:master Dec 21, 2022
@jdno jdno deleted the fastly-static-staging branch December 21, 2022 11:33
jdno added a commit to jdno/rust-simpleinfra that referenced this pull request Dec 21, 2022
An outdated Cargo.lock file got pushed to the default branch when
rebasing pull request rust-lang#155.
@jdno jdno mentioned this pull request Dec 21, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants