44{-# LANGUAGE RankNTypes #-}
55{-# LANGUAGE TemplateHaskell #-}
66
7+ import qualified Cardano.Configuration as Cfg
8+ import qualified Cardano.Configuration.CliArgs as CliArgs
9+ import qualified Cardano.Configuration.Commands as Cmds
710import qualified Cardano.Crypto.Init as Crypto
811import Cardano.Git.Rev (gitRev )
9- import Cardano.Node.Configuration.POM (PartialNodeConfiguration (.. ))
12+ import Cardano.Node.Configuration.CardanoConfigAdapter
13+ (cardanoConfigToNodeConfiguration )
14+ import Cardano.Node.Configuration.CardanoConfigCompare
15+ (compareConfigurations )
16+ import Cardano.Node.Configuration.POM (NodeConfiguration (.. ),
17+ PartialNodeConfiguration (.. ), defaultPartialNodeConfiguration ,
18+ makeNodeConfiguration , parseNodeConfigurationFP )
1019import Cardano.Node.Handlers.TopLevel
1120import Cardano.Node.Parsers (nodeCLIParser )
1221import Cardano.Node.Run (runNode )
1322import Cardano.Node.Tracing.Documentation (TraceDocumentationCmd (.. ),
1423 parseTraceDocumentationCmd , runTraceDocumentationCmd )
24+ import Cardano.Node.Types (ConfigYamlFilePath (.. ))
1525
16- import Data.Monoid (Last (getLast ))
26+ import Data.Monoid (Last (.. ))
1727import qualified Data.Text as Text
1828import qualified Data.Text.IO as Text
1929import Data.Version (showVersion )
2030import Options.Applicative
2131import qualified Options.Applicative as Opt
32+ import System.Exit (exitFailure )
2233import System.Info (arch , compilerName , compilerVersion , os )
2334import System.IO (hPutStrLn , stderr )
2435
@@ -37,6 +48,7 @@ main = do
3748 runNode args
3849 TraceDocumentation tdc -> runTraceDocumentationCmd tdc
3950 VersionCmd -> runVersionCommand
51+ ConfigCmd act -> act
4052
4153 where
4254 p = Opt. prefs Opt. showHelpOnEmpty
@@ -56,6 +68,7 @@ main = do
5668 Opt. info (fmap RunCmd nodeCLIParser
5769 <|> fmap TraceDocumentation parseTraceDocumentationCmd
5870 <|> parseVersionCmd
71+ <|> fmap ConfigCmd configSubcommands
5972 <**> helper)
6073
6174 ( Opt. fullDesc <>
@@ -66,6 +79,7 @@ main = do
6679data Command = RunCmd PartialNodeConfiguration
6780 | TraceDocumentation TraceDocumentationCmd
6881 | VersionCmd
82+ | ConfigCmd (IO () )
6983
7084-- Yes! A --version flag or version command. Either guess is right!
7185parseVersionCmd :: Parser Command
@@ -105,3 +119,84 @@ command' c descr p =
105119 [ command c (info (p <**> helper) $ mconcat [ progDesc descr ])
106120 , metavar c
107121 ]
122+
123+ -- cardano-config subcommands --------------------------------------------------
124+
125+ -- | The @migrate@, @schema@ and @resolve@ subcommands, spliced from the shared
126+ -- @cardano-config:commands@ sublibrary. @migrate@ and @schema@ are
127+ -- cardano-config's own commands, unchanged; @resolve@ is a node-specific variant
128+ -- (see 'resolveDualCommand') that additionally cross-checks the node's own parser
129+ -- against cardano-config's.
130+ configSubcommands :: Parser (IO () )
131+ configSubcommands =
132+ Opt. hsubparser
133+ ( Opt. commandGroup " Configuration commands:"
134+ <> Cmds. migrateCommand
135+ <> Cmds. schemaCommand
136+ <> resolveDualCommand
137+ )
138+
139+ -- | A node-specific @resolve@: resolve the configuration with cardano-config
140+ -- (printing the result as YAML, exactly like cardano-config's own @resolve@),
141+ -- then re-resolve the same configuration with the node's own POM parser and
142+ -- report any discrepancies between the two. Exits non-zero when they disagree,
143+ -- so it doubles as a CI parity check while the node still has two parsers.
144+ resolveDualCommand :: Mod CommandFields (IO () )
145+ resolveDualCommand =
146+ command " resolve"
147+ ( info
148+ (runDualResolve <$> Cmds. resolveOptionsParser)
149+ ( progDesc
150+ ( " Resolve a cardano-node configuration (defaults + file + CLI) with both the "
151+ <> " node and cardano-config parsers, print the result as YAML, and report any "
152+ <> " discrepancies between the two parsers (exit non-zero if they disagree)."
153+ )
154+ )
155+ )
156+
157+ runDualResolve :: Cmds. ResolveOptions -> IO ()
158+ runDualResolve resolveOpts@ (Cmds. ResolveOptions cli _geneses) = do
159+ -- Print the resolved configuration using cardano-config's own renderer (which
160+ -- honours --with-geneses); this also terminates via 'die' if resolution fails.
161+ Cmds. runResolveCommand resolveOpts
162+ -- Cross-check: resolve the same inputs with the node's POM parser and diff.
163+ discrepancies <- resolveDiscrepancies cli
164+ case discrepancies of
165+ [] ->
166+ putStrLn " resolve: the node and cardano-config parsers agree on the resolved configuration."
167+ ds -> do
168+ hPutStrLn stderr $
169+ " resolve: " <> show (length ds)
170+ <> " discrepancy(ies) between the node and cardano-config parsers:"
171+ mapM_ (hPutStrLn stderr . (" - " <> )) ds
172+ exitFailure
173+
174+ -- | Resolve the configuration file (+ CLI) both ways and return the divergences.
175+ -- The node (POM) side takes its CLI-supplied, file-absent fields (topology /
176+ -- database / protocol files / socket) from the shared cardano-config resolution,
177+ -- so the diff reflects how the two parsers read the configuration FILE (plus the
178+ -- documented adapter gaps) rather than an independent — and necessarily
179+ -- asymmetric — CLI reverse-mapping.
180+ resolveDiscrepancies :: Cfg. CliArgs -> IO [String ]
181+ resolveDiscrepancies cli = do
182+ (fileCfg, _warns) <- Cfg. parseConfigurationFiles configFp
183+ case Cfg. resolveConfiguration cli fileCfg of
184+ Left err -> pure [" cardano-config failed to resolve the configuration: " <> show err]
185+ Right (cfgNc, _) ->
186+ case cardanoConfigToNodeConfiguration cfgNc of
187+ Left adaptErr -> pure [" cardano-config configuration could not be adapted: " <> adaptErr]
188+ Right adaptedNc -> do
189+ filePartial <- parseNodeConfigurationFP (Just (ConfigYamlFilePath configFp))
190+ let withCli =
191+ (defaultPartialNodeConfiguration <> filePartial)
192+ { pncConfigFile = Last (Just (ConfigYamlFilePath configFp))
193+ , pncTopologyFile = Last (Just (ncTopologyFile adaptedNc))
194+ , pncDatabaseFile = Last (Just (ncDatabaseFile adaptedNc))
195+ , pncProtocolFiles = Last (Just (ncProtocolFiles adaptedNc))
196+ , pncSocketConfig = Last (Just (ncSocketConfig adaptedNc))
197+ }
198+ case makeNodeConfiguration withCli of
199+ Left err -> pure [" node parser (makeNodeConfiguration) failed: " <> err]
200+ Right pomNc -> pure (compareConfigurations pomNc adaptedNc)
201+ where
202+ configFp = CliArgs. configFilePath cli
0 commit comments