diff options
author | Matthias Beyer <matthias.beyer@atos.net> | 2021-03-10 10:06:59 +0100 |
---|---|---|
committer | Matthias Beyer <matthias.beyer@atos.net> | 2021-03-10 10:06:59 +0100 |
commit | 73223d4cea89ca45600362d03b420579107ed7c1 (patch) | |
tree | 8ad0736f3c16f49efc0ad684b439fe2e908d2295 /src | |
parent | 5825e516a71c0809bc0531909397c14fc2eaea28 (diff) |
Add subcommand: container inspect
Signed-off-by: Matthias Beyer <matthias.beyer@atos.net>
Diffstat (limited to 'src')
-rw-r--r-- | src/cli.rs | 6 | ||||
-rw-r--r-- | src/commands/endpoint_container.rs | 309 |
2 files changed, 315 insertions, 0 deletions
@@ -1027,6 +1027,12 @@ pub fn cli<'a>() -> App<'a> { "#)) ) ) + + .subcommand(App::new("inspect") + .version(crate_version!()) + .about("Display details about the container") + .long_about("Display details about the container. Do not assume the output format to be stable.") + ) ) ) } diff --git a/src/commands/endpoint_container.rs b/src/commands/endpoint_container.rs index b891e8b..4989b26 100644 --- a/src/commands/endpoint_container.rs +++ b/src/commands/endpoint_container.rs @@ -90,6 +90,7 @@ pub async fn container(endpoint_names: Vec<String>, })?; exec(matches, container).await }, + Some(("inspect", _)) => inspect(container).await, Some((other, _)) => Err(anyhow!("Unknown subcommand: {}", other)), None => Err(anyhow!("No subcommand")), } @@ -154,3 +155,311 @@ async fn exec<'a>(matches: &ArgMatches, container: Container<'a>) -> Result<()> .await } +// Print inspect details about the container +// +// +// ABANDON HOPE ALL YE WHO ENTER HERE +// +// (Dante) +// +// This is the most ugly function of the whole codebase. As ugly as it is: It is simply printing +// things, nothing here is too complex code-wise (except some nested formatting stuff...) +async fn inspect<'a>(container: Container<'a>) -> Result<()> { + use std::io::Write; + use itertools::Itertools; + + let d = container.inspect().await?; + + fn option_vec(ov: Option<&Vec<String>>) -> String { + ov.map(|v| format!("Some({})", v.iter().join(", "))).unwrap_or_else(|| String::from("None")) + } + + fn option_vec_nl(ov: Option<&Vec<String>>, ind: usize) -> String { + ov.map(|v| v.iter().map(|s| format!("{:ind$}{s}", "", ind = ind, s = s)).join("\n")).map(|s| format!("\n{}", s)).unwrap_or_else(|| String::from("None")) + } + + fn option_tostr<T: ToString>(ots: Option<T>) -> String { + ots.map(|s| format!("Some({})", s.to_string())).unwrap_or_else(|| String::from("None")) + } + + writeln!(std::io::stdout(), "{}", indoc::formatdoc!(r#" + Container: {container_id} + + app_armor_profile: {app_armor_profile} + args: {args} + config: + attach_stderr: {config_attach_stderr} + attach_stdin: {config_attach_stdin} + attach_stdout: {config_attach_stdout} + cmd: {config_cmd} + domainname: {config_domainname} + entrypoint: {config_entrypoint} + env: {config_env} + exposed_ports: {config_exposed_ports} + hostname: {config_hostname} + image: {config_image} + labels: {config_labels} + on_build: {config_on_build} + open_stdin: {config_open_stdin} + stdin_once: {config_stdin_once} + tty: {config_tty} + user: {config_user} + working_dir: {config_working_dir} + + created: {created} + driver: {driver} + host_config: + cgroup_parent: {host_config_cgroup_parent} + container_id_file: {host_config_container_id_file} + cpu_shares: {host_config_cpu_shares} + cpuset_cpus: {host_config_cpuset_cpus} + memory: {host_config_memory} + memory_swap: {host_config_memory_swap} + network_mode: {host_config_network_mode} + pid_mode: {host_config_pid_mode} + port_bindings: {host_config_port_bindings} + privileged: {host_config_privileged} + publish_all_ports: {host_config_publish_all_ports} + readonly_rootfs: {host_config_readonly_rootfs} + hostname_path: {hostname_path} + hosts_path: {hosts_path} + log_path: {log_path} + id: {id} + image: {image} + mount_label: {mount_label} + name: {name} + network_settings: + bridge: {network_settings_bridge} + gateway: {network_settings_gateway} + ip_address: {network_settings_ip_address} + ip_prefix_len: {network_settings_ip_prefix_len} + mac_address: {network_settings_mac_address} + ports: {network_settings_ports} + networks: {network_settings_networks} + path: {path} + process_label: {process_label} + resolv_conf_path: {resolv_conf_path} + restart_count: {restart_count} + state: + error: {state_error} + exit_code: {state_exit_code} + finished_at: {state_finished_at} + oom_killed: {state_oom_killed} + paused: {state_paused} + pid: {state_pid} + restarting: {state_restarting} + running: {state_running} + started_at: {state_started_at} + status: {state_status} + mounts: {mounts} + "#, + + container_id = container.id(), + + app_armor_profile = d.app_armor_profile, + args = d.args.iter().join(", "), + + config_attach_stderr = d.config.attach_stderr.to_string(), + config_attach_stdin = d.config.attach_stdin.to_string(), + config_attach_stdout = d.config.attach_stdout.to_string(), + config_cmd = option_vec(d.config.cmd.as_ref()), + config_domainname = d.config.domainname, + config_entrypoint = option_vec(d.config.entrypoint.as_ref()), + config_env = option_vec_nl(d.config.env.as_ref(), 8), + config_exposed_ports = { + d.config.exposed_ports.map(|hm| { + let s = hm.iter() + .map(|(k, v_hm)| { + format!("{:ind$}{k}:\n{hm}", + "", ind = 8, + k = k, + hm = v_hm.iter() + .map(|(k, v)| format!("{:ind$}{k}: {v}", "", ind = 12, k = k, v = v)) + .collect::<Vec<_>>() + .join("\n") + ) + }) + .collect::<Vec<_>>() + .join("\n"); + + format!("\n{s}", s = s) + }) + .unwrap_or_else(|| String::from("None")) + }, + config_hostname = d.config.hostname, + config_image = d.config.image, + config_labels = { + d.config.labels + .map(|hm| { + let s = hm.iter() + .map(|(k, v)| format!("{:ind$}{k}: {v}", "", ind = 8, k = k, v = v)) + .collect::<Vec<_>>() + .join("\n"); + format!("\n{s}", s = s) + }) + .unwrap_or_else(|| String::from("None")) + }, + config_on_build = option_vec(d.config.on_build.as_ref()), + config_open_stdin = d.config.open_stdin.to_string(), + config_stdin_once = d.config.stdin_once.to_string(), + config_tty = d.config.tty.to_string(), + config_user = d.config.user, + config_working_dir = d.config.working_dir, + + created = d.created.to_string(), + driver = d.driver, + + host_config_cgroup_parent = option_tostr(d.host_config.cgroup_parent.as_ref()), + host_config_container_id_file = d.host_config.container_id_file, + host_config_cpu_shares = option_tostr(d.host_config.cpu_shares.as_ref()), + host_config_cpuset_cpus = option_tostr(d.host_config.cpuset_cpus.as_ref()), + host_config_memory = option_tostr(d.host_config.memory.as_ref()), + host_config_memory_swap = option_tostr(d.host_config.memory_swap.as_ref()), + host_config_network_mode = d.host_config.network_mode, + host_config_pid_mode = option_tostr(d.host_config.pid_mode.as_ref()), + host_config_port_bindings = { + d.host_config.port_bindings + .map(|hm| { + let s = hm.iter() + .map(|(k, v)| { + let v = v.iter() + .map(|hm| { + hm.iter() + .map(|(k, v)| { + format!("{:ind$}{k}: {v}", "", ind = 12, k = k, v = v) + }) + .collect::<Vec<_>>() + .join("\n") + }) + .collect::<Vec<_>>() + .join("\n"); + + format!("{:ind$}{k}: {v}", "", ind = 8, k = k, v = format!("\n{}", v)) + }) + .collect::<Vec<_>>() + .join("\n"); + format!("\n{s}", s = s) + }) + .unwrap_or_else(|| String::from("None")) + }, + host_config_privileged = d.host_config.privileged.to_string(), + host_config_publish_all_ports = d.host_config.publish_all_ports.to_string(), + host_config_readonly_rootfs = option_tostr(d.host_config.readonly_rootfs.as_ref()), + + hostname_path = d.hostname_path, + hosts_path = d.hosts_path, + log_path = d.log_path, + id = d.id, + image = d.image, + mount_label = d.mount_label, + name = d.name, + + network_settings_bridge = d.network_settings.bridge, + network_settings_gateway = d.network_settings.gateway, + network_settings_ip_address = d.network_settings.ip_address, + network_settings_ip_prefix_len = d.network_settings.ip_prefix_len.to_string(), + network_settings_mac_address = d.network_settings.mac_address, + network_settings_ports = { + d.network_settings.ports + .map(|hm| { + let s = hm.iter() + .map(|(k, v)| { + let v = v.as_ref().map(|v| { + v.iter() + .map(|hm| { + let s = hm.iter() + .map(|(k, v)| format!("{:ind$}{k}: {v}", "", ind = 12, k = k, v = v)) + .collect::<Vec<_>>() + .join("\n"); + format!("\n{s}", s = s) + }) + .collect::<Vec<_>>() + .join("\n") + }).unwrap_or_else(|| String::from("None")); + + format!("{:ind$}{k}: {v}", "", ind = 8, k = k, v = format!("\n{}", v)) + }) + .collect::<Vec<_>>() + .join("\n"); + format!("\n{s}", s = s) + }) + .unwrap_or_else(|| String::from("None")) + }, + network_settings_networks = { + let s = d.network_settings.networks.iter().map(|(k, v)| { + indoc::formatdoc!(r#" + {k}: + network_id: {network_id} + endpoint_id: {endpoint_id} + gateway: {gateway} + ip_address: {ip_address} + ip_prefix_len: {ip_prefix_len} + ipv6_gateway: {ipv6_gateway} + global_ipv6_address: {global_ipv6_address} + global_ipv6_prefix_len: {global_ipv6_prefix_len} + mac_address: {mac_address} + "#, + k = k, + network_id = v.network_id, + endpoint_id = v.endpoint_id, + gateway = v.gateway, + ip_address = v.ip_address, + ip_prefix_len = v.ip_prefix_len, + ipv6_gateway = v.ipv6_gateway, + global_ipv6_address = v.global_ipv6_address, + global_ipv6_prefix_len = v.global_ipv6_prefix_len.to_string(), + mac_address = v.mac_address, + ) + .lines() + .map(|s| format!("{:ind$}{s}", "", ind = 8, s = s)) + .join("\n") + }) + .collect::<Vec<_>>() + .join("\n"); + + format!("\n{}", s) + }, + + path = d.path, + process_label = d.process_label, + resolv_conf_path = d.resolv_conf_path, + restart_count = d.restart_count.to_string(), + + state_error = d.state.error, + state_exit_code = d.state.exit_code.to_string(), + state_finished_at = d.state.finished_at.to_string(), + state_oom_killed = d.state.oom_killed.to_string(), + state_paused = d.state.paused.to_string(), + state_pid = d.state.pid.to_string(), + state_restarting = d.state.restarting.to_string(), + state_running = d.state.running.to_string(), + state_started_at = d.state.started_at.to_string(), + state_status = d.state.status, + + mounts = { + let s = d.mounts.iter() + .map(|mount| { + indoc::formatdoc!(r#" + source: {source} + destination: {destination} + mode: {mode} + rw: {rw} + + "#, + source = mount.source, + destination = mount.destination, + mode = mount.mode, + rw = mount.rw.to_string() + ) + .lines() + .map(|s| format!("{:ind$}{s}", "", ind = 4, s = s)) + .join("\n") + }) + .collect::<Vec<_>>() + .join("\n"); + + format!("\n{}", s) + } + )).map_err(Error::from) +} + |