-
Notifications
You must be signed in to change notification settings - Fork 1.1k
SSH Runner: Fix several issues and refactor code #17357
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop2
Are you sure you want to change the base?
Conversation
cf0893c to
6c7b1b8
Compare
ff37095 to
5032b3c
Compare
e0ce817 to
83c905d
Compare
| assert "Restore: pkg/0.2:8631cf963dbbb4d7a378a64a6fd1dc57558bc2fe" in client.out | ||
| assert "Restore: pkg/0.2:8631cf963dbbb4d7a378a64a6fd1dc57558bc2fe metadata" in client.out | ||
| assert "Removing container" in client.out | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please enable pep-8 style checks in your IDE.
f359099 to
a025ba9
Compare
| from conan.test.assets.sources import gen_function_h, gen_function_cpp | ||
|
|
||
|
|
||
| def docker_from_env(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
feels like tests related to the Docker runner belong in a separate PR - if changes are needed for this PR, maybe open a new one that can be independently reviewed and merged ahead of this?
| hostname = self.host_profile.runner.get("host") | ||
| if not hostname: | ||
| raise ConanException("Host not specified in runner.ssh configuration") | ||
| configfile = self.host_profile.runner.get("configfile", False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is being changed - I like that we can accept a path to a file, rather than assume the user, but the name config variable seems odd - I would be more explicit - both openssh and paramiko call it "ssh_config"
| if ssh_config and ssh_config.lookup(hostname): | ||
| hostname = ssh_config.lookup(hostname)['hostname'] | ||
| self.client = SSHClient() | ||
| self.client.set_missing_host_key_policy(AutoAddPolicy()) # Auto accept unknown keys |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the sake of security, drop this line. Assume the user has previously explicitly added the host key - this needs to be documented and done outside of Conan's realm
| python_command = remote_folder + "/venv" + "/Scripts" + "/python.exe" | ||
| else: | ||
| python_command = remote_folder + "/venv" + "/bin" + "/python" | ||
| if not self.remote_conn.run_command(f"{conan_cmd} remote update conancenter --url='https://center2.conan.io'", "Updating conancenter remote").success: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Outside of the scope of the SSH runner - given that the runner will create a new conan home on the fly (and that this feature was never publicly released) - users will already have the correct value for Conan Center, so this is unnecessary.
| def _install_conan_remotely(self, python_command: str, version: str): | ||
| self.output.verbose(f"Installing conan version: {version}") | ||
| # Note: this may fail on windows | ||
| result = self.remote_conn.run_command(f"{python_command} -m pip install conan{f'=={version}' if version != 'dev' else ' --upgrade'}", "Installing conan") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not --force-reinstall if we are currently assuming the venv only has a single version?
| result = self.remote_conn.run_command(f'{self.remote_python_command} -c "import os; print(os.name)"', "Checking remote OS type") | ||
| if not result.success: | ||
| self.output.error("Unable to determine remote OS type") | ||
| self.is_remote_windows = result.stdout == "nt" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please keep self.remote_is_windows
| self.client: SSHClient = client | ||
| self.runner_output = runner_output | ||
|
|
||
| def put(self, src: str, dst: str) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
put, get and mkdir are specific FTP words - so they make sense for sftp.put(), but not so much for a RemoteConnection.put() - I would suggest calling them sftp_put() and spft_get() etc, if we are so inclined to have these in this class
| if sys.stdout.isatty(): | ||
| width, height = os.get_terminal_size() | ||
| else: | ||
| width, height = 80, 24 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should fallback to paramiko's defaults, and not hardcode a new set without additional context
| # This is the easiest and better working approach but not testable | ||
| # sys.stdout.buffer.write(line) | ||
| # sys.stdout.buffer.flush() | ||
| line = remove_cursor_movements(line.replace(b'\r', b'').decode(errors='ignore').strip()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if I'm getting this right.. are we doing a line.replace() on every line (or rather, every chunk of read bytes, as they're not necessarily lines), and operating on each line with the remove_cursos_movements ?
what is the aim of this? it was working fine before (despite the slightly off logic for windows hosts) - we're dealing with potentially tens of megabytes of log data - should it not be streamed to the terminal as quickly as possible, minimising overhead to the fullest extent?
| remote_tmp_dir = _Path(self.remote_create_dir) | ||
|
|
||
| # Check if recipe defines a root folder | ||
| app = ConanApp(self.conan_api) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably be better explained in the PR description and I would suggest handling this in a separate PR for a proper discussion
With the proposed logic, the conanfile would be loaded 3 times:
- during the first export locally
- here
- remotely, when invoking Conan create
We may as well simplify everything - we know that the recipe was already exported in the local cache - thus, all paths relative to the recipe were already resolved in order for that to succeed. We can export the recipe as it is in the cache, or use the cona cache save/restore for this - and just restore it remotely (we may need to switch a conan install + conan test instead) - but as I said, best discussed in a separate PR altogether
Changelog: Feature: Introducing
SSH runnerDocs: conan-io/docs#3972
Changes:
jinja2templates intact to allow remote runner to render them locallybuildrecipes. Solved by always exporting all the packages and forcing a local cache refresh&and*during ssh remote invoquesdevelopbranch, documenting this one.