Part I · Getting started · Chapter 2
Your first spell
Writing a manifest that discovers something real about your account, and understanding what each part of it does.
A spell is a project SpellCraft renders. Its entry point is manifest.jsonnet, and the whole model fits in one sentence: top-level keys are filenames, and their values are the file contents.
The smallest useful manifest
local spellcraft = import "spellcraft";
{
"config.json": {
renderedFrom: spellcraft.path(),
},
}
Render it:
npx spellcraft generate manifest.jsonnet
SpellCraft evaluates the file and writes render/config.json. The key became the filename; the value became the contents, serialised as JSON because the filename ends in .json.
Making it discover something
The point of SpellCraft is that evaluation can reach out. Install a provider plugin:
npm install --save @c6fc/spellcraft-plugins
local plugins = import "@c6fc/spellcraft-plugins/module.libsonnet";
local identity = plugins.aws.auth.getCallerIdentity();
{
"account.json": {
account: identity.Account,
principal: identity.Arn,
},
}
Render that and render/account.json contains your real account number, fetched from STS while the Jsonnet was being evaluated. Nobody typed it, and nobody has to update it when you switch accounts.
The account number above is not a variable you set, a tfvars file you maintain, or a value your CI injects. It is a question the configuration asks at the moment it renders. Everything else in SpellCraft exists to make that pattern practical: plugins to supply the questions, events to order them, and memoisation to keep them cheap.
Three kinds of import
local spellcraft = import "spellcraft"; // built-ins
local modules = import "modules"; // spellcraft_modules/
local plugins = import "@c6fc/spellcraft-plugins/module.libsonnet"; // installed plugins
{
"context.json": {
renderedFrom: spellcraft.path(),
name: modules.util.slug("My First Spell"),
account: plugins.aws.auth.getCallerIdentity().Account,
},
}
"spellcraft"is the built-in library. It is small on purpose:envvar(name)andpath()."modules"is generated from your project’sspellcraft_modules/directory, and only exists when that directory does. See Local modules.- A package path imports an installed plugin’s Jsonnet facade.
Producing more than one file
Because the keys are filenames, a manifest can emit an entire directory:
{
"main.tf.json": { /* ... */ },
"variables.tf.json": { /* ... */ },
"README.md": "# Generated. Do not edit.\n",
}
Each file is serialised according to its extension. Rendering output covers how that dispatch works and how to add your own formats.