Merge pull request #5388 from jodh-intel/kata-ctl

kata-ctl: Move development to main branch
This commit is contained in:
James O. D. Hunt 2022-10-12 14:29:35 +01:00 committed by GitHub
commit d3ee8d9f1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 725 additions and 2 deletions

View File

@ -16,9 +16,10 @@ COMPONENTS += runtime-rs
TOOLS =
TOOLS += agent-ctl
TOOLS += trace-forwarder
TOOLS += runk
TOOLS += kata-ctl
TOOLS += log-parser
TOOLS += runk
TOOLS += trace-forwarder
STANDARD_TARGETS = build check clean install test vendor

View File

@ -133,6 +133,7 @@ The table below lists the remaining parts of the project:
| [kernel](https://www.kernel.org) | kernel | Linux kernel used by the hypervisor to boot the guest image. Patches are stored [here](tools/packaging/kernel). |
| [osbuilder](tools/osbuilder) | infrastructure | Tool to create "mini O/S" rootfs and initrd images and kernel for the hypervisor. |
| [`agent-ctl`](src/tools/agent-ctl) | utility | Tool that provides low-level access for testing the agent. |
| [`kata-ctl`](src/tools/kata-ctl) | utility | Tool that provides advanced commands and debug facilities. |
| [`trace-forwarder`](src/tools/trace-forwarder) | utility | Agent tracing helper. |
| [`runk`](src/tools/runk) | utility | Standard OCI container runtime based on the agent. |
| [`ci`](https://github.com/kata-containers/ci) | CI | Continuous Integration configuration files and scripts. |

1
src/tools/kata-ctl/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/vendor/

View File

@ -0,0 +1,20 @@
# Copyright (c) 2022 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
#
[package]
name = "kata-ctl"
version = "0.0.1"
authors = ["The Kata Containers community <kata-dev@lists.katacontainers.io>"]
edition = "2018"
[dependencies]
anyhow = "1.0.31"
clap = { version = "3.2.20", features = ["derive", "cargo"] }
reqwest = { version = "0.11", default-features = false, features = ["json", "blocking", "rustls-tls"] }
serde_json = "1.0.85"
thiserror = "1.0.35"
[dev-dependencies]
semver = "1.0.12"

View File

@ -0,0 +1,64 @@
# Copyright (c) 2022 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
#
include ../../../utils.mk
PROJECT_NAME = Kata Containers
PROJECT_URL = https://github.com/kata-containers
PROJECT_COMPONENT = kata-ctl
TARGET = $(PROJECT_COMPONENT)
VERSION_FILE := ./VERSION
VERSION := $(shell grep -v ^\# $(VERSION_FILE))
COMMIT_NO := $(shell git rev-parse HEAD 2>/dev/null || true)
COMMIT_NO_SHORT := $(shell git rev-parse --short HEAD 2>/dev/null || true)
COMMIT := $(if $(shell git status --porcelain --untracked-files=no 2>/dev/null || true),${COMMIT_NO}-dirty,${COMMIT_NO})
# Exported to allow cargo to see it
export KATA_CTL_VERSION := $(if $(COMMIT),$(VERSION)-$(COMMIT),$(VERSION))
GENERATED_CODE = src/ops/version.rs
GENERATED_REPLACEMENTS= \
KATA_CTL_VERSION
GENERATED_FILES := $(GENERATED_CODE)
.DEFAULT_GOAL := default
default: $(TARGET) build
$(TARGET): $(GENERATED_CODE)
build:
@RUSTFLAGS="$(EXTRA_RUSTFLAGS) --deny warnings" cargo build --target $(TRIPLE) $(if $(findstring release,$(BUILD_TYPE)),--release) $(EXTRA_RUSTFEATURES)
$(GENERATED_FILES): %: %.in
@sed $(foreach r,$(GENERATED_REPLACEMENTS),-e 's|@$r@|$($r)|g') "$<" > "$@"
clean:
@cargo clean
@rm -f $(GENERATED_FILES)
vendor:
cargo vendor
test:
@RUSTFLAGS="$(EXTRA_RUSTFLAGS) --deny warnings" cargo test --target $(TRIPLE) $(if $(findstring release,$(BUILD_TYPE)),--release) $(EXTRA_RUSTFEATURES)
install:
@RUSTFLAGS="$(EXTRA_RUSTFLAGS) --deny warnings" cargo install --target $(TRIPLE) --path .
check: standard_rust_check
.PHONY: \
build \
check \
clean \
install \
test \
vendor

View File

@ -0,0 +1,49 @@
# Kata Containers control tool
## Overview
The `kata-ctl` tool is a rust rewrite of the
[`kata-runtime`](../../runtime/cmd/kata-runtime)
[utility program](../../../docs/design/architecture/README.md#utility-program).
The program provides a number of utility commands for:
- Using advanced Kata Containers features.
- Problem determination and debugging.
## Audience and environment
Users and administrators.
## Build the tool
```bash
$ make
```
## Install the tool
```bash
$ make install
```
## Run the tool
```bash
$ kata-ctl ...
```
For example, to determine if your system is capable of running Kata
Containers, run:
```bash
$ kata-ctl check all
```
### Full details
For a usage statement, run:
```bash
$ kata-ctl --help
```

1
src/tools/kata-ctl/VERSION Symbolic link
View File

@ -0,0 +1 @@
../../../VERSION

View File

@ -0,0 +1,15 @@
// Copyright (c) 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
#[cfg(target_arch = "aarch64")]
pub use arch_specific::*;
mod arch_specific {
use anyhow::Result;
pub fn check() -> Result<()> {
unimplemented!("Check not implemented in aarch64")
}
}

View File

@ -0,0 +1,42 @@
// Copyright (c) 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
use anyhow::Result;
#[cfg(target_arch = "aarch64")]
pub mod aarch64;
#[cfg(target_arch = "powerpc64le")]
pub mod powerpc64le;
#[cfg(target_arch = "s390x")]
pub mod s390x;
#[cfg(target_arch = "x86_64")]
pub mod x86_64;
pub fn check() -> Result<()> {
#[cfg(target_arch = "aarch64")]
let result = aarch64::check();
#[cfg(target_arch = "powerpc64le")]
let result = powerpc64le::check();
#[cfg(target_arch = "s390x")]
let result = s390x::check();
#[cfg(target_arch = "x86_64")]
let result = x86_64::check();
#[cfg(not(any(
target_arch = "aarch64",
target_arch = "powerpc64le",
target_arch = "s390x",
target_arch = "x86_64"
)))]
compile_error!("unknown architecture");
result
}

