Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,40 @@ jobs:
- name: Check cabal file formatting
run: ./scripts/cabal-format.sh

cue:
name: CUE front-end (schema drift, vet, ajv)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: stable

# Pin the CUE version: `cue import jsonschema` output (e.g. how defaults
# are translated) varies across releases, so the committed cue/schema/gen_*
# must be regenerated with this same version (see cue/README.md).
- name: Install CUE 0.17.0
run: |
go install cuelang.org/go/cmd/cue@v0.17.0
echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
"$(go env GOPATH)/bin/cue" version

- name: Install just
run: |
sudo apt-get update
sudo apt-get install -y just
just --version

- name: Install ajv-cli
run: npm install -g ajv-cli

# Generated-CUE drift gate, then vet + schema-validate + lint every config
# and example (the per-file `just` recipes, looped over everything).
- name: Check CUE front-end
run: ./scripts/cue-ci.sh

test:
name: Build & test (GHC ${{ matrix.ghc }})
runs-on: ubuntu-latest
Expand Down
83 changes: 66 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ The goal is one shared parser for applications that need the node's configuratio
such as [`cardano-cli`](https://github.com/IntersectMBO/cardano-cli),
[`dmq-node`](https://github.com/IntersectMBO/dmq-node/) and
[the `ouroboros-consensus` tools](https://github.com/IntersectMBO/ouroboros-consensus/tree/main/ouroboros-consensus-cardano#consensus-db-tools).
The bundled `cardano-config` executable exposes the same via its `resolve` and
`schema` subcommands.
The bundled `cardano-config` executable exposes the same via its `resolve`,
`schema` and `migrate` subcommands.

## Recommended format

Expand Down Expand Up @@ -42,21 +42,57 @@ pointing to that component's schema (e.g. a `StorageConfig` sub-file uses `schem
so editors and validators pick up the right schema for the sub-file. The key is
an annotation: the parser accepts and ignores it.

To port an old config to the new format, group the component keys under their
sections inside `Configuration` and add the `Version` / `MinNodeVersion`
envelope. `cardano-config schema` documents the recommended form;
`--legacy-one-file` documents the flat form.
To port an old config to the new format, run `cardano-config migrate` (it reads
`-` as stdin, so you can fetch and convert in one step):

```console
$ cardano-config migrate old-config.json > config.json
$ curl -sL <url-of-old-config> | cardano-config migrate - > config.json
```

It reshapes the document into the envelope as JSON: it adds `$schema` and
`Version`, carries `MinNodeVersion` through, and groups each component's keys
under its section inside `Configuration`. It also brings field names up to date:
the parser rejects the old names, so `migrate` rewrites the ones that were
renamed (`hardLimit`/`softLimit`/`delay` → `HardLimit`/`SoftLimit`/`Delay`,
`EnableRpc`/`RpcSocketPath` → `EnableGrpc`/`GrpcSocketPath`, `TargetNumberOf*` →
`DeadlineTargetNumberOf*`) and drops the ones that were removed
(`PBftSignatureThreshold`, `LastKnownBlockVersion-Major`/`-Minor`/`-Alt`, now
supplied by consensus defaults; the vestigial `Protocol`; and
`MaxKnownMajorProtocolVersion`, a dead key the node never read). Apart from that
it preserves the values as written and does not fill in defaults, inline
referenced sub-files, or read genesis files; follow it with `resolve` to check
the result.

A genuinely unrecognised key (a typo, say) is **kept** rather than silently
dropped, so nothing is lost - but it remains unrecognised and so still surfaces
as an `UnrecognisedKeys` warning on the next parse. Remove it by hand if you want
a warning-free config.

(To port by hand instead: group the component keys under their sections inside
`Configuration` and add the `Version` / `MinNodeVersion` envelope. `cardano-config
schema` documents the recommended form; `--legacy-one-file` documents the flat
form.)

## Authoring with CUE

For typed authoring use the CUE front-end in [`cue/`](cue/): you write the
configuration as a CUE value, `just vet` / `just lint` catch structural and
cross-field mistakes early, and `cue export` emits the envelope JSON this
library ingests. The library stays the final authority (it re-parses, fills
defaults, and verifies genesis hashes). See [`cue/README.md`](cue/README.md).

## Defaults and layering

Every component ships a **default file** under [`defaults/`](defaults/), with the
network/role overlays under [`variants/`](variants/). For each component the
layering, from lowest to highest precedence, is:
per-network overlays under [`variants/`](variants/) and the `NetworkConfig` role
overlays under [`defaults/NetworkConfig/`](defaults/NetworkConfig/). For each
component the layering, from lowest to highest precedence, is:

1. the package's base default (`defaults/<Component>.json`), always applied;
2. for the `Network` component only, a **role layer** chosen automatically from
credential presence: the block-producer or relay variant
(`variants/NetworkConfig/{blockproducer,relay}.json`)
(`defaults/NetworkConfig/{blockproducer,relay}.json`)
fills the deadline peer targets and `PeerSharing` when the configuration leaves
them unset (so it sits *below* the file value);
3. the component's value in the configuration file (an inline object or a sub-file
Expand Down Expand Up @@ -150,14 +186,27 @@ The flag names, metavars and help text match those historically accepted by
`cardano-node`, so existing operator scripts keep working. You can inspect the
parsed options with `cabal run cardano-config -- resolve --help`.

## Tracing options are **not** parsed by this library

Tracing is **not** parsed: it is owned by the node's tracing system (hermod /
`trace-dispatcher`) and given under a single top-level `HermodTracing` key whose
value is a path to a separate file. The key is recognised and captured opaquely
(it appears in the schema), but its contents are not interpreted here. The
authoritative schema lives in
[`hermod-tracing`](https://github.com/IntersectMBO/hermod-tracing).
## Tracing options are owned by `trace-dispatcher`

Tracing is owned by the node's tracing system (hermod / `trace-dispatcher`),
given under a single top-level `HermodTracing` key whose value is **either** a
path to a separate file holding the tracing configuration **or** that
configuration object inline. This library does not define or validate the shape
of that object — the authoritative schema lives in
[`hermod-tracing`](https://github.com/IntersectMBO/hermod-tracing), so the
configuration schema describes `HermodTracing` only as "a path or a JSON
object".

Instead, the parser hands the `HermodTracing` value to `trace-dispatcher`'s own
parser (`readConfiguration`), which resolves it into a `TraceConfig`: a file
reference is read via `FromFile` (after resolving the path to its canonical
location), an inline object via `FromJSONObject`.

The resolved `TraceConfig` is carried through to the final `NodeConfiguration`
(as `tracingConfiguration :: Maybe TraceConfig`), so a consumer of the library
gets the tracing configuration already parsed, and `cardano-config resolve`
emits it back under the `HermodTracing` key (as an inline object). It is
`Nothing`/absent when the configuration has no `HermodTracing` key.

## Mandatory keys

Expand Down
36 changes: 35 additions & 1 deletion app/Main.hs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
-- | The @cardano-config@ command-line tool. It exposes two subcommands:
-- | The @cardano-config@ command-line tool. It exposes three subcommands:
--
-- * @cardano-config resolve@ resolves a @cardano-node@ configuration
-- (per-component defaults, the configuration file and the CLI flags),
Expand All @@ -7,11 +7,17 @@
--
-- * @cardano-config schema@ dumps the configuration JSON Schema (for the
-- whole configuration or a single component), derived from the same codecs.
--
-- * @cardano-config migrate@ reshapes a configuration into the recommended
-- @{ $schema, Version, MinNodeVersion, Configuration }@ envelope and prints
-- it as JSON (a purely structural migration, preserving the values).
module Main (main) where

import Cardano.Configuration (parseConfigurationFiles, renderConfigWarning, resolveConfiguration)
import Cardano.Configuration.CliArgs (CliArgs, configFilePath, parseCliArgs)
import Cardano.Configuration.File (componentDefaults)
import Cardano.Configuration.File.Merge (decodeValueFile)
import Cardano.Configuration.File.Migrate (migrate)
import Cardano.Configuration.Render (GenesisRendering (..), nodeConfigurationToJSON)
import Cardano.Configuration.Schema
( configurationSchemas
Expand All @@ -28,6 +34,7 @@ import qualified Data.ByteString.Lazy.Char8 as L
import Data.Foldable (for_)
import Data.List (intercalate)
import qualified Data.Text as T
import Data.Yaml (decodeThrow)
import Data.Yaml.Pretty (encodePretty, setConfCompare, setConfDropNull)
import qualified Data.Yaml.Pretty as Yaml
import Options.Applicative
Expand All @@ -42,6 +49,8 @@ data Command
Resolve CliArgs GenesisRendering
| -- | @schema@: dump a JSON Schema.
Schema SchemaCmd
| -- | @migrate@: reshape a configuration file into the Version1 envelope.
Migrate FilePath

-- | What the @schema@ subcommand should print.
data SchemaCmd
Expand Down Expand Up @@ -91,6 +100,20 @@ commandParser =
<> footerDoc (Just schemaValidationHelp)
)
)
<> command
"migrate"
( info
( Migrate
<$> strArgument
(metavar "CONFIG" <> help "Configuration file to migrate (JSON or YAML), or - for stdin.")
)
( progDesc
( "Reshape a configuration into the recommended "
<> "{ $schema, Version, MinNodeVersion, Configuration } envelope and print it as JSON. "
<> "Preserves the values as written (no defaults are filled, no sub-files inlined)."
)
)
)
)

-- | How to validate a configuration against the schema, shown under
Expand Down Expand Up @@ -148,6 +171,17 @@ withGenesesFlag =
run :: Command -> IO ()
run (Resolve cli geneses) = runResolve cli geneses
run (Schema cmd) = runSchema cmd
run (Migrate path) = runMigrate path

-- | Read a configuration and print it, reshaped into the Version1 envelope, as
-- JSON. A purely structural migration: it does not resolve, default or validate.
-- A path of @-@ reads the configuration from stdin (so it composes with @curl@).
runMigrate :: FilePath -> IO ()
runMigrate path = handleAny (die . displayException) $ do
raw <- case path of
"-" -> BS.getContents >>= decodeThrow
_ -> decodeValueFile Nothing path
dump (migrate raw)

-- | Resolve a configuration and print it as YAML.
runResolve :: CliArgs -> GenesisRendering -> IO ()
Expand Down
66 changes: 39 additions & 27 deletions cabal.project
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,6 @@ index-state:
packages:
.

-- cardano-ledger at current master (provides the genesis types we decode here).
-- Kept to the minimum set of subdirs we actually depend on; add more only if the
-- solver requires them.
source-repository-package
type: git
location: https://github.com/IntersectMBO/cardano-ledger
tag: 29c7e3040621f323f3af27fd4e89a223ba429021
--sha256: sha256-XPXfHvYjuzN0ASL/z4HwWJwDPQn7vJbTPlEa5zV3w9k=
subdir:
eras/byron/ledger/impl
eras/byron/crypto
eras/byron/ledger/executable-spec
eras/byron/chain/executable-spec
eras/shelley/impl
eras/allegra/impl
eras/mary/impl
eras/alonzo/impl
eras/babbage/impl
eras/conway/impl
eras/dijkstra/impl
libs/cardano-ledger-core
libs/cardano-ledger-binary
libs/cardano-data
libs/vector-map
libs/non-integral
libs/small-steps

-- Ensures colourized output from test runners
test-show-details: direct
tests: true
Expand Down Expand Up @@ -73,6 +46,7 @@ if impl(ghc >=9.14)
, canonical-json:containers
, cborg:base
, cborg:containers
, cborg-json:base
, constrained-generators:QuickCheck
, dependent-map:containers
, dictionary-sharing:containers
Expand All @@ -91,3 +65,41 @@ if impl(ghc >=9.14)
constraints:
-- Happy version 2.2.1 fails to compile haskell-src-exts
, happy < 2.2.1

-- cardano-ledger at current master (provides the genesis types we decode here).
-- Kept to the minimum set of subdirs we actually depend on; add more only if the
-- solver requires them.
source-repository-package
type: git
location: https://github.com/IntersectMBO/cardano-ledger
tag: 29c7e3040621f323f3af27fd4e89a223ba429021
--sha256: sha256-XPXfHvYjuzN0ASL/z4HwWJwDPQn7vJbTPlEa5zV3w9k=
subdir:
eras/byron/ledger/impl
eras/byron/crypto
eras/byron/ledger/executable-spec
eras/byron/chain/executable-spec
eras/shelley/impl
eras/allegra/impl
eras/mary/impl
eras/alonzo/impl
eras/babbage/impl
eras/conway/impl
eras/dijkstra/impl
libs/cardano-ledger-core
libs/cardano-ledger-binary
libs/cardano-data
libs/vector-map
libs/non-integral
libs/small-steps

-- trace-dispatcher (hermod tracing) — provides the tracing configuration parser
-- (ConfigSource/readConfiguration) used to resolve the HermodTracing key.
-- Pinned to the merge commit after hermod-tracing#18 (mkarg/config-source).
source-repository-package
type: git
location: https://github.com/IntersectMBO/hermod-tracing
tag: 3d7aeb85d9ef01df48a44c751e45609b4c6dea00
--sha256: sha256-r4bVm4x2akrCLcg5f0TG6aovCGqNnZkiiBckA3YCz7Q=
subdir:
trace-dispatcher
2 changes: 2 additions & 0 deletions cardano-config.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ library internal
Cardano.Configuration.File.Lint
Cardano.Configuration.File.Mempool
Cardano.Configuration.File.Merge
Cardano.Configuration.File.Migrate
Cardano.Configuration.File.Network
Cardano.Configuration.File.Protocol
Cardano.Configuration.File.Storage
Expand Down Expand Up @@ -97,6 +98,7 @@ library internal
scientific,
text,
time,
trace-dispatcher,
transformers,
yaml,

Expand Down
Loading
Loading