-
Notifications
You must be signed in to change notification settings - Fork 92
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
cli: add bootc-reinstall
binary
#1063
base: main
Are you sure you want to change the base?
Conversation
88934de
to
a645547
Compare
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.
Nice, thanks for getting the ball rolling here!
f6f60e7
to
30b8271
Compare
6b037d9
to
a4d4e6f
Compare
# Background The current usage instructions for bootc involve a long podman invocation. # Issue It's hard to remember and type the long podman invocation, making the usage of bootc difficult for users. See https://issues.redhat.com/browse/BIFROST-610 and https://issues.redhat.com/browse/BIFROST-611 (Epic https://issues.redhat.com/browse/BIFROST-594) # Solution We want to make the usage of bootc easier by providing a new Fedora/RHEL subpackage that includes a new binary `bootc-reinstall`. This binary will simplify the usage of bootc by providing a simple command line interface (configured either through CLI flags or a configuration file) with an interactive prompt that allows users to reinstall the current system using bootc. The commandline will handle helping the user choose SSH keys / users and ensure and warn the user about the destructive nature of the operation, and eventually issues they might run into in the various clouds (e.g. missing cloud agent on the target image) # Implementation Added new reinstall-cli crate that outputs the new bootc-reinstall binary. Modified the bootc.spec file to generate the new subpackage which includes this binary. This new crate depends on the existing utils crate. Refactored the tracing initialization from the bootc binary into the utils crate so that it can be reused by the new crate. The new CLI can either be configured through commandline flags or through a configuration file in a path set by the environment variable `BOOTC_REINSTALL_CONFIG`. The configuration file is a YAML file. # Limitations Only root SSH keys are supported. The multi user selection TUI is implemented, but if you choose anything other than root you will get an error. # Try Try out instructions: ```bash # Make srpm cargo xtask package-srpm # Mock group sudo usermod -a -G mock $(whoami) newgrp mock # Build RPM for RHEL mock --rebuild -r rhel+epel-9-x86_64 --rebuild target/bootc-*.src.rpm ``` Then install the RPM (`/var/lib/mock/rhel+epel-9-x86_64/result/bootc-reinstall-2*.el9.x86_64.rpm`) on [a rhel9 gcp vm](https://console.cloud.google.com/compute/instanceTemplates/details/rhel9-dev-1?project=bifrost-devel&authuser=1&inv=1&invt=Abn-jg) instance template # TODO Missing docs, missing functionality. Everything is in alpha stage. User choice / SSH keys / prompt disabling should also eventually be supported to be configured through commandline arguments or the configuration file. Signed-off-by: Omer Tuchfeld <[email protected]>
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.
Awesome progress!! Various comments follow
let root_authorized_keys_path = root_key.authorized_keys_path.clone(); | ||
|
||
podman_command_and_args.push("-v".to_string()); | ||
podman_command_and_args.push(format!( |
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 later followup: I think we could support --root-ssh-authorized-keys-fd
or so, and pass it via file descriptor. That'd avoid the bind dance here.
Or maybe we should learn --ssh-home-dir
, and directly scrape all keys from there?
|
||
Ok(selected_user_indices | ||
.iter() | ||
// Safe unwrap because we know the index is valid |
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.
I dug because I was curious what happens if MultiSelect
was passed an empty set, looks like it's this: https://github.com/console-rs/dialoguer/blob/6244c77656e8f3e934a2981c5c7e9dacf938a492/src/prompts/multi_select.rs#L211
OK.
} | ||
|
||
let home_dir = user_info.home_dir(); | ||
let user_authorized_keys_path = home_dir.join(".ssh/authorized_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.
It's a bit harder than this in the general case; in CoreOS derivatives we've pushed for https://github.com/coreos/ssh-key-dir for example.
let mut result = Vec::new(); | ||
|
||
for user_info in iter { | ||
if user_info.uid() < 1000 && user_info.uid() != 0 { |
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.
Note though 1000 is recommend, it's not required to be the barrier. See https://systemd.io/UIDS-GIDS/
But see below comment about loginctl
/// multi-threaded. | ||
pub(crate) fn get_all_users_keys() -> Result<Vec<UserKeys>> { | ||
#[allow(unsafe_code)] | ||
let iter = unsafe { uzers::all_users() }; |
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.
It may be simpler and more reliable here to parse the output of loginctl -j
. That will only give us logged in users, which in the common case of non-root user + sudo will give us both, and in the case of just logging in as root won't offer other users, which also seems right.
.concat() | ||
} | ||
|
||
pub(crate) fn run_podman(all_args: Vec<String>) -> Result<()> { |
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 isn't specific to running podman? See below
.status() | ||
.context(format!( | ||
"Failed to run the command \"{}\"", | ||
all_args.join(" ") |
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.
I think the debug output of Command
correctly handles shell quoting, which is pretty useful because it means you can copy-paste.
It looks like the main goal of this run_podman
is really to add the error context from the command so we could just call it run_cmd
or something?
But that said, I notice an immediate large problem here in that we're not checking the return code of the child...this type of thing is why I added the CommandRunExt
helpers in the bootc-utils crate.
I think we could extend it with something like run_with_cmd_errcontext()
or so?
let all_args = podman::command(&config.bootc_image, prompt::get_root_key()?); | ||
println!("Going to run command: {}", all_args.join(" ")); | ||
prompt::temporary_developer_protection_prompt()?; | ||
podman::run_podman(all_args)?; |
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.
One thing that'd be cool I think is on failure, save all the provided interactive answers in e.g. /run/bootc/reinstall.yaml
or so, and tell the user:
Installation answers saved in /run/bootc/reinstall.yaml
error: Out of disk space
Then when we re-run we detect that and offer to resume from saved answers.
@@ -0,0 +1,31 @@ | |||
[package] | |||
name = "bootc-reinstall" |
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.
By far the biggest thing we need to figure out before trying to land this is one of the hardest problems: naming the thing.
I am not opposed to bootc-reinstall
...but I'm not in love with it either. One minor thing I mentioned elsewhere is that if we have both bootc
and bootc-reinstall
then shells will stop tab completion without adding a space.
I guess in most cases they'll be mutually exclusive? i.e. you only have bootc-reinstall
on non-bootc hosts? But I'm sure people will ask us why we don't ship this on bootc images too...
Another option which I like a little better is system-reinstall-bootc
- it's longer to type which is a good thing here, doesn't clash with tab completion for bootc
, and it feels more "tightly scoped".
Having it not have the bootc
prefix also makes clearer that logically it's a higher level tool that in theory could live separately or be maintained elsewhere.
IOW I think people would get legitimately confused if we have bootc install
and bootc-reinstall
. It seems easier to message system-reinstall-bootc
is a standalone tool.
Open to other names too of course...
|
||
ensure!( | ||
value.is_empty(), | ||
"unknown keys {:?} in config file", |
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.
Confused why we aren't using #[derive(Deserialize)]
on the struct and https://serde.rs/container-attrs.html#deny_unknown_fields
Background
The current usage instructions for bootc involve a long podman invocation.
Issue
It's hard to remember and type the long podman invocation, making the usage of bootc difficult for users.
See https://issues.redhat.com/browse/BIFROST-610 and https://issues.redhat.com/browse/BIFROST-611 (Epic https://issues.redhat.com/browse/BIFROST-594)
Solution
We want to make the usage of bootc easier by providing a new Fedora/RHEL subpackage that includes a new binary
bootc-reinstall
. This binary will simplify the usage of bootc by providing a simple command line interface (configured either through CLI flags or a configuration file) with an interactive prompt that allows users to reinstall the current system using bootc. The commandline will handle helping the user choose SSH keys / users and ensure and warn the user about the destructive nature of the operation, and eventually issues they might run into in the various clouds (e.g. missing cloud agent on the target image)Implementation
Added new reinstall-cli crate that outputs the new bootc-reinstall binary. Modified the bootc.spec file to generate the new subpackage which includes this binary.
This new crate depends on the existing utils crate.
Refactored the tracing initialization from the bootc binary into the utils crate so that it can be reused by the new crate. The new CLI can either be configured through commandline flags or through a configuration file in a path set by the environment variable
BOOTC_REINSTALL_CONFIG
.The configuration file is a YAML file.
Limitations
Only root SSH keys are supported. The multi user selection TUI is implemented, but if you choose anything other than root you will get an error.
Try
Try out instructions:
Then install the RPM (
/var/lib/mock/rhel+epel-9-x86_64/result/bootc-reinstall-2*.el9.x86_64.rpm
) on a rhel9 gcp vm instance templateTODO
Missing docs, missing functionality. Everything is in alpha stage. User choice / SSH keys / prompt disabling should also eventually be supported to be configured through commandline arguments or the configuration file.
Demo