View File

@ -0,0 +1,15 @@
// Copyright (c) 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
#[cfg(target_arch = "powerpc64le")]
pub use arch_specific::*;
mod arch_specific {
use anyhow::Result;
pub fn check() -> Result<()> {
unimplemented!("Check not implemented in powerpc64le");
}
}

View File

@ -0,0 +1,15 @@
// Copyright (c) 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
#[cfg(target_arch = "s390x")]
pub use arch_specific::*;
mod arch_specific {
use anyhow::Result;
pub fn check() -> Result<()> {
unimplemented!("Check not implemented in s390x");
}
}

View File

@ -0,0 +1,55 @@
// Copyright (c) 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
#[cfg(target_arch = "x86_64")]
pub use arch_specific::*;
mod arch_specific {
use crate::check;
use anyhow::{anyhow, Result};
const PROC_CPUINFO: &str = "/proc/cpuinfo";
const CPUINFO_DELIMITER: &str = "\nprocessor";
const CPUINFO_FLAGS_TAG: &str = "flags";
const CPU_FLAGS_INTEL: &[&str] = &["lm", "sse4_1", "vmx"];
const CPU_ATTRIBS_INTEL: &[&str] = &["GenuineIntel"];
// check cpu
fn check_cpu() -> Result<()> {
println!("INFO: check CPU: x86_64");
let cpu_info = check::get_single_cpu_info(PROC_CPUINFO, CPUINFO_DELIMITER)?;
let cpu_flags = check::get_cpu_flags(&cpu_info, CPUINFO_FLAGS_TAG)
.map_err(|e| anyhow!("Error parsing CPU flags, file {:?}, {:?}", PROC_CPUINFO, e))?;
// perform checks
// TODO: Perform checks based on hypervisor type
// TODO: Add more information to output (see kata-check in go tool); adjust formatting
let missing_cpu_attributes = check::check_cpu_attribs(&cpu_info, CPU_ATTRIBS_INTEL)?;
if !missing_cpu_attributes.is_empty() {
eprintln!(
"WARNING: Missing CPU attributes {:?}",
missing_cpu_attributes
);
}
let missing_cpu_flags = check::check_cpu_flags(&cpu_flags, CPU_FLAGS_INTEL)?;
if !missing_cpu_flags.is_empty() {
eprintln!("WARNING: Missing CPU flags {:?}", missing_cpu_flags);
}
Ok(())
}
pub fn check() -> Result<()> {
println!("INFO: check: x86_64");
let _cpu_result = check_cpu();
// TODO: collect outcome of tests to determine if checks pass or not
Ok(())
}
}

