@@ -55,6 +55,39 @@ def __call__(
5555 setattr (namespace , self .dest , current )
5656
5757
58+ class _SshKeyAction (argparse .Action ):
59+ """Select key-file auth and override an agent default from config or env."""
60+
61+ def __call__ (
62+ self ,
63+ parser : argparse .ArgumentParser ,
64+ namespace : argparse .Namespace ,
65+ values : str ,
66+ option_string : str | None = None ,
67+ ) -> None :
68+ del parser , option_string
69+ namespace .ssh_key = values
70+ namespace .ssh_agent = False
71+
72+
73+ class _SshAgentAction (argparse .Action ):
74+ """Select agent auth and override a key default from config or env."""
75+
76+ def __init__ (self , option_strings : Sequence [str ], dest : str , ** kwargs : Any ) -> None :
77+ super ().__init__ (option_strings , dest , nargs = 0 , ** kwargs )
78+
79+ def __call__ (
80+ self ,
81+ parser : argparse .ArgumentParser ,
82+ namespace : argparse .Namespace ,
83+ values : Any ,
84+ option_string : str | None = None ,
85+ ) -> None :
86+ del parser , values , option_string
87+ namespace .ssh_agent = True
88+ namespace .ssh_key = None
89+
90+
5891def _parser (defaults : dict [str , Any ] | None = None ) -> argparse .ArgumentParser :
5992 parser = argparse .ArgumentParser (prog = "ch-diag" , description = "ClickHouse diagnostics" )
6093 parser .add_argument ("--config" , help = "TOML configuration file (must precede command)" )
@@ -152,7 +185,14 @@ def _add_collection_arguments(parser: argparse.ArgumentParser) -> None:
152185 parser .add_argument ("--ssh-host" )
153186 parser .add_argument ("--ssh-port" , type = int , default = 22 )
154187 parser .add_argument ("--ssh-user" )
155- parser .add_argument ("--ssh-key" )
188+ ssh_auth = parser .add_mutually_exclusive_group ()
189+ ssh_auth .add_argument ("--ssh-key" , action = _SshKeyAction )
190+ ssh_auth .add_argument (
191+ "--ssh-agent" ,
192+ action = _SshAgentAction ,
193+ default = False ,
194+ help = "authenticate through the existing SSH_AUTH_SOCK agent" ,
195+ )
156196 parser .add_argument ("--ssh-known-hosts" )
157197
158198
@@ -200,18 +240,35 @@ def _connection(args: argparse.Namespace) -> ConnectionConfig:
200240
201241
202242def _ssh_config (args : argparse .Namespace ) -> SshConfig | None :
203- values = (args .ssh_host , args .ssh_user , args .ssh_key , args .ssh_known_hosts )
243+ values = (
244+ args .ssh_host ,
245+ args .ssh_user ,
246+ args .ssh_key ,
247+ args .ssh_agent ,
248+ args .ssh_known_hosts ,
249+ )
204250 if not any (values ):
205251 return None
206- if not all ( values ) :
252+ if not args . ssh_host or not args . ssh_user or not args . ssh_known_hosts :
207253 raise ValueError (
208- "SSH requires --ssh-host, --ssh-user, --ssh-key and --ssh-known-hosts"
254+ "SSH requires --ssh-host, --ssh-user, --ssh-known-hosts and "
255+ "exactly one of --ssh-key or --ssh-agent"
209256 )
257+ if bool (args .ssh_key ) == bool (args .ssh_agent ):
258+ raise ValueError ("SSH requires exactly one of --ssh-key or --ssh-agent" )
259+ agent_path = None
260+ if args .ssh_agent :
261+ agent_path = os .environ .get ("SSH_AUTH_SOCK" )
262+ if not agent_path :
263+ raise ValueError (
264+ "--ssh-agent requires SSH_AUTH_SOCK to reference a running agent"
265+ )
210266 return SshConfig (
211267 host = args .ssh_host ,
212268 port = args .ssh_port ,
213269 username = args .ssh_user ,
214270 client_key = args .ssh_key ,
271+ agent_path = agent_path ,
215272 known_hosts = args .ssh_known_hosts ,
216273 )
217274
0 commit comments