@@ -1583,6 +1583,19 @@ cli_common_ensure_formation(KeeperConfig *options)
15831583 return true;
15841584 }
15851585
1586+ /*
1587+ * When --monitor has been used rather than --pgdata, we are operating at a
1588+ * distance and we don't expect a configuration file to exist.
1589+ */
1590+ if (IS_EMPTY_STRING_BUFFER (options -> pgSetup .pgdata ))
1591+ {
1592+ strlcpy (options -> formation ,
1593+ FORMATION_DEFAULT ,
1594+ sizeof (options -> formation ));
1595+
1596+ return true;
1597+ }
1598+
15861599 switch (ProbeConfigurationFileRole (options -> pathnames .config ))
15871600 {
15881601 case PG_AUTOCTL_ROLE_MONITOR :
@@ -1834,6 +1847,7 @@ cli_get_name_getopts(int argc, char **argv)
18341847
18351848 static struct option long_options [] = {
18361849 { "pgdata" , required_argument , NULL , 'D' },
1850+ { "monitor" , required_argument , NULL , 'm' },
18371851 { "formation" , required_argument , NULL , 'f' },
18381852 { "name" , required_argument , NULL , 'a' },
18391853 { "json" , no_argument , NULL , 'J' },
@@ -1876,6 +1890,19 @@ cli_get_name_getopts(int argc, char **argv)
18761890 break ;
18771891 }
18781892
1893+ case 'm' :
1894+ {
1895+ if (!validate_connection_string (optarg ))
1896+ {
1897+ log_fatal ("Failed to parse --monitor connection string, "
1898+ "see above for details." );
1899+ exit (EXIT_CODE_BAD_ARGS );
1900+ }
1901+ strlcpy (options .monitor_pguri , optarg , MAXCONNINFO );
1902+ log_trace ("--monitor %s" , options .monitor_pguri );
1903+ break ;
1904+ }
1905+
18791906 case 'f' :
18801907 {
18811908 strlcpy (options .formation , optarg , NAMEDATALEN );
@@ -1959,7 +1986,23 @@ cli_get_name_getopts(int argc, char **argv)
19591986 }
19601987
19611988 /* now that we have the command line parameters, prepare the options */
1962- (void ) prepare_keeper_options (& options );
1989+ /* when we have a monitor URI we don't need PGDATA */
1990+ if (cli_use_monitor_option (& options ))
1991+ {
1992+ if (!IS_EMPTY_STRING_BUFFER (options .pgSetup .pgdata ))
1993+ {
1994+ log_warn ("Given --monitor URI, the --pgdata option is ignored" );
1995+ log_info ("Connecting to monitor at \"%s\"" , options .monitor_pguri );
1996+
1997+ /* the rest of the program needs pgdata actually empty */
1998+ bzero ((void * ) options .pgSetup .pgdata ,
1999+ sizeof (options .pgSetup .pgdata ));
2000+ }
2001+ }
2002+ else
2003+ {
2004+ (void ) prepare_keeper_options (& options );
2005+ }
19632006
19642007 /* ensure --formation, or get it from the configuration file */
19652008 if (!cli_common_ensure_formation (& options ))
@@ -1975,6 +2018,79 @@ cli_get_name_getopts(int argc, char **argv)
19752018}
19762019
19772020
2021+ /*
2022+ * cli_use_monitor_option returns true when the --monitor option should be
2023+ * used, or when PG_AUTOCTL_MONITOR has been set in the environment. In that
2024+ * case the options->monitor_pguri is also set to the value found in the
2025+ * environment.
2026+ */
2027+ bool
2028+ cli_use_monitor_option (KeeperConfig * options )
2029+ {
2030+ /* if --monitor is used, then use it */
2031+ if (!IS_EMPTY_STRING_BUFFER (options -> monitor_pguri ))
2032+ {
2033+ return true;
2034+ }
2035+
2036+ /* otherwise, have a look at the PG_AUTOCTL_MONITOR environment variable */
2037+ if (env_exists (PG_AUTOCTL_MONITOR ) &&
2038+ get_env_copy (PG_AUTOCTL_MONITOR ,
2039+ options -> monitor_pguri ,
2040+ sizeof (options -> monitor_pguri )))
2041+ {
2042+ log_debug ("Using environment PG_AUTOCTL_MONITOR \"%s\"" ,
2043+ options -> monitor_pguri );
2044+ return true;
2045+ }
2046+
2047+ /*
2048+ * Still nothing? well don't use --monitor then.
2049+ *
2050+ * Now, on commands that are compatible with using just a monitor and no
2051+ * local pg_autoctl node, we want to include an error message about the
2052+ * lack of a --monitor when we also lack --pgdata.
2053+ */
2054+ if (IS_EMPTY_STRING_BUFFER (options -> pgSetup .pgdata ) &&
2055+ !env_exists ("PGDATA" ))
2056+ {
2057+ log_error ("Failed to get value for environment variable '%s', "
2058+ "which is unset" , PG_AUTOCTL_MONITOR );
2059+ log_warn ("This command also supports the --monitor option, which "
2060+ "is not used here" );
2061+ }
2062+
2063+ return false;
2064+ }
2065+
2066+
2067+ /*
2068+ * cli_monitor_init_from_option_or_config initialises a monitor connection
2069+ * either from the --monitor Postgres URI given on the command line, or from
2070+ * the configuration file of the local node (monitor or keeper).
2071+ */
2072+ void
2073+ cli_monitor_init_from_option_or_config (Monitor * monitor , KeeperConfig * kconfig )
2074+ {
2075+ if (IS_EMPTY_STRING_BUFFER (kconfig -> monitor_pguri ))
2076+ {
2077+ if (!monitor_init_from_pgsetup (monitor , & (kconfig -> pgSetup )))
2078+ {
2079+ /* errors have already been logged */
2080+ exit (EXIT_CODE_BAD_CONFIG );
2081+ }
2082+ }
2083+ else
2084+ {
2085+ if (!monitor_init (monitor , kconfig -> monitor_pguri ))
2086+ {
2087+ /* errors have already been logged */
2088+ exit (EXIT_CODE_BAD_ARGS );
2089+ }
2090+ }
2091+ }
2092+
2093+
19782094/*
19792095 * cli_ensure_node_name ensures that we have a node name to continue with,
19802096 * either from the command line itself, or from the configuration file when
@@ -1983,40 +2099,141 @@ cli_get_name_getopts(int argc, char **argv)
19832099void
19842100cli_ensure_node_name (Keeper * keeper )
19852101{
2102+ /* if we have a --name option, we're done already */
2103+ if (!IS_EMPTY_STRING_BUFFER (keeper -> config .name ))
2104+ {
2105+ return ;
2106+ }
2107+
2108+ /* we might have --monitor instead of --pgdata */
2109+ if (IS_EMPTY_STRING_BUFFER (keeper -> config .pgSetup .pgdata ))
2110+ {
2111+ log_fatal ("Please use either --name or --pgdata "
2112+ "to target a specific node" );
2113+ exit (EXIT_CODE_BAD_ARGS );
2114+ }
2115+
19862116 switch (ProbeConfigurationFileRole (keeper -> config .pathnames .config ))
19872117 {
19882118 case PG_AUTOCTL_ROLE_MONITOR :
19892119 {
1990- if (IS_EMPTY_STRING_BUFFER (keeper -> config .name ))
2120+ log_fatal ("Please use --name to target a specific node" );
2121+ exit (EXIT_CODE_BAD_ARGS );
2122+ break ;
2123+ }
2124+
2125+ case PG_AUTOCTL_ROLE_KEEPER :
2126+ {
2127+ bool monitorDisabledIsOk = false;
2128+
2129+ if (!keeper_config_read_file_skip_pgsetup (& (keeper -> config ),
2130+ monitorDisabledIsOk ))
19912131 {
1992- log_fatal ( "Please use --name to target a specific node" );
1993- exit (EXIT_CODE_BAD_ARGS );
2132+ /* errors have already been logged */
2133+ exit (EXIT_CODE_BAD_CONFIG );
19942134 }
19952135 break ;
19962136 }
19972137
1998- case PG_AUTOCTL_ROLE_KEEPER :
2138+ default :
2139+ {
2140+ log_fatal ("Unrecognized configuration file \"%s\"" ,
2141+ keeper -> config .pathnames .config );
2142+ exit (EXIT_CODE_INTERNAL_ERROR );
2143+ }
2144+ }
2145+ }
2146+
2147+
2148+ /*
2149+ * cli_set_groupId sets the kconfig.groupId depending on the --group argument
2150+ * given on the command line, and if that was not given then figures it out:
2151+ *
2152+ * - it could be that we have a single group in the formation, in that case
2153+ * --group must be zero, so we set it that way,
2154+ *
2155+ * - we may have a local keeper node setup thanks to --pgdata, in that case
2156+ * read the configuration file and grab the groupId from there.
2157+ */
2158+ void
2159+ cli_set_groupId (Monitor * monitor , KeeperConfig * kconfig )
2160+ {
2161+ int groupsCount = 0 ;
2162+
2163+ if (!monitor_count_groups (monitor , kconfig -> formation , & groupsCount ))
2164+ {
2165+ /* errors have already been logged */
2166+ exit (EXIT_CODE_MONITOR );
2167+ }
2168+
2169+ if (groupsCount == 0 )
2170+ {
2171+ /* nothing to be done here */
2172+ log_fatal ("The monitor currently has no Postgres nodes "
2173+ "registered in formation \"%s\"" ,
2174+ kconfig -> formation );
2175+ exit (EXIT_CODE_BAD_STATE );
2176+ }
2177+
2178+ /*
2179+ * When --group was not given, we may proceed when there is only one
2180+ * possible target group in the formation, which is the case with Postgres
2181+ * standalone setups.
2182+ */
2183+ if (kconfig -> groupId == -1 )
2184+ {
2185+ /*
2186+ * When --group is not given and we have a keeper node, we can grab a
2187+ * default from the configuration file. We have to support the usage
2188+ * either --monitor or --pgdata. We have a local keeper node/role only
2189+ * when we have been given --pgdata.
2190+ */
2191+ if (!IS_EMPTY_STRING_BUFFER (kconfig -> pgSetup .pgdata ))
19992192 {
2000- /* when --name has not been used, fetch it from the config */
2001- if (IS_EMPTY_STRING_BUFFER (keeper -> config .name ))
2193+ pgAutoCtlNodeRole role =
2194+ ProbeConfigurationFileRole (kconfig -> pathnames .config );
2195+
2196+ if (role == PG_AUTOCTL_ROLE_KEEPER )
20022197 {
2003- bool monitorDisabledIsOk = false;
2198+ const bool missingPgdataIsOk = true;
2199+ const bool pgIsNotRunningIsOk = true;
2200+ const bool monitorDisabledIsOk = false;
20042201
2005- if (!keeper_config_read_file_skip_pgsetup (& (keeper -> config ),
2006- monitorDisabledIsOk ))
2202+ if (!keeper_config_read_file (kconfig ,
2203+ missingPgdataIsOk ,
2204+ pgIsNotRunningIsOk ,
2205+ monitorDisabledIsOk ))
20072206 {
20082207 /* errors have already been logged */
20092208 exit (EXIT_CODE_BAD_CONFIG );
20102209 }
2210+
2211+ log_info ("Targetting group %d in formation \"%s\"" ,
2212+ kconfig -> groupId ,
2213+ kconfig -> formation );
20112214 }
2012- break ;
20132215 }
2216+ }
20142217
2015- default :
2218+ /*
2219+ * We tried to see if we have a local keeper configuration to grab the
2220+ * groupId from, what if we don't have a local setup, or the local setup is
2221+ * not a keeper role.
2222+ */
2223+ if (kconfig -> groupId == -1 )
2224+ {
2225+ if (groupsCount == 1 )
20162226 {
2017- log_fatal ("Unrecognized configuration file \"%s\"" ,
2018- keeper -> config .pathnames .config );
2019- exit (EXIT_CODE_INTERNAL_ERROR );
2227+ /* we have only one group, it's group number zero, proceed */
2228+ kconfig -> groupId = 0 ;
2229+ kconfig -> pgSetup .pgKind = NODE_KIND_STANDALONE ;
2230+ }
2231+ else
2232+ {
2233+ log_error ("Please use the --group option to target a "
2234+ "specific group in formation \"%s\"" ,
2235+ kconfig -> formation );
2236+ exit (EXIT_CODE_BAD_ARGS );
20202237 }
20212238 }
20222239}
0 commit comments