Line data Source code
1 : use anyhow::*;
2 : use clap::{value_parser, Arg, ArgMatches, Command};
3 : use std::{path::PathBuf, str::FromStr};
4 : use wal_craft::*;
5 :
6 10 : fn main() -> Result<()> {
7 10 : env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("wal_craft=info"))
8 10 : .init();
9 10 : let arg_matches = cli().get_matches();
10 10 :
11 10 : let wal_craft = |arg_matches: &ArgMatches, client| {
12 5 : let (intermediate_lsns, end_of_wal_lsn) = match arg_matches
13 5 : .get_one::<String>("type")
14 5 : .map(|s| s.as_str())
15 5 : .context("'type' is required")?
16 : {
17 5 : Simple::NAME => Simple::craft(client)?,
18 4 : LastWalRecordXlogSwitch::NAME => LastWalRecordXlogSwitch::craft(client)?,
19 3 : LastWalRecordXlogSwitchEndsOnPageBoundary::NAME => {
20 1 : LastWalRecordXlogSwitchEndsOnPageBoundary::craft(client)?
21 : }
22 2 : WalRecordCrossingSegmentFollowedBySmallOne::NAME => {
23 1 : WalRecordCrossingSegmentFollowedBySmallOne::craft(client)?
24 : }
25 1 : LastWalRecordCrossingSegment::NAME => LastWalRecordCrossingSegment::craft(client)?,
26 0 : a => panic!("Unknown --type argument: {a}"),
27 : };
28 13 : for lsn in intermediate_lsns {
29 8 : println!("intermediate_lsn = {lsn}");
30 8 : }
31 5 : println!("end_of_wal = {end_of_wal_lsn}");
32 5 : Ok(())
33 5 : };
34 :
35 10 : match arg_matches.subcommand() {
36 0 : None => panic!("No subcommand provided"),
37 10 : Some(("print-postgres-config", _)) => {
38 20 : for cfg in REQUIRED_POSTGRES_CONFIG.iter() {
39 20 : println!("{cfg}");
40 20 : }
41 5 : Ok(())
42 : }
43 :
44 5 : Some(("with-initdb", arg_matches)) => {
45 0 : let cfg = Conf {
46 0 : pg_version: *arg_matches
47 0 : .get_one::<u32>("pg-version")
48 0 : .context("'pg-version' is required")?,
49 0 : pg_distrib_dir: arg_matches
50 0 : .get_one::<PathBuf>("pg-distrib-dir")
51 0 : .context("'pg-distrib-dir' is required")?
52 0 : .to_owned(),
53 0 : datadir: arg_matches
54 0 : .get_one::<PathBuf>("datadir")
55 0 : .context("'datadir' is required")?
56 0 : .to_owned(),
57 0 : };
58 0 : cfg.initdb()?;
59 0 : let srv = cfg.start_server()?;
60 0 : wal_craft(arg_matches, &mut srv.connect_with_timeout()?)?;
61 0 : srv.kill();
62 0 : Ok(())
63 : }
64 5 : Some(("in-existing", arg_matches)) => wal_craft(
65 5 : arg_matches,
66 5 : &mut postgres::Config::from_str(
67 5 : arg_matches
68 5 : .get_one::<String>("connection")
69 5 : .context("'connection' is required")?,
70 : )
71 5 : .context(
72 5 : "'connection' argument value could not be parsed as a postgres connection string",
73 5 : )?
74 5 : .connect(postgres::NoTls)?,
75 : ),
76 0 : Some(_) => panic!("Unknown subcommand"),
77 : }
78 10 : }
79 :
80 12 : fn cli() -> Command {
81 12 : let type_arg = &Arg::new("type")
82 12 : .help("Type of WAL to craft")
83 12 : .value_parser([
84 12 : Simple::NAME,
85 12 : LastWalRecordXlogSwitch::NAME,
86 12 : LastWalRecordXlogSwitchEndsOnPageBoundary::NAME,
87 12 : WalRecordCrossingSegmentFollowedBySmallOne::NAME,
88 12 : LastWalRecordCrossingSegment::NAME,
89 12 : ])
90 12 : .required(true);
91 12 :
92 12 : Command::new("Postgres WAL crafter")
93 12 : .about("Crafts Postgres databases with specific WAL properties")
94 12 : .subcommand(
95 12 : Command::new("print-postgres-config")
96 12 : .about("Print the configuration required for PostgreSQL server before running this script")
97 12 : )
98 12 : .subcommand(
99 12 : Command::new("with-initdb")
100 12 : .about("Craft WAL in a new data directory first initialized with initdb")
101 12 : .arg(type_arg)
102 12 : .arg(
103 12 : Arg::new("datadir")
104 12 : .help("Data directory for the Postgres server")
105 12 : .value_parser(value_parser!(PathBuf))
106 12 : .required(true)
107 12 : )
108 12 : .arg(
109 12 : Arg::new("pg-distrib-dir")
110 12 : .long("pg-distrib-dir")
111 12 : .value_parser(value_parser!(PathBuf))
112 12 : .help("Directory with Postgres distributions (bin and lib directories, e.g. pg_install containing subpath `v14/bin/postgresql`)")
113 12 : .default_value("/usr/local")
114 12 : )
115 12 : .arg(
116 12 : Arg::new("pg-version")
117 12 : .long("pg-version")
118 12 : .help("Postgres version to use for the initial tenant")
119 12 : .value_parser(value_parser!(u32))
120 12 : .required(true)
121 12 :
122 12 : )
123 12 : )
124 12 : .subcommand(
125 12 : Command::new("in-existing")
126 12 : .about("Craft WAL at an existing recently created Postgres database. Note that server may append new WAL entries on shutdown.")
127 12 : .arg(type_arg)
128 12 : .arg(
129 12 : Arg::new("connection")
130 12 : .help("Connection string to the Postgres database to populate")
131 12 : .required(true)
132 12 : )
133 12 : )
134 12 : }
135 :
136 2 : #[test]
137 2 : fn verify_cli() {
138 2 : cli().debug_assert();
139 2 : }
|