View File

@ -0,0 +1,86 @@
// Copyright (c) 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
use clap::{Args, Parser, Subcommand};
use thiserror::Error;
#[derive(Parser, Debug)]
#[clap(name = "kata-ctl", author, about = "Kata Containers control tool")]
pub struct KataCtlCli {
#[clap(subcommand)]
pub command: Commands,
}
#[derive(Debug, Subcommand)]
pub enum Commands {
/// Tests if system can run Kata Containers
Check(CheckArgument),
/// Directly assign a volume to Kata Containers to manage
DirectVolume,
/// Display settings
Env,
/// Enter into guest by debug console
Exec,
/// Manage vm factory
Factory,
/// Manages iptables
Iptables(IptablesCommand),
/// Gather metrics associated with infrastructure used to run a sandbox
Metrics(MetricsCommand),
/// Display version details
Version,
}
#[derive(Debug, Args, Error)]
#[error("Argument is not valid")]
pub struct CheckArgument {
#[clap(subcommand)]
pub command: CheckSubCommand,
}
#[derive(Debug, Subcommand)]
pub enum CheckSubCommand {
/// Runs all checks
All,
/// Runs all checks but excluding network checks.
NoNetworkChecks,
/// Only compare the current and latest available versions
CheckVersionOnly,
}
#[derive(Debug, Args)]
pub struct MetricsCommand {
#[clap(subcommand)]
pub metrics_cmd: MetricsSubCommand,
}
#[derive(Debug, Subcommand)]
pub enum MetricsSubCommand {
/// Arguments for metrics
MetricsArgs,
}
// #[derive(Parser, Debug)]
#[derive(Debug, Args)]
pub struct IptablesCommand {
#[clap(subcommand)]
pub iptables: IpTablesArguments,
}
#[derive(Debug, Subcommand)]
pub enum IpTablesArguments {
/// Configure iptables
Metrics,
}

View File

