V Platform Features

Point the Application at Its Database, Broker and Cache

The shop runs against a database on your machine, another in staging and a cluster in production, and the orders are to move to a database of their own. None of that touches a component: where things are is written in the Context, the same way for the database, the broker and the cache, and the user names and passwords are supplied separately as secrets.

§One address for everything

# context.toa.yaml
amqp: amqp://localhost
mongodb: mongodb://localhost
stash: redis://localhost

These are the lines toa create wrote, with the addresses of what npm run dock starts. Every component of the application uses that broker, that database and that cache.

KeyAddress ofRead
amqp (amqp.context)the message brokerBindings
amqp.sources.<source>a broker of another systemBrokers of other systems
mongodbthe databaseStorages
stashRedis for the stashCache Something All Replicas Share
httpcomponents, for streamed callsThe HTTP binding

atomicity.redis and the telemetry endpoints are addresses too, but plain ones: one value for the whole application, without the per-component keys described below.

§Per environment

Suffix the key:

# context.toa.yaml
mongodb@local: mongodb://localhost:27017
mongodb@production: mongodb+srv://db.example.com

stash@local: redis://localhost:6379
stash@production: redis://redis.internal:6379

amqp:
  context:
    .@local: amqp://localhost:5672
    .@production:
      - amqp://rmq-0.internal:5672/store
      - amqp://rmq-1.internal:5672/store

An address is a URL as the technology understands it, so it may carry a path, such as a broker’s virtual host, and options in its query string.

§A different address for one component

Written as a map, a declaration gives an address for everything, for a namespace, or for one component. . is the default:

# context.toa.yaml
mongodb:
  .: mongodb://mongo.example.com # everything else
  store: mongodb://store.mongo.example.com # every component of the namespace `store`
  store.orders: mongodb://orders.mongo.example.com # `store.orders` alone

A component takes the longest key that matches its id:

ComponentTakes
store.ordersorders.mongo.example.com
store.catalog, store.vouchersstore.mongo.example.com
billing.wallets, billing.ledgermongo.example.com
accountsmongo.example.com

mongodb: mongodb://mongo.example.com is the same map with the single key ..

A component that matches no key, where there is no ., is refused, and the message names the component.

A component without a namespace is keyed by its name: accounts for one, default for all such components.

§Several addresses under one key

A key may hold a list, and every component that resolves to it is given all of them:

amqp:
  context:
    .:
      - amqp://rmq-0.example.com
      - amqp://rmq-1.example.com

Numbered hosts can be written as a range in brackets:

amqp:
  context: amqp://rmq-[0..31].example.com

which is rmq-00 to rmq-31: every number is as wide as the end of the range. What several addresses mean depends on who reads them: the broker binding uses them as nodes to connect to, while the stash uses only the first.

§Credentials

An address never contains credentials. One that does is refused:

Pointer URI 'mongodb://user:pass@mongo.example.com' must not contain credentials.

§Locally

toa env writes one pair of variables per secret into .env, and keeps what is already in the file when it runs again:

$ npx toa env --interactive   # asks for each value
$ npx toa env --dev           # fills in development defaults

npm run env runs the second. Its defaults, developer and secret, are the user and the password the generated docker-compose.yaml gives the broker and the database.

§In a deployment

A user name and a password are supplied as a secret in the environment the application is deployed to. There is one secret per key of the declaration, not per component: every component that resolved to a key shares it, and so does every address listed under it.

Ask for the list of what an environment needs:

$ npx toa export secrets production
toa-amqp-context.default:
  username
  password
toa-mongodb.default:
  username
  password

Each holds the keys username and password:

$ npx toa conceal mongodb-store-orders username=orders password=secret
$ npx toa conceal amqp-context.default username=store password=secret

toa conceal deploys the secret with the prefix toa- (toa-mongodb-store-orders). A secret created by other means has to carry that prefix itself.

The name is made of the declaration’s identifier and the key, with dots turned into dashes:

DeclarationKeySecret
mongodb.mongodb.default
mongodbstoremongodb-store
mongodbstore.ordersmongodb-store-orders
amqp.context.amqp-context.default
amqp.contextstore.ordersamqp-context-store-orders
amqp.sources.payments—amqp-sources-payments

A deployment into an environment that lacks any of them is refused before anything is built, naming what is missing:

Secrets are not deployed: toa-mongodb.default/username, toa-mongodb.default/password

Addresses on protocols that carry no credentials have no secret: redis, http and https. TLS client configuration is not available this way.

§Not the same as a secret of configuration

  • The credentials of an address are named after the declaration and supplied as described here.
  • A secret value of a component’s configuration, such as the key of an external API, is declared format: secret in the manifest, referenced as $NAME in the Context, and deployed into the secret configuration. See Keep an API key out of the repository.

External HTTP services a component calls through context.fetch are of the second kind: their address and key are configuration of the calling component (Call an External Service).

Search
Search the documentation