VI Shipping

Give Each Environment Its Values

The shop runs on your machine against localhost, and in production against a managed database and under a real host name. The components are the same in both places. What differs is written in the Context, per environment.

§A value for one environment

Append @<environment> to a key. The first line is what toa create wrote; the second is added:

# context.toa.yaml
mongodb: mongodb://localhost
mongodb@production: mongodb://mongo.internal.example.com

A key without a suffix applies everywhere. A key with a suffix applies in that environment and wins there: in production the database is mongo.internal.example.com, everywhere else it is localhost.

The environment is named on the command line:

$ npx toa env local
$ npx toa deploy production

An environment is not declared anywhere. It exists because a command names it; one that no key mentions takes every unsuffixed value.

§At any depth

Any key of the Context can be suffixed, not only the top-level ones: the host of the gateway, the limit that Add Configuration declares, the replicas of a composition:

# context.toa.yaml
exposition:
  authorities:
    local: localhost:8000
    local@production: api.example.com

configuration:
  store.orders:
    limit: 10000
    limit@staging: 100

compositions:
  - name: core
    components: [store.orders, billing.wallets]
    replicas: 3
    replicas@staging: 1

A suffixed key replaces the whole value of the unsuffixed one. It is not merged into it:

storages:
  pictures:
    provider: tmp
    directory: store-pictures
  pictures@production:
    provider: s3
    bucket: store-pictures
    region: eu-west-1

In production, pictures is the s3 declaration alone.

A key may exist only with suffixes. Environments that are not named then have no such key:

evicted@production:
  components: [pictures]

Because @ marks a suffix, a key of the Context cannot contain @ for any other purpose. A package name used as a key under annotations is the one exception.

§What you typically vary

  • Addresses — amqp, mongodb, stash, storage buckets, the gateway’s authorities, the ingress hosts.
  • Configuration values — a limit that is small in tests, a sender address, a feature that is off locally.
  • Shape — compositions, replicas, resources, mono.
  • What is deployed — evicted.
  • How images are built — registry.base, registry.platforms, registry.services.

Secret values are never written in the Context, in any environment. The Context names them and each environment is given its own values; see Supply Secrets.

§The local environment

toa env without a name selects local and writes .env beside the Context. In the application toa create wrote, the env script runs it, with --dev to fill secrets with local development defaults, and npm run restart runs that script before it restarts the processes; What a restart does shows both:

$ npm run env       # toa types --quiet && toa env --dev && toa map
$ npm run restart   # npm run env, then both processes again

Run npm run restart after changing the Context. The .env file is a snapshot. Secret values already in it are kept, so writing it again does not lose what you typed.

.env* is in the generated .gitignore, and has to stay there: the file holds secrets.

toa env takes more options: --path for a Context elsewhere, --as to write another file, -c and -s to write only what one component or one service needs. See The CLI.

§Several local environments

A common second one runs the application in a container on your machine, where localhost is the container itself:

mongodb@local: mongodb://localhost:27017
mongodb@docker: mongodb://host.docker.internal:27017
$ npx toa env docker --dev --as .env.docker
$ npx toa compose ./components/* --dock --env .env.docker

--as keeps the two environment files side by side.

§Derived environments

Staging is production with a smaller database and a lower limit. Instead of repeating every production key, name the environments as a chain, separated by colons. Each key takes the first name in the chain it has a suffix for:

amqp: amqp://localhost
amqp@production: amqp://rmq.internal.example.com

mongodb@production: mongodb://mongo.internal.example.com
mongodb@staging: mongodb://mongo.staging.example.com

configuration:
  store.orders:
    limit: 10000
    limit@staging: 100
$ npx toa deploy staging:production
Keystaging:production takes
mongodb@staging
amqp@production, as there is no @staging
limit@staging

A key with neither suffix takes its unsuffixed value. A chain may be longer: preview:staging:production.

  • The processes run as the first name. toa env staging:production writes TOA_ENV=staging.
  • The chain is not stored. It is written on the command line each time, never in the Context. Give the same chain to every command for that environment: the one toa deploy read is the one toa export secrets must read.
  • An empty name in a chain is refused: Environment 'staging:' contains an empty name.

§Naming an environment

Every command that reads the Context takes an environment in the same form.

CommandDefault environment
toa env, toa map, toa npm, toa typeslocal
toa deploy, toa pushdefault
toa export secrets, toa export tags, toa export deployment, toa export convergencenone; it must be given

Do not name an environment eight hexadecimal digits, or deps- followed by eight. A deploy tags images with the environment’s name, and such a name cannot be told from the tags the build writes; see Build Images.

§What an environment cannot change

An environment changes values in the Context. It cannot give an operation a different implementation, schema or contract in staging. An operation that has to behave differently reads a configuration value, and the environment sets it; see Add Configuration.

A running operation can read the environment’s name as context.env (The Operation Context). Prefer a configuration value to branching on the name: a value says what differs, a name only says where.

§Keep lower environments capable

Run local and staging infrastructure that can do what production does, at a smaller size: a database with transactions, the same broker. Where the infrastructure is weaker, the runtime says so in the log when it starts, and what you test is no longer what you release. Environments has the reasoning.

Search
Search the documentation