mirror of
https://github.com/aljazceru/kata-containers.git
synced 2026-01-05 23:44:21 +01:00
Allow the `agent-ctl` tool to connect to a Hybrid VSOCK hypervisor such as Cloud Hypervisor or Firecracker. Fixes: #2914. Signed-off-by: James O. D. Hunt <james.o.hunt@intel.com>
322 lines
10 KiB
Rust
322 lines
10 KiB
Rust
// Copyright (c) 2020 Intel Corporation
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
#[macro_use]
|
|
extern crate lazy_static;
|
|
|
|
use crate::types::Config;
|
|
use anyhow::{anyhow, Result};
|
|
use clap::{crate_name, crate_version, App, Arg, SubCommand};
|
|
use std::io;
|
|
use std::process::exit;
|
|
|
|
// Convenience macro to obtain the scope logger
|
|
#[macro_export]
|
|
macro_rules! sl {
|
|
() => {
|
|
slog_scope::logger()
|
|
};
|
|
}
|
|
|
|
mod client;
|
|
mod rpc;
|
|
mod types;
|
|
mod utils;
|
|
|
|
const DEFAULT_LOG_LEVEL: slog::Level = slog::Level::Info;
|
|
|
|
const DESCRIPTION_TEXT: &str = r#"DESCRIPTION:
|
|
Low-level test tool that allows basic interaction with
|
|
the Kata Containers agent using agent API calls."#;
|
|
|
|
const ABOUT_TEXT: &str = "Kata Containers agent tool";
|
|
|
|
const WARNING_TEXT: &str = r#"WARNING:
|
|
This tool is for *advanced* users familiar with the low-level agent API calls.
|
|
Further, it is designed to be run on test and development systems **only**:
|
|
since the tool can make arbitrary API calls, it is possible to easily confuse
|
|
irrevocably other parts of the system or even kill a running container or
|
|
sandbox."#;
|
|
|
|
// The VSOCK port number the Kata agent uses to listen to API requests on.
|
|
const DEFAULT_KATA_AGENT_API_VSOCK_PORT: &str = "1024";
|
|
|
|
fn make_examples_text(program_name: &str) -> String {
|
|
let abstract_server_address = "unix://@/foo/bar/abstract.socket";
|
|
let bundle = "$bundle_dir";
|
|
let config_file_uri = "file:///tmp/config.json";
|
|
let container_id = "$container_id";
|
|
let local_server_address = "unix:///tmp/local.socket";
|
|
let sandbox_id = "$sandbox_id";
|
|
let vsock_server_address = "vsock://3:1024";
|
|
let hybrid_vsock_server_address = "unix:///run/vc/vm/foo/clh.sock";
|
|
|
|
format!(
|
|
r#"EXAMPLES:
|
|
|
|
- Check if the agent is running:
|
|
|
|
$ {program} connect --server-address "{vsock_server_address}" --cmd Check
|
|
|
|
- Connect to the agent using a Hybrid VSOCK hypervisor (here Cloud Hypervisor):
|
|
|
|
$ {program} connect --server-address "{hybrid_vsock_server_address}" --hybrid-vsock --cmd Check
|
|
|
|
- Connect to the agent using local sockets (when running in same environment as the agent):
|
|
|
|
# Local socket
|
|
$ {program} connect --server-address "{local_server_address}" --cmd Check
|
|
|
|
# Abstract socket
|
|
$ {program} connect --server-address "{abstract_server_address}" --cmd Check
|
|
|
|
- Query the agent environment:
|
|
|
|
$ {program} connect --server-address "{vsock_server_address}" --cmd GetGuestDetails
|
|
|
|
- List all available (built-in and Kata Agent API) commands:
|
|
|
|
$ {program} connect --server-address "{vsock_server_address}" --cmd list
|
|
|
|
- Generate a random container ID:
|
|
|
|
$ {program} generate-cid
|
|
|
|
- Generate a random sandbox ID:
|
|
|
|
$ {program} generate-sid
|
|
|
|
- Attempt to create 7 sandboxes, ignoring any errors:
|
|
|
|
$ {program} connect --server-address "{vsock_server_address}" --repeat 7 --cmd CreateSandbox
|
|
|
|
- Query guest details forever:
|
|
|
|
$ {program} connect --server-address "{vsock_server_address}" --repeat -1 --cmd GetGuestDetails
|
|
|
|
- Send a 'SIGUSR1' signal to a container process:
|
|
|
|
$ {program} connect --server-address "{vsock_server_address}" --cmd 'SignalProcess signal=usr1 sid={sandbox_id} cid={container_id}'
|
|
|
|
- Create a sandbox with a single container, and then destroy everything:
|
|
|
|
$ {program} connect --server-address "{vsock_server_address}" --cmd CreateSandbox
|
|
$ {program} connect --server-address "{vsock_server_address}" --bundle-dir {bundle:?} --cmd CreateContainer
|
|
$ {program} connect --server-address "{vsock_server_address}" --cmd DestroySandbox
|
|
|
|
- Create a Container using a custom configuration file:
|
|
|
|
$ {program} connect --server-address "{vsock_server_address}" --bundle-dir {bundle:?} --cmd 'CreateContainer spec={config_file_uri}'
|
|
"#,
|
|
abstract_server_address = abstract_server_address,
|
|
bundle = bundle,
|
|
config_file_uri = config_file_uri,
|
|
container_id = container_id,
|
|
local_server_address = local_server_address,
|
|
program = program_name,
|
|
sandbox_id = sandbox_id,
|
|
vsock_server_address = vsock_server_address,
|
|
hybrid_vsock_server_address = hybrid_vsock_server_address,
|
|
)
|
|
}
|
|
|
|
fn connect(name: &str, global_args: clap::ArgMatches) -> Result<()> {
|
|
let args = global_args
|
|
.subcommand_matches("connect")
|
|
.ok_or("BUG: missing sub-command arguments".to_string())
|
|
.map_err(|e| anyhow!(e))?;
|
|
|
|
let interactive = args.is_present("interactive");
|
|
let ignore_errors = args.is_present("ignore-errors");
|
|
|
|
let server_address = args
|
|
.value_of("server-address")
|
|
.ok_or("need server adddress".to_string())
|
|
.map_err(|e| anyhow!(e))?
|
|
.to_string();
|
|
|
|
let mut commands: Vec<&str> = Vec::new();
|
|
|
|
if !interactive {
|
|
commands = args
|
|
.values_of("cmd")
|
|
.ok_or("need commands to send to the server".to_string())
|
|
.map_err(|e| anyhow!(e))?
|
|
.collect();
|
|
}
|
|
|
|
// Cannot fail as a default has been specified
|
|
let log_level_name = global_args.value_of("log-level").unwrap();
|
|
|
|
let log_level = logging::level_name_to_slog_level(log_level_name).map_err(|e| anyhow!(e))?;
|
|
|
|
let writer = io::stdout();
|
|
let (logger, _guard) = logging::create_logger(name, crate_name!(), log_level, writer);
|
|
|
|
let timeout_nano: i64 = match args.value_of("timeout") {
|
|
Some(t) => utils::human_time_to_ns(t).map_err(|e| e)?,
|
|
None => 0,
|
|
};
|
|
|
|
let hybrid_vsock_port: u64 = args
|
|
.value_of("hybrid-vsock-port")
|
|
.ok_or("Need Hybrid VSOCK port number")
|
|
.map(|p| p.parse::<u64>().unwrap())
|
|
.map_err(|e| anyhow!("VSOCK port number must be an integer: {:?}", e))?;
|
|
|
|
let bundle_dir = args.value_of("bundle-dir").unwrap_or("").to_string();
|
|
|
|
let hybrid_vsock = args.is_present("hybrid-vsock");
|
|
|
|
let cfg = Config {
|
|
server_address,
|
|
bundle_dir,
|
|
interactive,
|
|
ignore_errors,
|
|
timeout_nano,
|
|
hybrid_vsock_port,
|
|
hybrid_vsock,
|
|
};
|
|
|
|
let result = rpc::run(&logger, &cfg, commands);
|
|
|
|
if result.is_err() {
|
|
return result;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn real_main() -> Result<()> {
|
|
let name = crate_name!();
|
|
|
|
let hybrid_vsock_port_help = format!(
|
|
"Kata agent VSOCK port number (only useful with --hybrid-vsock) [default: {}]",
|
|
DEFAULT_KATA_AGENT_API_VSOCK_PORT
|
|
);
|
|
|
|
let app = App::new(name)
|
|
.version(crate_version!())
|
|
.about(ABOUT_TEXT)
|
|
.long_about(DESCRIPTION_TEXT)
|
|
.after_help(WARNING_TEXT)
|
|
.arg(
|
|
Arg::with_name("log-level")
|
|
.long("log-level")
|
|
.short("l")
|
|
.help("specific log level")
|
|
.default_value(logging::slog_level_to_level_name(DEFAULT_LOG_LEVEL).unwrap())
|
|
.possible_values(&logging::get_log_levels())
|
|
.takes_value(true)
|
|
.required(false),
|
|
)
|
|
.subcommand(
|
|
SubCommand::with_name("connect")
|
|
.about("Connect to agent")
|
|
.after_help(WARNING_TEXT)
|
|
.arg(
|
|
Arg::with_name("bundle-dir")
|
|
.long("bundle-dir")
|
|
.help("OCI bundle directory")
|
|
.takes_value(true)
|
|
.value_name("directory"),
|
|
)
|
|
.arg(
|
|
Arg::with_name("cmd")
|
|
.long("cmd")
|
|
.short("c")
|
|
.takes_value(true)
|
|
.multiple(true)
|
|
.help("API command (with optional arguments) to send to the server"),
|
|
)
|
|
.arg(
|
|
Arg::with_name("ignore-errors")
|
|
.long("ignore-errors")
|
|
.help("Don't exit on first error"),
|
|
)
|
|
.arg(
|
|
Arg::with_name("hybrid-vsock")
|
|
.long("hybrid-vsock")
|
|
.help("Treat a unix:// server address as a Hybrid VSOCK one"),
|
|
)
|
|
.arg(
|
|
Arg::with_name("hybrid-vsock-port")
|
|
.long("hybrid-vsock-port")
|
|
.help(&hybrid_vsock_port_help)
|
|
.default_value(DEFAULT_KATA_AGENT_API_VSOCK_PORT)
|
|
.takes_value(true)
|
|
.value_name("PORT")
|
|
)
|
|
.arg(
|
|
Arg::with_name("interactive")
|
|
.short("i")
|
|
.long("interactive")
|
|
.help("Allow interactive client"),
|
|
)
|
|
.arg(
|
|
Arg::with_name("server-address")
|
|
.long("server-address")
|
|
.help("server URI (vsock:// or unix://)")
|
|
.takes_value(true)
|
|
.value_name("URI"),
|
|
)
|
|
.arg(
|
|
Arg::with_name("timeout")
|
|
.long("timeout")
|
|
.help("timeout value as nanoseconds or using human-readable suffixes (0 [forever], 99ns, 30us, 2ms, 5s, 7m, etc)")
|
|
.takes_value(true)
|
|
.value_name("human-time"),
|
|
)
|
|
)
|
|
.subcommand(
|
|
SubCommand::with_name("generate-cid")
|
|
.about("Create a random container ID")
|
|
)
|
|
.subcommand(
|
|
SubCommand::with_name("generate-sid")
|
|
.about("Create a random sandbox ID")
|
|
)
|
|
.subcommand(
|
|
SubCommand::with_name("examples")
|
|
.about("Show usage examples")
|
|
);
|
|
|
|
let args = app.get_matches();
|
|
|
|
let subcmd = args
|
|
.subcommand_name()
|
|
.ok_or("need sub-command".to_string())
|
|
.map_err(|e| anyhow!(e))?;
|
|
|
|
match subcmd {
|
|
"generate-cid" => {
|
|
println!("{}", utils::random_container_id());
|
|
return Ok(());
|
|
}
|
|
"generate-sid" => {
|
|
println!("{}", utils::random_sandbox_id());
|
|
return Ok(());
|
|
}
|
|
"examples" => {
|
|
println!("{}", make_examples_text(name));
|
|
return Ok(());
|
|
}
|
|
"connect" => {
|
|
return connect(name, args);
|
|
}
|
|
_ => return Err(anyhow!(format!("invalid sub-command: {:?}", subcmd))),
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
match real_main() {
|
|
Err(e) => {
|
|
eprintln!("ERROR: {}", e);
|
|
exit(1);
|
|
}
|
|
_ => (),
|
|
};
|
|
}
|