@ -0,0 +1,200 @@
// Copyright (c) 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
// Contains checks that are not architecture-specific
use anyhow::{anyhow, Result};
use reqwest::header::{CONTENT_TYPE, USER_AGENT};
use serde_json::Value;
use std::collections::HashMap;
use std::fs;
const KATA_GITHUB_URL: &str =
"https://api.github.com/repos/kata-containers/kata-containers/releases/latest";
fn get_cpu_info(cpu_info_file: &str) -> Result<String> {
let contents = fs::read_to_string(cpu_info_file)?;
Ok(contents)
}
// get_single_cpu_info returns the contents of the first cpu from
// the specified cpuinfo file by parsing based on a specified delimiter
pub fn get_single_cpu_info(cpu_info_file: &str, substring: &str) -> Result<String> {
let contents = get_cpu_info(cpu_info_file)?;
if contents.is_empty() {
return Err(anyhow!("cpu_info string is empty"))?;
}
let subcontents: Vec<&str> = contents.split(substring).collect();
let result = subcontents
.first()
.ok_or("error splitting contents of cpuinfo")
.map_err(|e| anyhow!(e))?
.to_string();
Ok(result)
}
// get_cpu_flags returns a string of cpu flags from cpuinfo, passed in
// as a string
pub fn get_cpu_flags(cpu_info: &str, cpu_flags_tag: &str) -> Result<String> {
if cpu_info.is_empty() {
return Err(anyhow!("cpu_info string is empty"))?;
}
let subcontents: Vec<&str> = cpu_info.split('\n').collect();
for line in subcontents {
if line.starts_with(cpu_flags_tag) {
let line_data: Vec<&str> = line.split(':').collect();
let flags = line_data
.last()
.ok_or("error splitting flags in cpuinfo")
.map_err(|e| anyhow!(e))?
.to_string();
return Ok(flags);
}
}
Ok("".to_string())
}
// get_missing_strings searches for required (strings) in data and returns
// a vector containing the missing strings
fn get_missing_strings(data: &str, required: &'static [&'static str]) -> Result<Vec<String>> {
let mut missing: Vec<String> = Vec::new();
for item in required {
if !data.split_whitespace().any(|x| x == *item) {
missing.push(item.to_string());
}
}
Ok(missing)
}
pub fn check_cpu_flags(
retrieved_flags: &str,
required_flags: &'static [&'static str],
) -> Result<Vec<String>> {
let missing_flags = get_missing_strings(retrieved_flags, required_flags)?;
Ok(missing_flags)
}
pub fn check_cpu_attribs(
cpu_info: &str,
required_attribs: &'static [&'static str],
) -> Result<Vec<String>> {
let mut cpu_info_processed = cpu_info.replace('\t', "");
cpu_info_processed = cpu_info_processed.replace('\n', " ");
let missing_attribs = get_missing_strings(&cpu_info_processed, required_attribs)?;
Ok(missing_attribs)
}
pub fn run_network_checks() -> Result<()> {
Ok(())
}
fn get_kata_version_by_url(url: &str) -> std::result::Result<String, reqwest::Error> {
let content = reqwest::blocking::Client::new()
.get(url)
.header(CONTENT_TYPE, "application/json")
.header(USER_AGENT, "kata")
.send()?
.json::<HashMap<String, Value>>()?;
let version = content["tag_name"].as_str().unwrap();
Ok(version.to_string())
}
fn handle_reqwest_error(e: reqwest::Error) -> anyhow::Error {
if e.is_connect() {
return anyhow!(e).context("http connection failure: connection refused");
}
if e.is_timeout() {
return anyhow!(e).context("http connection failure: connection timeout");
}
if e.is_builder() {
return anyhow!(e).context("http connection failure: url malformed");
}
if e.is_decode() {
return anyhow!(e).context("http connection failure: unable to decode response body");
}
anyhow!(e).context("unknown http connection failure: {:?}")
}
pub fn check_version() -> Result<()> {
let version = get_kata_version_by_url(KATA_GITHUB_URL).map_err(handle_reqwest_error)?;
println!("Version: {}", version);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use semver::Version;
#[test]
fn test_get_cpu_info_empty_input() {
let expected = "No such file or directory (os error 2)";
let actual = get_cpu_info("").err().unwrap().to_string();
assert_eq!(expected, actual);
let actual = get_single_cpu_info("", "\nprocessor")
.err()
.unwrap()
.to_string();
assert_eq!(expected, actual);
}
#[test]
fn test_get_cpu_flags_empty_input() {
let expected = "cpu_info string is empty";
let actual = get_cpu_flags("", "").err().unwrap().to_string();
assert_eq!(expected, actual);
}
#[test]
fn check_version_by_empty_url() {
const TEST_URL: &str = "http:";
let expected = "builder error: empty host";
let actual = get_kata_version_by_url(TEST_URL).err().unwrap().to_string();
assert_eq!(expected, actual);
}
#[test]
fn check_version_by_garbage_url() {
const TEST_URL: &str = "_localhost_";
let expected = "builder error: relative URL without a base";
let actual = get_kata_version_by_url(TEST_URL).err().unwrap().to_string();
assert_eq!(expected, actual);
}
#[test]
fn check_version_by_invalid_url() {
const TEST_URL: &str = "http://localhost :80";
let expected = "builder error: invalid domain character";
let actual = get_kata_version_by_url(TEST_URL).err().unwrap().to_string();
assert_eq!(expected, actual);
}
#[test]
fn check_latest_version() {
let version = get_kata_version_by_url(KATA_GITHUB_URL).unwrap();
let v = Version::parse(&version).unwrap();
assert!(!v.major.to_string().is_empty());
assert!(!v.minor.to_string().is_empty());
assert!(!v.patch.to_string().is_empty());
}
}

