Declarative configuration

:wave:t3:

There are a few fly configuration options that one must remember to faithfully “clone” an application (max/min instance count, region, auto scaling, etc.)

Would it be possible to have all of these options part of the fly.toml configuration file?

I guess one could use terraform to achieve this, but that would include writing the terraform provider first. E_TOO_MANY_YAKS_TO_SHAVE

2 Likes

There is currently work underway concerning this. Mostly because I wanted it for myself :slight_smile: It’s actually what our new remote builders use!

I’ve made a GraphQL mutation to launch an app with volumes, VM count, VM size, regions, etc. Like all our GraphQL API, it’s largely undocumented.

However, here’s how you can use it:

mutation($input: LaunchAppInput!) {
  launchApp(input: $input) {
    app { id, name }
    release { id, version, inProgress }
  }
}

where LaunchAppInput looks like this:

{
  organizationId: "your organization graphql node id",
  name: "my-app-name",
  config: {
    mounts: [{
      source: "vol",
      destination: "/voldata",
    }],
    services: [
      {
        ports: [{
          port: 443,
          handlers: ["tls", "http"]
        }]
      }
    ],
    env: {
      FOO: "bar"
    }
  },
  image: "registry/image",
  vmSize: "DEDICATED_CPU_1X",
  regions: ["EWR", "YYZ"],
  secrets: [
    {
      key: "foo",
      value: "bar",
    },
    {
      key: "hello",
      value: "world",
    },
  ],
  volumes: [
    {
      name: "vol",
      sizeGb: 15,
      region: "EWR"
    },
    {
      name: "vol",
      sizeGb: 15,
      region: "YYZ"
    }
  ]
}

You’ll need to have an image built and pushed already, either in a public repository or on us (in the same organization).

Anyway, I don’t recommend using this just yet until we make it better :slight_smile:

It’s also not idempotent. It won’t update an app with new configs, this is just to create an initial app.

@pims what’s your use case for cloning apps?

I’ll hold off a little longer then :slight_smile: idempotency would be really convenient.

@michael my (planned) use case is to create a handful of identical apps that matches the different usage tiers.

Same code, same docker image, same everything except: VM size, regions, instance count. Having those as part of the fly.toml configuration would be helpful.
For now, the plan is using a bash script.

@pims How do you imagine overriding things that vary between apps? A config file per app? Merging several config files at deploy time with overrides? One config file with something like yaml includes? Something else?

We’ve been debating config formats for a while, curious to see how much our opinions match what you all want :slight_smile:

I’m still trying to figure this out myself.
I’m definitely not in the camp of yaml trickery (references and includes).

In the k8s world, kustomize is one solution to this problem, but I’m also not convinced it is the “right” one.

There are two things at play:

  1. Having everything about an app included in its configuration
  2. Elegant way of identifying what needs to change across environments

If there is a solution for 1), finding a solution for 2) is less of an immediate concern.

I’m happy with a bunch of hacks for 2) but 1) is something that would have to be solved by you guys (or terraform)

What’s the recommended way to update an app with new configs?

Context: I’m experimenting with writing a terraform provider for fly based on the GraphQL API and now I’m trying to figure out how “updates” to a fly app should be plumbed through.

Oooh! That’s interesting.

You have three places you can change configs with our API:

  1. Secrets
  2. Environment variables
  3. The image itself

Depending on what you’re trying to update, secrets and env vars can do a lot! Changes to these restart VMs. You could go as far as having settings URL in the env vars that gets pulled down at boot time.

Updating images is also very powerful though. If Terraform is consuming flyctl, you can make in place file modifications and build + deploy the image. Repeated builds should cache layers and be very fast.

If you really want to go bonkers, you can connect to running VMs with SSH and do stuff that way. Maybe fun, probably not super userful.

I want to change everything :slight_smile:

Based on the parameters of launchApp that means (in decreasing order of approximate importance):

  • image
  • config
  • secrets (perhaps via setSecrets?)
  • count (perhaps via setVmCount?)
  • vmSize (perhaps via setVmSize?)
  • scheduling
  • regions
  • volumes

I really, really want avoid scenarios where terraform might try to destroy/recreate one of my fly apps.

1 Like

Oh I see what you’re asking! @jerome or @michael can pop in here with better options than I have, ultimately we’d like you to be able to run all those mutations as one “transaction”.

Right now, you can do image/config/secrets with deployImage and it’s reasonably transactional.

The others are individual, isolated calls.

What you’re doing is important and we might be able to expose one mutation to update all of that in one go. We have a “launch” mutation that’ll take most of that for initial app create, we could apply some of those rules to updates as well.

Yes, I just got the create (and delete) bit working via launchApp (and deleteApp).

I’m over in this issue trying to improve the debugging experience for GraphQL + Terraform.

In the meantime I’ll take a closer look at deployImage. At first glance it looks like I need services and/or definition. Can I just pass in the JSON version of my fly.toml to definition?

1 Like

deployImage is what you want now. The config json goes into definition, services is deprecated in favor of services in the config.

I’ve also wanted a single mutation, like deploy, that takes image, config, secrets, counts, size, etc and applies everything in a single deployment. Volumes should use the existing volume mutations since they are individually addressable objects off the app rather than a point-in-time configuration linked to a deployment. Same for certificates and IP addresses.

I can hack this together for you, but it’ll probably be next week.

2 Likes

Following up on my adventure here, it seems that setSecrets does not like to be invoked if there are no secrets to change. It will return an error. This is perhaps reasonable, but means it is a bad fit for use with this terraform kludge I’m assembling.

We can tweak that mutation. What makes sense to return?

In this case it might make sense to just indicate that:

  • it was/wasn’t a no-op (via some field)
  • created a release (along with some way to see the “current” state the release is trying to converge to)

How’s this looking? Still a “maybe this week” thing?

Yes I’m going to try to get it out tomorrow.

Did this ever get released? Excited to use it!

Not yet, it’s almost done though. I’ll @ you when there’s something to play with.

1 Like

Planning to work more on the terraform provider today. Anything early for me to experiment with?