Conversation
e487535 to
f99e917
Compare
387d52f to
efc2d57
Compare
|
|
||
| self.rescan_block_devices_info() | ||
|
|
||
| if self.file_exists('/usr/bin/yum'): |
There was a problem hiding this comment.
woultnt it make sense to prefer dnf over yum ?
There was a problem hiding this comment.
I’d say that for now, yum is the default, since we’re on version 8.3 and are adding dnf alongside it for 9.0. And as we don't have dnf on the 8.3 (and before).
If we wanted dnf to be the default over yum for 9.0, we would need to base that on version detection first, before the presence of the file.
There was a problem hiding this comment.
There is no yum command on XCP-ng 9, so it doesn't matter much in practice :)
There was a problem hiding this comment.
There is actually a yum compatibility wrapper (in package yum). It's surely safer to look for dnf first, just in case.
| try: | ||
| history_str = self.ssh('yum history list --noplugins') | ||
| history_str = self.ssh(f'{self.package_manager} history list --noplugins') | ||
| except commands.SSHCommandFailed: |
There was a problem hiding this comment.
It's not directly related to your PR, and is probably code I wrote initially, so I just make it a suggestion: we should probably only tolerate failures that we can verify to be actually caused by an empty history, rather than any command failure that could happen for whatever reason. And let the exception through in the other cases.
| if not history: | ||
| # we need a transaction to already exist. Install a small package with no deps, and remove it immediately. | ||
| logging.info(f"[{self}] Install and remove a small package to workaround empty yum history.") | ||
| small_pkg = 'gpm-libs' if self.xcp_version.major == 8 else 'bubblewrap' |
There was a problem hiding this comment.
I suggest dummypkg, a package that must be present in our repos because other tests rely on it. If not present in XCP-ng 9, we should add it there too.
There was a problem hiding this comment.
It's not in xcp-ng 9 yet
We could use the same package on xcp-ng 9 and xcp-ng 8.3 👍
| pytest.skip(f"This test requires an XCP-ng < {version_str} host") | ||
|
|
||
| @pytest.fixture(scope='session') | ||
| @pytest.fixture(scope='function') |
There was a problem hiding this comment.
Most host_xxx fixtures in this global conftest.py are session-scoped. I think that to change the scope we should change the fixture name too to avoid wrong expectations. And/or bring the fixture closer to where it's used.
| ret = fn() | ||
| if not invert and ret: | ||
| return | ||
| return ret |
There was a problem hiding this comment.
nitpick: it took me time to understand this part of the commit message: "Enhance wait_for to return the callback value ", because I thought it meant you returned the callback itself, as a value.
Due to the use of word "callback", I thought there was a function we'd call back once returned, but you just mean you return the value produced by the function executed by wait_for.
Suggestion: "Enhance wait_for to return the polled function's result"
| tmp_file = res_host.ssh('mktemp') | ||
| session = f"detached-cat-{self.uuid}" | ||
| ret = False | ||
| # screen install is broken on RHEL, see https://bugzilla.redhat.com/show_bug.cgi?id=2385964 |
There was a problem hiding this comment.
Okay, another "I got confused by the wording" item: I thought there was something about display issues 😂.
But you're talking about screen, the tool.
Suggestion:
The `screen` tool may be broken on RHEL, see https://bugzilla.redhat.com/show_bug.cgi?id=2385964
There was a problem hiding this comment.
I forgot to ask: was the PR tested on XCP-ng 8 too? Probably so but it would be best to mention it in the PR description so that we don't ask :)
There was a problem hiding this comment.
I tested both for the tests I modified, but I haven't run a complete run on XCP-ng 8.3 yet.
|
I integrated the requested changes and updated on master |
|
|
||
| self.rescan_block_devices_info() | ||
|
|
||
| if self.file_exists('/usr/bin/yum'): |
There was a problem hiding this comment.
There is actually a yum compatibility wrapper (in package yum). It's surely safer to look for dnf first, just in case.
| """ | ||
| try: | ||
| history_str = self.ssh('yum history list --noplugins') | ||
| except commands.SSHCommandFailed: | ||
| # yum history list fails if the list is empty, and it's also not possible to rollback | ||
| # to before the first transaction, so "0" would not be appropriate as last transaction. | ||
| # To workaround this, create transactions: install and remove a small package. | ||
| logging.info(f"[{self}] Install and remove a small package to workaround empty yum history.") | ||
| self.yum_install(['gpm-libs']) | ||
| self.yum_remove(['gpm-libs']) | ||
| history_str = self.ssh('yum history list --noplugins') | ||
|
|
||
| history = history_str.splitlines() | ||
| line_index = None | ||
| for i in range(len(history)): | ||
| if history[i].startswith('--------'): | ||
| line_index = i | ||
| break | ||
| history_str = self.ssh(f'{self.package_manager} history list --noplugins') | ||
| except commands.SSHCommandFailed as e: | ||
| if 'Error: Failed history list' not in e.stdout: | ||
| # this is not the expected error for an empty history list, re-raise the exception | ||
| raise e | ||
| # yum history list fails on xcp-ng 8 if the list is empty, but dnf history list works properly on xcp-ng 9. | ||
| # Just use a fake empty value to deal with both versions in the same way. | ||
| if self.package_manager == 'yum': | ||
| history_str = '''ID | Command line | Date and time | Action(s) | Altered | ||
| -------------------------------------------------------------------------------''' | ||
| else: | ||
| raise | ||
|
|
||
| if line_index is None: | ||
| raise Exception('Unable to get yum transactions') | ||
| def split_history(history_str: str) -> list[str]: | ||
| history = history_str.splitlines() | ||
| line_index = None | ||
| for i in range(len(history)): | ||
| if history[i].startswith('--------'): | ||
| line_index = i | ||
| break | ||
| if line_index is None: | ||
| raise Exception('Unable to get yum transactions') | ||
| return history[line_index + 1:] | ||
|
|
||
| history = split_history(history_str) | ||
| if not history: | ||
| # we need a transaction to already exist. Install a small package with no deps, and remove it immediately. | ||
| logging.info(f"[{self}] Install and remove a small package to workaround empty yum history.") | ||
| self.yum_install(['dummypkg']) | ||
| self.yum_remove(['dummypkg']) | ||
| history = split_history(self.ssh(f'{self.package_manager} history list --noplugins')) | ||
|
|
||
| try: | ||
| return int(history[line_index + 1].split()[0]) | ||
| return int(history[0].split()[0]) | ||
| except ValueError: |
There was a problem hiding this comment.
(nitpick?) This change could have been easier to read by introducing split_history() in a preliminary commit (which involves some reindentation), and then the effective difference specific to v9 would have stood out more clearly in the diff.
| except commands.SSHCommandFailed as e: | ||
| if 'Error: Failed history list' not in e.stdout: | ||
| # this is not the expected error for an empty history list, re-raise the exception | ||
| raise e |
There was a problem hiding this comment.
raise without argument would be more correct I think
There was a problem hiding this comment.
Agreed, because it would avoid changing the stack trace 👍
| logging.info(f"[{self}] Install and remove a small package to workaround empty yum history.") | ||
| self.yum_install(['dummypkg']) | ||
| self.yum_remove(['dummypkg']) | ||
| history = split_history(self.ssh(f'{self.package_manager} history list --noplugins')) |
There was a problem hiding this comment.
A few words in the commit message about that particular behavior change and how it is restructured would be nice (yes the can read the code and actually understand, but a "reading guide" would actually allow the reader to spend less time)
There was a problem hiding this comment.
Done. This feels very much not useful to me, but if that's useful to you…
| self.ssh( | ||
| f'yum history rollback --enablerepo=xcp-ng-base,xcp-ng-testing,xcp-ng-updates {self.saved_rollback_id} -y' | ||
| ) | ||
| repositories = ['xcp-ng-base'] | ||
| if self.xcp_version.major == 8: | ||
| # TODO: activate those repositories in xcp-ng 9. For now they are not available. | ||
| repositories += ['xcp-ng-testing', 'xcp-ng-updates'] | ||
| self.ssh(f'{self.package_manager} history rollback' | ||
| f' --enablerepo={",".join(repositories)} {self.saved_rollback_id} -y') |
There was a problem hiding this comment.
This also is not implied by "use the right package manager". Sure this is v9 support, and relates to repositories, so likely has a place grouped as such, but would be nice in the commit message
| self.yum_install(['gpm-libs']) | ||
| self.yum_remove(['gpm-libs']) |
There was a problem hiding this comment.
switching to dummypkg because this package is not available in v9?
There was a problem hiding this comment.
Are you sure about that? I added it yesterday :)
| def check_file_type(expected: str) -> None: | ||
| assert host.ssh(f'file --mime-type -b {filepath}') == expected | ||
| def check_file_type(expected: list[str]) -> None: | ||
| assert host.ssh(f'file --mime-type -b {filepath}') in expected | ||
|
|
||
| if compress == 'none': | ||
| check_file_type('application/x-tar') | ||
| check_file_type(['application/x-tar']) | ||
| elif compress == 'gzip': | ||
| check_file_type('application/x-gzip') | ||
| check_file_type(['application/x-gzip', 'application/gzip']) | ||
| elif compress == 'zstd': | ||
| check_file_type('application/octet-stream') | ||
| check_file_type(['application/octet-stream', 'application/zstd']) |
There was a problem hiding this comment.
I disagree this makes things "more robust". application/octet-stream is "I did not recognize this file", which should be an error when the more specific type is known. I like my "test_export: support modern distro knowing about zstd and better gzip" patch in ydi/9 better 😄
There was a problem hiding this comment.
I replace this commit with yours
| raise InstallationFailed(failed) | ||
| return cmd() | ||
| return wait_for(inner, msg, timeout_secs=timeout_secs) | ||
| wait_for(inner, msg, timeout_secs=timeout_secs) |
There was a problem hiding this comment.
It feels awkward to have wait_for gain a real return value, and just not propagate it here.
Any reason not to change this function to behave similarly to ` (possibly by just changing the return type annotation)?
There was a problem hiding this comment.
Because the function to call returns a bool (Callable[[], bool]), so, given the behavior of wait_for, the only possible returned value is Literal[True], which is not very useful.
I'm fine with both versions, though
| def wait_for(fn: Callable[[], object], msg: str | None = None, timeout_secs: int = 2 * 60, retry_delay_secs: int = 2, | ||
| invert: bool = False) -> None: | ||
| def wait_for(fn: Callable[[], T], msg: str | None = None, timeout_secs: int = 2 * 60, retry_delay_secs: int = 2, | ||
| invert: bool = False) -> T: |
There was a problem hiding this comment.
This use of the return value should be documented, a function docstring would seem adequate.
Especially the behavior with invert=True may need some notice, that only False-like values will be returned in this case (so not very useful).
| tmp_file = res_host.ssh('mktemp') | ||
| session = f"detached-cat-{self.uuid}" | ||
| ret = False | ||
| # The screen package installation is broken on RHEL, see https://bugzilla.redhat.com/show_bug.cgi?id=2385964 |
This is required for XCP-ng 9, which uses dnf. Reading guide for get_last_yum_history_tid(): - Behavior change: the empty-history workaround now only runs when the parsed history is genuinely empty, instead of on any failure of "history list". Other errors now propagate (previously they were masked by the workaround). dnf succeeds on an empty history (only the header is output) while yum fails with "Error: Failed history list", so a fake header row is used for yum to handle both the same way. - Restructure: output parsing was extracted into a split_history() helper, and the workaround (install/remove a dummy package to create a transaction) is now triggered by the empty parsed output rather than by the command failing. Also yum_restore_saved_state() doesn't activate xcp-ng-testing and xcp-ng-updates repositories, as they are not yet available. Signed-off-by: Gaëtan Lehmann <gaetan.lehmann@vates.tech>
Signed-off-by: Yann Dirson <yann.dirson@vates.tech>
Enhance wait_for to return the polled function's result and use it to retry file server header retrieval until the server is ready. Change host_with_hsts fixture to function scope for test isolation, and move it in the module where it's used, to make clear it's not a session scope anymore. Signed-off-by: Gaëtan Lehmann <gaetan.lehmann@vates.tech>
Signed-off-by: Gaëtan Lehmann <gaetan.lehmann@vates.tech>
Adapt to XCP-ng 9 specificities