View File

@ -0,0 +1,42 @@
// Copyright (c) 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
mod arch;
mod args;
mod check;
mod ops;
use anyhow::Result;
use clap::Parser;
use std::process::exit;
use args::{Commands, KataCtlCli};
use ops::check_ops::{
handle_check, handle_check_volume, handle_env, handle_exec, handle_factory, handle_iptables,
handle_metrics, handle_version,
};
fn real_main() -> Result<()> {
let args = KataCtlCli::parse();
match args.command {
Commands::Check(args) => handle_check(args),
Commands::DirectVolume => handle_check_volume(),
Commands::Env => handle_env(),
Commands::Exec => handle_exec(),
Commands::Factory => handle_factory(),
Commands::Iptables(args) => handle_iptables(args),
Commands::Metrics(args) => handle_metrics(args),
Commands::Version => handle_version(),
}
}
fn main() {
if let Err(e) = real_main() {
eprintln!("ERROR: {:#?}", e);
exit(1);
}
}

View File

@ -0,0 +1,7 @@
// Copyright (c) 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
pub mod check_ops;
pub mod version;

View File

@ -0,0 +1,70 @@
// Copyright (c) 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
use crate::arch;
use crate::check;
use crate::ops::version;
use crate::args::{CheckArgument, CheckSubCommand, IptablesCommand, MetricsCommand};
use anyhow::Result;
const NAME: &str = "kata-ctl";
pub fn handle_check(checkcmd: CheckArgument) -> Result<()> {
let command = checkcmd.command;
match command {
CheckSubCommand::All => {
// run architecture-specific tests
arch::check()?;
// run code that uses network checks
check::run_network_checks()?;
}
CheckSubCommand::NoNetworkChecks => {
// run architecture-specific tests
arch::check()?;
}
CheckSubCommand::CheckVersionOnly => {
// retrieve latest release
check::check_version()?;
}
}
Ok(())
}
pub fn handle_check_volume() -> Result<()> {
Ok(())
}
pub fn handle_env() -> Result<()> {
Ok(())
}
pub fn handle_exec() -> Result<()> {
Ok(())
}
pub fn handle_factory() -> Result<()> {
Ok(())
}
pub fn handle_iptables(_args: IptablesCommand) -> Result<()> {
Ok(())
}
pub fn handle_metrics(_args: MetricsCommand) -> Result<()> {
Ok(())
}
pub fn handle_version() -> Result<()> {
let version = version::get().unwrap();
println!("{} version {:?} (type: rust)", NAME, version);
Ok(())
}

View File

@ -0,0 +1,39 @@
// Copyright (c) 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
//
// WARNING: This file is auto-generated - DO NOT EDIT!
//
use clap::crate_version;
const KATA_CTL_VERSION: &str = "@KATA_CTL_VERSION@";
pub fn get() -> Result<String, String> {
if KATA_CTL_VERSION.trim().is_empty() {
Err("Unable to retrieve kata Version. Check that Kata is properly installed".to_string())
} else {
let version = format!("{}-{}", KATA_CTL_VERSION, crate_version!());
Ok(version)
}
}
#[cfg(test)]
mod tests {
use super::*;
use semver::Version;
#[test]
fn test_get() {
let version = get().unwrap();
let v = Version::parse(&version).unwrap();
assert!(!v.major.to_string().is_empty());
assert!(!v.minor.to_string().is_empty());
assert!(!v.patch.to_string().is_empty());
assert!(!v.pre.to_string().is_empty());
}
}