Compare commits
103 Commits
shim-proto
...
main
Author | SHA1 | Date |
---|---|---|
|
e4551b1013 | |
|
0e4ff0c5da | |
|
afab3c8eba | |
|
11e97809b8 | |
|
15fbabcf8e | |
|
6aa801807e | |
|
220d6d6a65 | |
|
b8107d6101 | |
|
c0b92c4e96 | |
|
8469d6d7a8 | |
|
ad6d647960 | |
|
055e49bf6f | |
|
0a07bdde72 | |
|
468fbc0b4c | |
|
cd5a84d8a7 | |
|
cea5523d20 | |
|
9b5727a28e | |
|
4744fafcd6 | |
|
3b42040af2 | |
|
8130e41939 | |
|
2a098d9f69 | |
|
59589df20f | |
|
18fcf6bc52 | |
|
277a1a65f2 | |
|
df1b8f05dd | |
|
35980682a6 | |
|
f4fdddc5e5 | |
|
8fba47295b | |
|
906bd0f466 | |
|
559cc576b9 | |
|
9d9cc05d18 | |
|
822a065062 | |
|
cd926a2d89 | |
|
ae876e3a33 | |
|
c016d933ed | |
|
2d999680fe | |
|
ef81c80577 | |
|
e5db241747 | |
|
26b783c0b4 | |
|
545b0e789e | |
|
1f1e38dc06 | |
|
2d47d88f13 | |
|
2cb7714e76 | |
|
a345bac309 | |
|
41d2ded16a | |
|
95964f295e | |
|
ce05587af2 | |
|
30be44066a | |
|
68bff89898 | |
|
92e4b8b4fb | |
|
c3620b7602 | |
|
4c66dd46f5 | |
|
bd5dd4fcbd | |
|
feac9615e1 | |
|
314e384806 | |
|
346b82e673 | |
|
e27decc5ba | |
|
6ad0a6e0d3 | |
|
4133fef4c0 | |
|
a0f3bf13a1 | |
|
6526c28e87 | |
|
eef8806871 | |
|
1e17e1495e | |
|
7051e312f5 | |
|
8029a61a2b | |
|
c6b623cb8b | |
|
3044a44363 | |
|
0db44b965c | |
|
25f06b4c7f | |
|
f87972b9b0 | |
|
3bb4e14fe5 | |
|
474db59c81 | |
|
b7de553f9e | |
|
7efe8d8390 | |
|
3da6d80b0b | |
|
9cfc6454f0 | |
|
6d496ef7fa | |
|
ac89daa9b1 | |
|
857ace0e3f | |
|
a665cfcfc0 | |
|
b535a030d1 | |
|
d584728948 | |
|
2689bbf250 | |
|
7c01662306 | |
|
6cb625da04 | |
|
792c2e619e | |
|
c22dba6201 | |
|
a6b4286542 | |
|
7794b07f16 | |
|
304d67069b | |
|
3f541b9454 | |
|
da4d1f48bf | |
|
ece6516da5 | |
|
6f2812f4b8 | |
|
fb4cb85d66 | |
|
2265428508 | |
|
81f51386f0 | |
|
ece15e9275 | |
|
2050c857d6 | |
|
4b8bd82ea9 | |
|
4e60e8a6fa | |
|
15f51b0899 | |
|
202c756d26 |
|
@ -22,22 +22,30 @@ jobs:
|
|||
- run: ./scripts/install-protobuf.sh
|
||||
shell: bash
|
||||
|
||||
- run: rustup toolchain install nightly --component rustfmt
|
||||
- run: cargo +nightly fmt --all -- --check
|
||||
|
||||
# the "runc" and "containerd-shim" crates have `sync` code that is not covered by the workspace
|
||||
- run: cargo check -p runc --all-targets
|
||||
- run: cargo clippy -p runc --all-targets -- -D warnings
|
||||
- run: cargo check -p containerd-shim --all-targets
|
||||
- run: cargo clippy -p containerd-shim --all-targets -- -D warnings
|
||||
|
||||
# check the workspace
|
||||
- run: cargo check --examples --tests --all-targets
|
||||
- run: cargo check --examples --tests --all-targets --all-features
|
||||
|
||||
- run: rustup toolchain install nightly --component rustfmt
|
||||
- run: cargo +nightly fmt --all -- --check --files-with-diff
|
||||
|
||||
- run: cargo clippy --all-targets -- -D warnings
|
||||
- run: cargo clippy --all-targets --all-features -- -D warnings
|
||||
|
||||
# the shim has sync code that is not covered when running with --all-features
|
||||
- run: cargo clippy -p containerd-shim --all-targets -- -D warnings
|
||||
|
||||
- run: cargo doc --no-deps --features docs
|
||||
env:
|
||||
RUSTDOCFLAGS: -Dwarnings
|
||||
|
||||
- name: check unused dependencies
|
||||
uses: bnjbvr/cargo-machete@v0.8.0
|
||||
env:
|
||||
RUSTUP_TOOLCHAIN: "stable"
|
||||
|
||||
# TODO: Merge this with the checks job above
|
||||
windows-checks:
|
||||
name: Windows Checks
|
||||
|
@ -51,7 +59,7 @@ jobs:
|
|||
- run: cargo check --examples --tests -p containerd-shim -p containerd-shim-protos -p containerd-client
|
||||
|
||||
- run: rustup toolchain install nightly --component rustfmt
|
||||
- run: cargo +nightly fmt -p containerd-shim -p containerd-shim-protos -p containerd-client -- --check --files-with-diff
|
||||
- run: cargo +nightly fmt -p containerd-shim -p containerd-shim-protos -p containerd-client -- --check
|
||||
|
||||
- run: cargo clippy -p containerd-shim -p containerd-shim-protos -- -D warnings
|
||||
- run: cargo doc --no-deps -p containerd-shim -p containerd-shim-protos -p containerd-client
|
||||
|
@ -111,7 +119,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: EmbarkStudios/cargo-deny-action@v1
|
||||
- uses: EmbarkStudios/cargo-deny-action@v2
|
||||
|
||||
linux-integration:
|
||||
name: Linux Integration
|
||||
|
@ -120,8 +128,8 @@ jobs:
|
|||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04, ubuntu-22.04]
|
||||
containerd: [v1.6.28, v1.7.13, v2.0.0-beta.2]
|
||||
os: [ubuntu-latest]
|
||||
containerd: [v1.6.38, v1.7.27, v2.1.1]
|
||||
|
||||
steps:
|
||||
- name: Checkout extensions
|
||||
|
@ -158,7 +166,7 @@ jobs:
|
|||
## get latest go version for integrations tests so we can skip runnings tests
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.20.12'
|
||||
go-version: '1.23'
|
||||
|
||||
- name: Integration
|
||||
env:
|
||||
|
@ -222,7 +230,7 @@ jobs:
|
|||
# run the example
|
||||
cargo run -p containerd-shim --example skeleton -- -namespace default -id 1234 -address "\\.\pipe\containerd-containerd" -publish-binary ./bin/containerd start
|
||||
ps skeleton
|
||||
cargo run -p containerd-shim-protos --example shim-proto-connect \\.\pipe\containerd-shim-17630016127144989388-pipe
|
||||
cargo run -p containerd-shim-protos --example shim-proto-connect \\.\pipe\containerd-shim-bc764c65e177434fcefe8257dc440be8b8acf7c96156320d965938f7e9ae1a35-pipe
|
||||
$skeleton = get-process skeleton -ErrorAction SilentlyContinue
|
||||
if ($skeleton) { exit 1 }
|
||||
- name: Run client
|
||||
|
|
|
@ -22,7 +22,7 @@ jobs:
|
|||
|
||||
- name: Install grcov
|
||||
run: |
|
||||
cargo install --locked grcov
|
||||
cargo install --locked grcov@0.8.24
|
||||
grcov --version
|
||||
|
||||
- name: Tests
|
||||
|
@ -47,7 +47,7 @@ jobs:
|
|||
--output-path ./target/coverage/
|
||||
|
||||
- name: Upload coverage data
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
flags: unittests
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
name: 'Close stale issues and PRs'
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *" # Every day at midnight
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/stale.yml'
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
|
||||
# All stale bot options: https://github.com/actions/stale#all-options
|
||||
with:
|
||||
# Idle number of days before marking issues/PRs stale
|
||||
days-before-stale: 90
|
||||
# Idle number of days before closing stale issues/PRs
|
||||
days-before-close: 7
|
||||
# Comment on the staled issues
|
||||
stale-issue-message: 'This issue is stale because it has been open 90 days with no activity. This issue will be closed in 7 days unless new comments are made or the stale label is removed.'
|
||||
# Comment on the staled PRs
|
||||
stale-pr-message: 'This PR is stale because it has been open 90 days with no activity. This PR will be closed in 7 days unless new comments are made or the stale label is removed.'
|
||||
# Comment on the staled issues while closed
|
||||
close-issue-message: 'This issue was closed because it has been stalled for 7 days with no activity.'
|
||||
# Comment on the staled PRs while closed
|
||||
close-pr-message: 'This PR was closed because it has been stalled for 7 days with no activity.'
|
||||
# Enable dry-run when changing this file from a PR.
|
||||
debug-only: github.event_name == 'pull_request'
|
16
Cargo.toml
16
Cargo.toml
|
@ -32,20 +32,20 @@ futures = "0.3.19"
|
|||
libc = "0.2.112"
|
||||
log = {version = "0.4.2", features=["kv_unstable"]}
|
||||
nix = "0.29"
|
||||
oci-spec = "0.6"
|
||||
oci-spec = "0.7"
|
||||
os_pipe = "1.1"
|
||||
prctl = "1.0.0"
|
||||
prost = "0.12"
|
||||
prost-build = "0.12"
|
||||
prost-types = "0.12"
|
||||
prost = "0.13"
|
||||
prost-build = "0.13"
|
||||
prost-types = "0.13"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
simple_logger = { version = "5.0", default-features = false }
|
||||
tempfile = "3.6"
|
||||
thiserror = "1.0"
|
||||
thiserror = "2.0"
|
||||
time = { version = "0.3.29", features = ["serde", "std", "formatting"] }
|
||||
tokio = "1.26"
|
||||
tonic = "0.11"
|
||||
tonic-build = "0.11"
|
||||
tower = "0.4"
|
||||
tonic = "0.13"
|
||||
tonic-build = "0.13"
|
||||
tower = "0.5"
|
||||
uuid = { version = "1.0", features = ["v4"] }
|
||||
|
|
|
@ -8,3 +8,5 @@
|
|||
# GitHub ID, Name, Email address
|
||||
"Burning1020","Zhang Tianyang","burning9699@gmail.com"
|
||||
"jsturtevant","James Sturtevant","jstur@microsoft.com"
|
||||
"mossaka","Jiaxiao Zhou","jiazho@microsoft.com"
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "containerd-client"
|
||||
version = "0.5.0"
|
||||
version = "0.8.0"
|
||||
authors = [
|
||||
"Maksym Pavlenko <pavlenko.maksym@gmail.com>",
|
||||
"The containerd Authors",
|
||||
|
@ -23,6 +23,7 @@ name = "version"
|
|||
path = "examples/version.rs"
|
||||
|
||||
[dependencies]
|
||||
hyper-util = "0.1.6" # https://github.com/hyperium/hyper/issues/3110
|
||||
prost.workspace = true
|
||||
prost-types.workspace = true
|
||||
tokio = { workspace = true, optional = true }
|
||||
|
@ -48,3 +49,6 @@ default = ["connect"]
|
|||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["docs"]
|
||||
|
||||
[package.metadata.cargo-machete]
|
||||
ignored = ["prost"]
|
||||
|
|
|
@ -62,15 +62,17 @@ const FIXUP_MODULES: &[&str] = &[
|
|||
"containerd.services.tasks.v1",
|
||||
"containerd.services.containers.v1",
|
||||
"containerd.services.content.v1",
|
||||
"containerd.services.events.v1",
|
||||
];
|
||||
|
||||
fn main() {
|
||||
let mut config = prost_build::Config::new();
|
||||
config.protoc_arg("--experimental_allow_proto3_optional");
|
||||
config.enable_type_names();
|
||||
|
||||
tonic_build::configure()
|
||||
.build_server(false)
|
||||
.compile_with_config(config, PROTO_FILES, &["vendor/"])
|
||||
.compile_protos_with_config(config, PROTO_FILES, &["vendor/"])
|
||||
.expect("Failed to generate GRPC bindings");
|
||||
|
||||
for module in FIXUP_MODULES {
|
||||
|
@ -95,6 +97,7 @@ fn fixup_imports(path: &str) -> Result<(), io::Error> {
|
|||
|
||||
let contents = fs::read_to_string(&path)?
|
||||
.replace("super::super::super::v1::types", "crate::types::v1") // for tasks service
|
||||
.replace("super::super::super::super::types", "crate::types")
|
||||
.replace("super::super::super::types", "crate::types")
|
||||
.replace("super::super::super::super::google", "crate::google")
|
||||
.replace(
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
use std::env::consts;
|
||||
|
||||
use client::{
|
||||
services::v1::{transfer_client::TransferClient, TransferOptions, TransferRequest},
|
||||
to_any,
|
||||
types::{
|
||||
transfer::{ImageStore, OciRegistry, UnpackConfiguration},
|
||||
Platform,
|
||||
},
|
||||
with_namespace,
|
||||
};
|
||||
use containerd_client as client;
|
||||
use tonic::Request;
|
||||
|
||||
const IMAGE: &str = "docker.io/library/alpine:latest";
|
||||
const NAMESPACE: &str = "default";
|
||||
|
||||
/// Make sure you run containerd before running this example.
|
||||
/// NOTE: to run this example, you must prepare a rootfs.
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() {
|
||||
let arch = match consts::ARCH {
|
||||
"x86_64" => "amd64",
|
||||
"aarch64" => "arm64",
|
||||
_ => consts::ARCH,
|
||||
};
|
||||
|
||||
let channel = client::connect("/run/containerd/containerd.sock")
|
||||
.await
|
||||
.expect("Connect Failed");
|
||||
let mut client = TransferClient::new(channel.clone());
|
||||
|
||||
// Create the source (OCIRegistry)
|
||||
let source = OciRegistry {
|
||||
reference: IMAGE.to_string(),
|
||||
resolver: Default::default(),
|
||||
};
|
||||
|
||||
let platform = Platform {
|
||||
os: "linux".to_string(),
|
||||
architecture: arch.to_string(),
|
||||
variant: "".to_string(),
|
||||
os_version: "".to_string(),
|
||||
};
|
||||
|
||||
// Create the destination (ImageStore)
|
||||
let destination = ImageStore {
|
||||
name: IMAGE.to_string(),
|
||||
platforms: vec![platform.clone()],
|
||||
unpacks: vec![UnpackConfiguration {
|
||||
platform: Some(platform),
|
||||
..Default::default()
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let anys = to_any(&source);
|
||||
let anyd = to_any(&destination);
|
||||
|
||||
println!("Pulling image for linux/{} from source: {:?}", arch, source);
|
||||
|
||||
// Create the transfer request
|
||||
let request = TransferRequest {
|
||||
source: Some(anys),
|
||||
destination: Some(anyd),
|
||||
options: Some(TransferOptions {
|
||||
..Default::default()
|
||||
}),
|
||||
};
|
||||
// Execute the transfer (pull)
|
||||
client
|
||||
.transfer(with_namespace!(request, NAMESPACE))
|
||||
.await
|
||||
.expect("unable to transfer image");
|
||||
}
|
|
@ -28,6 +28,9 @@ pub mod types {
|
|||
pub mod v1 {
|
||||
tonic::include_proto!("containerd.v1.types");
|
||||
}
|
||||
pub mod transfer {
|
||||
tonic::include_proto!("containerd.types.transfer");
|
||||
}
|
||||
}
|
||||
|
||||
/// Generated `google.rpc` types, containerd services typically use some of these types.
|
||||
|
@ -86,23 +89,29 @@ pub async fn connect(
|
|||
|
||||
let path = path.as_ref().to_path_buf();
|
||||
|
||||
// Taken from https://github.com/hyperium/tonic/blob/eeb3268f71ae5d1107c937392389db63d8f721fb/examples/src/uds/client.rs#L19
|
||||
// Taken from https://github.com/hyperium/tonic/blob/71fca362d7ffbb230547f23b3f2fb75c414063a8/examples/src/uds/client.rs#L21-L28
|
||||
// There will ignore this uri because uds do not use it
|
||||
// and make connection with UnixStream::connect.
|
||||
let channel = Endpoint::try_from("http://[::]")
|
||||
.unwrap()
|
||||
let channel = Endpoint::try_from("http://[::]")?
|
||||
.connect_with_connector(tower::service_fn(move |_| {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
tokio::net::UnixStream::connect(path.clone())
|
||||
}
|
||||
let path = path.clone();
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let client = tokio::net::windows::named_pipe::ClientOptions::new()
|
||||
.open(path.clone())
|
||||
.map_err(|e| std::io::Error::from(e));
|
||||
async move { client }
|
||||
async move {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
Ok::<_, std::io::Error>(hyper_util::rt::TokioIo::new(
|
||||
tokio::net::UnixStream::connect(path).await?,
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let client = tokio::net::windows::named_pipe::ClientOptions::new()
|
||||
.open(&path)
|
||||
.map_err(|e| std::io::Error::from(e))?;
|
||||
|
||||
Ok::<_, std::io::Error>(hyper_util::rt::TokioIo::new(client))
|
||||
}
|
||||
}
|
||||
}))
|
||||
.await?;
|
||||
|
@ -110,12 +119,25 @@ pub async fn connect(
|
|||
Ok(channel)
|
||||
}
|
||||
|
||||
use prost::{Message, Name};
|
||||
use prost_types::Any;
|
||||
|
||||
// to_any provides a helper to match the current use of the protobuf "fullname" trait
|
||||
// in the Go code on the gRPC server side in containerd when handling matching of Any
|
||||
// types to registered types on the server. Further discussion on future direction
|
||||
// of typeurl in this issue: https://github.com/containerd/rust-extensions/issues/362
|
||||
pub fn to_any<T: Message + Name>(m: &T) -> Any {
|
||||
let mut anyt = Any::from_msg(m).unwrap();
|
||||
anyt.type_url = T::full_name();
|
||||
anyt
|
||||
}
|
||||
|
||||
/// Help to inject namespace into request.
|
||||
///
|
||||
/// To use this macro, the `tonic::Request` is needed.
|
||||
#[macro_export]
|
||||
macro_rules! with_namespace {
|
||||
($req : ident, $ns: expr) => {{
|
||||
($req:expr, $ns:expr) => {{
|
||||
let mut req = Request::new($req);
|
||||
let md = req.metadata_mut();
|
||||
// https://github.com/containerd/containerd/blob/main/pkg/namespaces/grpc.go#L27
|
||||
|
|
4
crates/client/vendor/github.com/containerd/containerd/api/events/container.proto
generated
vendored
4
crates/client/vendor/github.com/containerd/containerd/api/events/container.proto
generated
vendored
|
@ -19,10 +19,10 @@ syntax = "proto3";
|
|||
package containerd.events;
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
import "github.com/containerd/containerd/protobuf/plugin/fieldpath.proto";
|
||||
import "github.com/containerd/containerd/api/types/fieldpath.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/events;events";
|
||||
option (containerd.plugin.fieldpath_all) = true;
|
||||
option (containerd.types.fieldpath_all) = true;
|
||||
|
||||
message ContainerCreate {
|
||||
string id = 1;
|
||||
|
|
|
@ -18,10 +18,15 @@ syntax = "proto3";
|
|||
|
||||
package containerd.events;
|
||||
|
||||
import "github.com/containerd/containerd/protobuf/plugin/fieldpath.proto";
|
||||
import "github.com/containerd/containerd/api/types/fieldpath.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/events;events";
|
||||
option (containerd.plugin.fieldpath_all) = true;
|
||||
option (containerd.types.fieldpath_all) = true;
|
||||
|
||||
message ContentCreate {
|
||||
string digest = 1;
|
||||
int64 size = 2;
|
||||
}
|
||||
|
||||
message ContentDelete {
|
||||
string digest = 1;
|
||||
|
|
|
@ -18,10 +18,10 @@ syntax = "proto3";
|
|||
|
||||
package containerd.services.images.v1;
|
||||
|
||||
import "github.com/containerd/containerd/protobuf/plugin/fieldpath.proto";
|
||||
import "github.com/containerd/containerd/api/types/fieldpath.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/events;events";
|
||||
option (containerd.plugin.fieldpath_all) = true;
|
||||
option (containerd.types.fieldpath_all) = true;
|
||||
|
||||
message ImageCreate {
|
||||
string name = 1;
|
||||
|
|
4
crates/client/vendor/github.com/containerd/containerd/api/events/namespace.proto
generated
vendored
4
crates/client/vendor/github.com/containerd/containerd/api/events/namespace.proto
generated
vendored
|
@ -18,10 +18,10 @@ syntax = "proto3";
|
|||
|
||||
package containerd.events;
|
||||
|
||||
import "github.com/containerd/containerd/protobuf/plugin/fieldpath.proto";
|
||||
import "github.com/containerd/containerd/api/types/fieldpath.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/events;events";
|
||||
option (containerd.plugin.fieldpath_all) = true;
|
||||
option (containerd.types.fieldpath_all) = true;
|
||||
|
||||
message NamespaceCreate {
|
||||
string name = 1;
|
||||
|
|
4
crates/client/vendor/github.com/containerd/containerd/api/events/snapshot.proto
generated
vendored
4
crates/client/vendor/github.com/containerd/containerd/api/events/snapshot.proto
generated
vendored
|
@ -18,10 +18,10 @@ syntax = "proto3";
|
|||
|
||||
package containerd.events;
|
||||
|
||||
import "github.com/containerd/containerd/protobuf/plugin/fieldpath.proto";
|
||||
import "github.com/containerd/containerd/api/types/fieldpath.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/events;events";
|
||||
option (containerd.plugin.fieldpath_all) = true;
|
||||
option (containerd.types.fieldpath_all) = true;
|
||||
|
||||
message SnapshotPrepare {
|
||||
string key = 1;
|
||||
|
|
|
@ -20,10 +20,10 @@ package containerd.events;
|
|||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "github.com/containerd/containerd/api/types/mount.proto";
|
||||
import "github.com/containerd/containerd/protobuf/plugin/fieldpath.proto";
|
||||
import "github.com/containerd/containerd/api/types/fieldpath.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/events;events";
|
||||
option (containerd.plugin.fieldpath_all) = true;
|
||||
option (containerd.types.fieldpath_all) = true;
|
||||
|
||||
message TaskCreate {
|
||||
string container_id = 1;
|
||||
|
|
|
@ -44,6 +44,8 @@ message ApplyRequest {
|
|||
repeated containerd.types.Mount mounts = 2;
|
||||
|
||||
map<string, google.protobuf.Any> payloads = 3;
|
||||
// SyncFs is to synchronize the underlying filesystem containing files.
|
||||
bool sync_fs = 4;
|
||||
}
|
||||
|
||||
message ApplyResponse {
|
||||
|
@ -74,8 +76,11 @@ message DiffRequest {
|
|||
// on content store commit.
|
||||
map<string, string> labels = 5;
|
||||
|
||||
// SourceDateEpoch specifies the timestamp used for whiteouts to provide control for reproducibility.
|
||||
// SourceDateEpoch specifies the timestamp used to provide control for reproducibility.
|
||||
// See also https://reproducible-builds.org/docs/source-date-epoch/ .
|
||||
//
|
||||
// Since containerd v2.0, the whiteout timestamps are set to zero (1970-01-01),
|
||||
// not to the source date epoch.
|
||||
google.protobuf.Timestamp source_date_epoch = 6;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,10 +18,9 @@ syntax = "proto3";
|
|||
|
||||
package containerd.services.events.v1;
|
||||
|
||||
import "github.com/containerd/containerd/protobuf/plugin/fieldpath.proto";
|
||||
import "github.com/containerd/containerd/api/types/event.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/services/events/v1;events";
|
||||
|
||||
|
@ -46,7 +45,7 @@ service Events {
|
|||
// from all namespaces unless otherwise specified. If this is not desired,
|
||||
// a filter can be provided in the format 'namespace==<namespace>' to
|
||||
// restrict the received events.
|
||||
rpc Subscribe(SubscribeRequest) returns (stream Envelope);
|
||||
rpc Subscribe(SubscribeRequest) returns (stream containerd.types.Envelope);
|
||||
}
|
||||
|
||||
message PublishRequest {
|
||||
|
@ -55,17 +54,9 @@ message PublishRequest {
|
|||
}
|
||||
|
||||
message ForwardRequest {
|
||||
Envelope envelope = 1;
|
||||
containerd.types.Envelope envelope = 1;
|
||||
}
|
||||
|
||||
message SubscribeRequest {
|
||||
repeated string filters = 1;
|
||||
}
|
||||
|
||||
message Envelope {
|
||||
option (containerd.plugin.fieldpath) = true;
|
||||
google.protobuf.Timestamp timestamp = 1;
|
||||
string namespace = 2;
|
||||
string topic = 3;
|
||||
google.protobuf.Any event = 4;
|
||||
}
|
||||
|
|
|
@ -140,4 +140,10 @@ message DeleteImageRequest {
|
|||
//
|
||||
// Default is false
|
||||
bool sync = 2;
|
||||
|
||||
// Target value for image to be deleted
|
||||
//
|
||||
// If image descriptor does not match the same digest,
|
||||
// the delete operation will return "not found" error.
|
||||
optional containerd.types.Descriptor target = 3;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ syntax = "proto3";
|
|||
|
||||
package containerd.services.introspection.v1;
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
import "github.com/containerd/containerd/api/types/introspection.proto";
|
||||
import "github.com/containerd/containerd/api/types/platform.proto";
|
||||
import "google/rpc/status.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
|
@ -33,6 +35,8 @@ service Introspection {
|
|||
rpc Plugins(PluginsRequest) returns (PluginsResponse);
|
||||
// Server returns information about the containerd server
|
||||
rpc Server(google.protobuf.Empty) returns (ServerResponse);
|
||||
// PluginInfo returns information directly from a plugin if the plugin supports it
|
||||
rpc PluginInfo(PluginInfoRequest) returns (PluginInfoResponse);
|
||||
}
|
||||
|
||||
message Plugin {
|
||||
|
@ -111,3 +115,19 @@ message DeprecationWarning {
|
|||
string message = 2;
|
||||
google.protobuf.Timestamp last_occurrence = 3;
|
||||
}
|
||||
|
||||
message PluginInfoRequest {
|
||||
string type = 1;
|
||||
string id = 2;
|
||||
|
||||
// Options may be used to request extra dynamic information from
|
||||
// a plugin.
|
||||
// This object is determined by the plugin and the plugin may return
|
||||
// NotImplemented or InvalidArgument if it is not supported
|
||||
google.protobuf.Any options = 3;
|
||||
}
|
||||
|
||||
message PluginInfoResponse {
|
||||
Plugin plugin = 1;
|
||||
google.protobuf.Any extra = 2;
|
||||
}
|
||||
|
|
41
crates/client/vendor/github.com/containerd/containerd/api/services/sandbox/v1/sandbox.proto
generated
vendored
Normal file → Executable file
41
crates/client/vendor/github.com/containerd/containerd/api/services/sandbox/v1/sandbox.proto
generated
vendored
Normal file → Executable file
|
@ -31,6 +31,7 @@ import "google/protobuf/timestamp.proto";
|
|||
import "github.com/containerd/containerd/api/types/sandbox.proto";
|
||||
import "github.com/containerd/containerd/api/types/mount.proto";
|
||||
import "github.com/containerd/containerd/api/types/platform.proto";
|
||||
import "github.com/containerd/containerd/api/types/metrics.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/services/sandbox/v1;sandbox";
|
||||
|
||||
|
@ -93,6 +94,8 @@ service Controller {
|
|||
rpc Wait(ControllerWaitRequest) returns (ControllerWaitResponse);
|
||||
rpc Status(ControllerStatusRequest) returns (ControllerStatusResponse);
|
||||
rpc Shutdown(ControllerShutdownRequest) returns (ControllerShutdownResponse);
|
||||
rpc Metrics(ControllerMetricsRequest) returns (ControllerMetricsResponse);
|
||||
rpc Update(ControllerUpdateRequest) returns (ControllerUpdateResponse);
|
||||
}
|
||||
|
||||
message ControllerCreateRequest {
|
||||
|
@ -100,6 +103,9 @@ message ControllerCreateRequest {
|
|||
repeated containerd.types.Mount rootfs = 2;
|
||||
google.protobuf.Any options = 3;
|
||||
string netns_path = 4;
|
||||
map<string, string> annotations = 5;
|
||||
containerd.types.Sandbox sandbox = 6;
|
||||
string sandboxer = 10;
|
||||
}
|
||||
|
||||
message ControllerCreateResponse {
|
||||
|
@ -108,6 +114,7 @@ message ControllerCreateResponse {
|
|||
|
||||
message ControllerStartRequest {
|
||||
string sandbox_id = 1;
|
||||
string sandboxer = 10;
|
||||
}
|
||||
|
||||
message ControllerStartResponse {
|
||||
|
@ -115,10 +122,16 @@ message ControllerStartResponse {
|
|||
uint32 pid = 2;
|
||||
google.protobuf.Timestamp created_at = 3;
|
||||
map<string, string> labels = 4;
|
||||
// Address of the sandbox for containerd to connect,
|
||||
// for calling Task or other APIs serving in the sandbox.
|
||||
// it is in the form of ttrpc+unix://path/to/uds or grpc+vsock://<vsock cid>:<port>.
|
||||
string address = 5;
|
||||
uint32 version = 6;
|
||||
}
|
||||
|
||||
message ControllerPlatformRequest {
|
||||
string sandbox_id = 1;
|
||||
string sandboxer = 10;
|
||||
}
|
||||
|
||||
message ControllerPlatformResponse {
|
||||
|
@ -128,12 +141,14 @@ message ControllerPlatformResponse {
|
|||
message ControllerStopRequest {
|
||||
string sandbox_id = 1;
|
||||
uint32 timeout_secs = 2;
|
||||
string sandboxer = 10;
|
||||
}
|
||||
|
||||
message ControllerStopResponse {}
|
||||
|
||||
message ControllerWaitRequest {
|
||||
string sandbox_id = 1;
|
||||
string sandboxer = 10;
|
||||
}
|
||||
|
||||
message ControllerWaitResponse {
|
||||
|
@ -144,6 +159,7 @@ message ControllerWaitResponse {
|
|||
message ControllerStatusRequest {
|
||||
string sandbox_id = 1;
|
||||
bool verbose = 2;
|
||||
string sandboxer = 10;
|
||||
}
|
||||
|
||||
message ControllerStatusResponse {
|
||||
|
@ -154,10 +170,35 @@ message ControllerStatusResponse {
|
|||
google.protobuf.Timestamp created_at = 5;
|
||||
google.protobuf.Timestamp exited_at = 6;
|
||||
google.protobuf.Any extra = 7;
|
||||
// Address of the sandbox for containerd to connect,
|
||||
// for calling Task or other APIs serving in the sandbox.
|
||||
// it is in the form of ttrpc+unix://path/to/uds or grpc+vsock://<vsock cid>:<port>.
|
||||
string address = 8;
|
||||
uint32 version = 9;
|
||||
}
|
||||
|
||||
message ControllerShutdownRequest {
|
||||
string sandbox_id = 1;
|
||||
string sandboxer = 10;
|
||||
}
|
||||
|
||||
message ControllerShutdownResponse {}
|
||||
|
||||
message ControllerMetricsRequest {
|
||||
string sandbox_id = 1;
|
||||
string sandboxer = 10;
|
||||
}
|
||||
|
||||
message ControllerMetricsResponse {
|
||||
types.Metric metrics = 1;
|
||||
}
|
||||
|
||||
message ControllerUpdateRequest {
|
||||
string sandbox_id = 1;
|
||||
string sandboxer = 2;
|
||||
containerd.types.Sandbox sandbox = 3;
|
||||
repeated string fields = 4;
|
||||
}
|
||||
|
||||
message ControllerUpdateResponse {
|
||||
}
|
||||
|
|
|
@ -18,10 +18,8 @@ syntax = "proto3";
|
|||
|
||||
package containerd.services.events.ttrpc.v1;
|
||||
|
||||
import "github.com/containerd/containerd/protobuf/plugin/fieldpath.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
import "github.com/containerd/containerd/api/types/event.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/services/ttrpc/events/v1;events";
|
||||
|
||||
|
@ -35,13 +33,5 @@ service Events {
|
|||
}
|
||||
|
||||
message ForwardRequest {
|
||||
Envelope envelope = 1;
|
||||
}
|
||||
|
||||
message Envelope {
|
||||
option (containerd.plugin.fieldpath) = true;
|
||||
google.protobuf.Timestamp timestamp = 1;
|
||||
string namespace = 2;
|
||||
string topic = 3;
|
||||
google.protobuf.Any event = 4;
|
||||
containerd.types.Envelope envelope = 1;
|
||||
}
|
||||
|
|
33
crates/client/vendor/github.com/containerd/containerd/api/types/event.proto
generated
vendored
Normal file
33
crates/client/vendor/github.com/containerd/containerd/api/types/event.proto
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.types;
|
||||
|
||||
import "github.com/containerd/containerd/api/types/fieldpath.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/types;types";
|
||||
|
||||
message Envelope {
|
||||
option (containerd.types.fieldpath) = true;
|
||||
google.protobuf.Timestamp timestamp = 1;
|
||||
string namespace = 2;
|
||||
string topic = 3;
|
||||
google.protobuf.Any event = 4;
|
||||
}
|
|
@ -26,12 +26,12 @@
|
|||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
syntax = "proto2";
|
||||
package containerd.plugin;
|
||||
syntax = "proto3";
|
||||
package containerd.types;
|
||||
|
||||
import "google/protobuf/descriptor.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/protobuf/plugin";
|
||||
option go_package = "github.com/containerd/containerd/api/types;types";
|
||||
|
||||
extend google.protobuf.FileOptions {
|
||||
optional bool fieldpath_all = 63300;
|
46
crates/client/vendor/github.com/containerd/containerd/api/types/introspection.proto
generated
vendored
Normal file
46
crates/client/vendor/github.com/containerd/containerd/api/types/introspection.proto
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.types;
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/types;types";
|
||||
|
||||
message RuntimeRequest {
|
||||
string runtime_path = 1;
|
||||
// Options correspond to CreateTaskRequest.options.
|
||||
// This is needed to pass the runc binary path, etc.
|
||||
google.protobuf.Any options = 2;
|
||||
}
|
||||
|
||||
message RuntimeVersion {
|
||||
string version = 1;
|
||||
string revision = 2;
|
||||
}
|
||||
|
||||
message RuntimeInfo {
|
||||
string name = 1;
|
||||
RuntimeVersion version = 2;
|
||||
// Options correspond to RuntimeInfoRequest.Options (contains runc binary path, etc.)
|
||||
google.protobuf.Any options = 3;
|
||||
// OCI-compatible runtimes should use https://github.com/opencontainers/runtime-spec/blob/main/features.md
|
||||
google.protobuf.Any features = 4;
|
||||
// Annotations of the shim. Irrelevant to features.Annotations.
|
||||
map<string, string> annotations = 5;
|
||||
}
|
|
@ -26,4 +26,5 @@ message Platform {
|
|||
string os = 1;
|
||||
string architecture = 2;
|
||||
string variant = 3;
|
||||
string os_version = 4;
|
||||
}
|
||||
|
|
63
crates/client/vendor/github.com/containerd/containerd/api/types/runc/options/oci.proto
generated
vendored
Normal file
63
crates/client/vendor/github.com/containerd/containerd/api/types/runc/options/oci.proto
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package containerd.runc.v1;
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/types/runc/options;options";
|
||||
|
||||
message Options {
|
||||
// disable pivot root when creating a container
|
||||
bool no_pivot_root = 1;
|
||||
// create a new keyring for the container
|
||||
bool no_new_keyring = 2;
|
||||
// place the shim in a cgroup
|
||||
string shim_cgroup = 3;
|
||||
// set the I/O's pipes uid
|
||||
uint32 io_uid = 4;
|
||||
// set the I/O's pipes gid
|
||||
uint32 io_gid = 5;
|
||||
// binary name of the runc binary
|
||||
string binary_name = 6;
|
||||
// runc root directory
|
||||
string root = 7;
|
||||
// criu binary path.
|
||||
//
|
||||
// Removed in containerd v2.0: string criu_path = 8;
|
||||
reserved 8;
|
||||
// enable systemd cgroups
|
||||
bool systemd_cgroup = 9;
|
||||
// criu image path
|
||||
string criu_image_path = 10;
|
||||
// criu work path
|
||||
string criu_work_path = 11;
|
||||
// task api address, can be a unix domain socket, or vsock address.
|
||||
// it is in the form of ttrpc+unix://path/to/uds or grpc+vsock://<vsock cid>:<port>.
|
||||
string task_api_address = 12;
|
||||
// task api version, currently supported value is 2 and 3.
|
||||
uint32 task_api_version = 13;
|
||||
}
|
||||
|
||||
message CheckpointOptions {
|
||||
// exit the container after a checkpoint
|
||||
bool exit = 1;
|
||||
// checkpoint open tcp connections
|
||||
bool open_tcp = 2;
|
||||
// checkpoint external unix sockets
|
||||
bool external_unix_sockets = 3;
|
||||
// checkpoint terminals (ptys)
|
||||
bool terminal = 4;
|
||||
// allow checkpointing of file locks
|
||||
bool file_locks = 5;
|
||||
// restore provided namespaces as empty namespaces
|
||||
repeated string empty_namespaces = 6;
|
||||
// set the cgroups mode, soft, full, strict
|
||||
string cgroups_mode = 7;
|
||||
// checkpoint image path
|
||||
string image_path = 8;
|
||||
// checkpoint work path
|
||||
string work_path = 9;
|
||||
}
|
||||
|
||||
message ProcessDetails {
|
||||
// exec process id if the process is managed by a shim
|
||||
string exec_id = 1;
|
||||
}
|
17
crates/client/vendor/github.com/containerd/containerd/api/types/runtimeoptions/v1/api.proto
generated
vendored
Normal file
17
crates/client/vendor/github.com/containerd/containerd/api/types/runtimeoptions/v1/api.proto
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
// To regenerate api.pb.go run `make protos`
|
||||
syntax = "proto3";
|
||||
|
||||
package runtimeoptions.v1;
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/types/runtimeoptions/v1;runtimeoptions";
|
||||
|
||||
message Options {
|
||||
// TypeUrl specifies the type of the content inside the config file.
|
||||
string type_url = 1;
|
||||
// ConfigPath specifies the filesystem location of the config file
|
||||
// used by the runtime.
|
||||
string config_path = 2;
|
||||
// Blob specifies an in-memory TOML blob passed from containerd's configuration section
|
||||
// for this runtime. This will be used if config_path is not specified.
|
||||
bytes config_body = 3;
|
||||
}
|
|
@ -41,11 +41,14 @@ message Sandbox {
|
|||
// bundle directory (similary to OCI spec).
|
||||
google.protobuf.Any spec = 3;
|
||||
// Labels provides an area to include arbitrary data on containers.
|
||||
map<string, string> labels = 4;
|
||||
map<string, string> labels = 4;
|
||||
// CreatedAt is the time the container was first created.
|
||||
google.protobuf.Timestamp created_at = 5;
|
||||
// UpdatedAt is the last time the container was mutated.
|
||||
google.protobuf.Timestamp updated_at = 6;
|
||||
// Extensions allow clients to provide optional blobs that can be handled by runtime.
|
||||
map<string, google.protobuf.Any> extensions = 7;
|
||||
// Sandboxer is the name of the sandbox controller who manages the sandbox.
|
||||
string sandboxer = 10;
|
||||
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ syntax = "proto3";
|
|||
|
||||
package containerd.types.transfer;
|
||||
|
||||
import "github.com/containerd/containerd/api/types/descriptor.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/types/transfer";
|
||||
|
||||
message Progress {
|
||||
|
@ -26,4 +28,5 @@ message Progress {
|
|||
repeated string parents = 3;
|
||||
int64 progress = 4;
|
||||
int64 total = 5;
|
||||
containerd.types.Descriptor desc = 6;
|
||||
}
|
||||
|
|
22
crates/client/vendor/github.com/containerd/containerd/api/types/transfer/registry.proto
generated
vendored
22
crates/client/vendor/github.com/containerd/containerd/api/types/transfer/registry.proto
generated
vendored
|
@ -27,6 +27,16 @@ message OCIRegistry {
|
|||
RegistryResolver resolver = 2;
|
||||
}
|
||||
|
||||
enum HTTPDebug {
|
||||
DISABLED = 0;
|
||||
// Enable HTTP debugging
|
||||
DEBUG = 1;
|
||||
// Enable HTTP requests tracing
|
||||
TRACE = 2;
|
||||
// Enable both HTTP debugging and requests tracing
|
||||
BOTH = 3;
|
||||
}
|
||||
|
||||
message RegistryResolver {
|
||||
// auth_stream is used to refer to a stream which auth callbacks may be
|
||||
// made on.
|
||||
|
@ -35,10 +45,18 @@ message RegistryResolver {
|
|||
// Headers
|
||||
map<string, string> headers = 2;
|
||||
|
||||
// Allow custom hosts dir?
|
||||
string host_dir = 3;
|
||||
|
||||
string default_scheme = 4;
|
||||
// Force skip verify
|
||||
// Force HTTP
|
||||
// CA callback? Client TLS callback?
|
||||
|
||||
// Whether to debug/trace HTTP requests to OCI registry.
|
||||
HTTPDebug http_debug = 5;
|
||||
|
||||
// Stream ID to use for HTTP logs (when logs are streamed to client).
|
||||
// When empty, logs are written to containerd logs.
|
||||
string logs_stream = 6;
|
||||
}
|
||||
|
||||
// AuthRequest is sent as a callback on a stream
|
||||
|
|
46
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/events/container.proto
generated
vendored
Normal file
46
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/events/container.proto
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.events;
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
import "github.com/containerd/containerd/api/types/fieldpath.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/events;events";
|
||||
option (containerd.types.fieldpath_all) = true;
|
||||
|
||||
message ContainerCreate {
|
||||
string id = 1;
|
||||
string image = 2;
|
||||
message Runtime {
|
||||
string name = 1;
|
||||
google.protobuf.Any options = 2;
|
||||
}
|
||||
Runtime runtime = 3;
|
||||
}
|
||||
|
||||
message ContainerUpdate {
|
||||
string id = 1;
|
||||
string image = 2;
|
||||
map<string, string> labels = 3;
|
||||
string snapshot_key = 4;
|
||||
}
|
||||
|
||||
message ContainerDelete {
|
||||
string id = 1;
|
||||
}
|
33
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/events/content.proto
generated
vendored
Normal file
33
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/events/content.proto
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.events;
|
||||
|
||||
import "github.com/containerd/containerd/api/types/fieldpath.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/events;events";
|
||||
option (containerd.types.fieldpath_all) = true;
|
||||
|
||||
message ContentCreate {
|
||||
string digest = 1;
|
||||
int64 size = 2;
|
||||
}
|
||||
|
||||
message ContentDelete {
|
||||
string digest = 1;
|
||||
}
|
38
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/events/image.proto
generated
vendored
Normal file
38
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/events/image.proto
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.services.images.v1;
|
||||
|
||||
import "github.com/containerd/containerd/api/types/fieldpath.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/events;events";
|
||||
option (containerd.types.fieldpath_all) = true;
|
||||
|
||||
message ImageCreate {
|
||||
string name = 1;
|
||||
map<string, string> labels = 2;
|
||||
}
|
||||
|
||||
message ImageUpdate {
|
||||
string name = 1;
|
||||
map<string, string> labels = 2;
|
||||
}
|
||||
|
||||
message ImageDelete {
|
||||
string name = 1;
|
||||
}
|
38
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/events/namespace.proto
generated
vendored
Normal file
38
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/events/namespace.proto
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.events;
|
||||
|
||||
import "github.com/containerd/containerd/api/types/fieldpath.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/events;events";
|
||||
option (containerd.types.fieldpath_all) = true;
|
||||
|
||||
message NamespaceCreate {
|
||||
string name = 1;
|
||||
map<string, string> labels = 2;
|
||||
}
|
||||
|
||||
message NamespaceUpdate {
|
||||
string name = 1;
|
||||
map<string, string> labels = 2;
|
||||
}
|
||||
|
||||
message NamespaceDelete {
|
||||
string name = 1;
|
||||
}
|
37
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/events/sandbox.proto
generated
vendored
Normal file
37
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/events/sandbox.proto
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.events;
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/events;events";
|
||||
|
||||
message SandboxCreate {
|
||||
string sandbox_id = 1;
|
||||
}
|
||||
|
||||
message SandboxStart {
|
||||
string sandbox_id = 1;
|
||||
}
|
||||
|
||||
message SandboxExit {
|
||||
string sandbox_id = 1;
|
||||
uint32 exit_status = 2;
|
||||
google.protobuf.Timestamp exited_at = 3;
|
||||
}
|
41
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/events/snapshot.proto
generated
vendored
Normal file
41
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/events/snapshot.proto
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.events;
|
||||
|
||||
import "github.com/containerd/containerd/api/types/fieldpath.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/events;events";
|
||||
option (containerd.types.fieldpath_all) = true;
|
||||
|
||||
message SnapshotPrepare {
|
||||
string key = 1;
|
||||
string parent = 2;
|
||||
string snapshotter = 5;
|
||||
}
|
||||
|
||||
message SnapshotCommit {
|
||||
string key = 1;
|
||||
string name = 2;
|
||||
string snapshotter = 5;
|
||||
}
|
||||
|
||||
message SnapshotRemove {
|
||||
string key = 1;
|
||||
string snapshotter = 5;
|
||||
}
|
93
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/events/task.proto
generated
vendored
Normal file
93
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/events/task.proto
generated
vendored
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.events;
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "github.com/containerd/containerd/api/types/mount.proto";
|
||||
import "github.com/containerd/containerd/api/types/fieldpath.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/events;events";
|
||||
option (containerd.types.fieldpath_all) = true;
|
||||
|
||||
message TaskCreate {
|
||||
string container_id = 1;
|
||||
string bundle = 2;
|
||||
repeated containerd.types.Mount rootfs = 3;
|
||||
TaskIO io = 4;
|
||||
string checkpoint = 5;
|
||||
uint32 pid = 6;
|
||||
}
|
||||
|
||||
message TaskStart {
|
||||
string container_id = 1;
|
||||
uint32 pid = 2;
|
||||
}
|
||||
|
||||
message TaskDelete {
|
||||
string container_id = 1;
|
||||
uint32 pid = 2;
|
||||
uint32 exit_status = 3;
|
||||
google.protobuf.Timestamp exited_at = 4;
|
||||
// id is the specific exec. By default if omitted will be `""` thus matches
|
||||
// the init exec of the task matching `container_id`.
|
||||
string id = 5;
|
||||
}
|
||||
|
||||
message TaskIO {
|
||||
string stdin = 1;
|
||||
string stdout = 2;
|
||||
string stderr = 3;
|
||||
bool terminal = 4;
|
||||
}
|
||||
|
||||
message TaskExit {
|
||||
string container_id = 1;
|
||||
string id = 2;
|
||||
uint32 pid = 3;
|
||||
uint32 exit_status = 4;
|
||||
google.protobuf.Timestamp exited_at = 5;
|
||||
}
|
||||
|
||||
message TaskOOM {
|
||||
string container_id = 1;
|
||||
}
|
||||
|
||||
message TaskExecAdded {
|
||||
string container_id = 1;
|
||||
string exec_id = 2;
|
||||
}
|
||||
|
||||
message TaskExecStarted {
|
||||
string container_id = 1;
|
||||
string exec_id = 2;
|
||||
uint32 pid = 3;
|
||||
}
|
||||
|
||||
message TaskPaused {
|
||||
string container_id = 1;
|
||||
}
|
||||
|
||||
message TaskResumed {
|
||||
string container_id = 1;
|
||||
}
|
||||
|
||||
message TaskCheckpointed {
|
||||
string container_id = 1;
|
||||
string checkpoint = 2;
|
||||
}
|
181
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/containers/v1/containers.proto
generated
vendored
Normal file
181
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/containers/v1/containers.proto
generated
vendored
Normal file
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.services.containers.v1;
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
import "google/protobuf/field_mask.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/services/containers/v1;containers";
|
||||
|
||||
// Containers provides metadata storage for containers used in the execution
|
||||
// service.
|
||||
//
|
||||
// The objects here provide an state-independent view of containers for use in
|
||||
// management and resource pinning. From that perspective, containers do not
|
||||
// have a "state" but rather this is the set of resources that will be
|
||||
// considered in use by the container.
|
||||
//
|
||||
// From the perspective of the execution service, these objects represent the
|
||||
// base parameters for creating a container process.
|
||||
//
|
||||
// In general, when looking to add fields for this type, first ask yourself
|
||||
// whether or not the function of the field has to do with runtime execution or
|
||||
// is invariant of the runtime state of the container. If it has to do with
|
||||
// runtime, or changes as the "container" is started and stops, it probably
|
||||
// doesn't belong on this object.
|
||||
service Containers {
|
||||
rpc Get(GetContainerRequest) returns (GetContainerResponse);
|
||||
rpc List(ListContainersRequest) returns (ListContainersResponse);
|
||||
rpc ListStream(ListContainersRequest) returns (stream ListContainerMessage);
|
||||
rpc Create(CreateContainerRequest) returns (CreateContainerResponse);
|
||||
rpc Update(UpdateContainerRequest) returns (UpdateContainerResponse);
|
||||
rpc Delete(DeleteContainerRequest) returns (google.protobuf.Empty);
|
||||
}
|
||||
|
||||
message Container {
|
||||
// ID is the user-specified identifier.
|
||||
//
|
||||
// This field may not be updated.
|
||||
string id = 1;
|
||||
|
||||
// Labels provides an area to include arbitrary data on containers.
|
||||
//
|
||||
// The combined size of a key/value pair cannot exceed 4096 bytes.
|
||||
//
|
||||
// Note that to add a new value to this field, read the existing set and
|
||||
// include the entire result in the update call.
|
||||
map<string, string> labels = 2;
|
||||
|
||||
// Image contains the reference of the image used to build the
|
||||
// specification and snapshots for running this container.
|
||||
//
|
||||
// If this field is updated, the spec and rootfs needed to updated, as well.
|
||||
string image = 3;
|
||||
|
||||
message Runtime {
|
||||
// Name is the name of the runtime.
|
||||
string name = 1;
|
||||
// Options specify additional runtime initialization options.
|
||||
google.protobuf.Any options = 2;
|
||||
}
|
||||
// Runtime specifies which runtime to use for executing this container.
|
||||
Runtime runtime = 4;
|
||||
|
||||
// Spec to be used when creating the container. This is runtime specific.
|
||||
google.protobuf.Any spec = 5;
|
||||
|
||||
// Snapshotter specifies the snapshotter name used for rootfs
|
||||
string snapshotter = 6;
|
||||
|
||||
// SnapshotKey specifies the snapshot key to use for the container's root
|
||||
// filesystem. When starting a task from this container, a caller should
|
||||
// look up the mounts from the snapshot service and include those on the
|
||||
// task create request.
|
||||
//
|
||||
// Snapshots referenced in this field will not be garbage collected.
|
||||
//
|
||||
// This field is set to empty when the rootfs is not a snapshot.
|
||||
//
|
||||
// This field may be updated.
|
||||
string snapshot_key = 7;
|
||||
|
||||
// CreatedAt is the time the container was first created.
|
||||
google.protobuf.Timestamp created_at = 8;
|
||||
|
||||
// UpdatedAt is the last time the container was mutated.
|
||||
google.protobuf.Timestamp updated_at = 9;
|
||||
|
||||
// Extensions allow clients to provide zero or more blobs that are directly
|
||||
// associated with the container. One may provide protobuf, json, or other
|
||||
// encoding formats. The primary use of this is to further decorate the
|
||||
// container object with fields that may be specific to a client integration.
|
||||
//
|
||||
// The key portion of this map should identify a "name" for the extension
|
||||
// that should be unique against other extensions. When updating extension
|
||||
// data, one should only update the specified extension using field paths
|
||||
// to select a specific map key.
|
||||
map<string, google.protobuf.Any> extensions = 10;
|
||||
|
||||
// Sandbox ID this container belongs to.
|
||||
string sandbox = 11;
|
||||
}
|
||||
|
||||
message GetContainerRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message GetContainerResponse {
|
||||
Container container = 1;
|
||||
}
|
||||
|
||||
message ListContainersRequest {
|
||||
// Filters contains one or more filters using the syntax defined in the
|
||||
// containerd filter package.
|
||||
//
|
||||
// The returned result will be those that match any of the provided
|
||||
// filters. Expanded, containers that match the following will be
|
||||
// returned:
|
||||
//
|
||||
// filters[0] or filters[1] or ... or filters[n-1] or filters[n]
|
||||
//
|
||||
// If filters is zero-length or nil, all items will be returned.
|
||||
repeated string filters = 1;
|
||||
}
|
||||
|
||||
message ListContainersResponse {
|
||||
repeated Container containers = 1;
|
||||
}
|
||||
|
||||
message CreateContainerRequest {
|
||||
Container container = 1;
|
||||
}
|
||||
|
||||
message CreateContainerResponse {
|
||||
Container container = 1;
|
||||
}
|
||||
|
||||
// UpdateContainerRequest updates the metadata on one or more container.
|
||||
//
|
||||
// The operation should follow semantics described in
|
||||
// https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/field-mask,
|
||||
// unless otherwise qualified.
|
||||
message UpdateContainerRequest {
|
||||
// Container provides the target values, as declared by the mask, for the update.
|
||||
//
|
||||
// The ID field must be set.
|
||||
Container container = 1;
|
||||
|
||||
// UpdateMask specifies which fields to perform the update on. If empty,
|
||||
// the operation applies to all fields.
|
||||
google.protobuf.FieldMask update_mask = 2;
|
||||
}
|
||||
|
||||
message UpdateContainerResponse {
|
||||
Container container = 1;
|
||||
}
|
||||
|
||||
message DeleteContainerRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message ListContainerMessage {
|
||||
Container container = 1;
|
||||
}
|
330
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/content/v1/content.proto
generated
vendored
Normal file
330
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/content/v1/content.proto
generated
vendored
Normal file
|
@ -0,0 +1,330 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.services.content.v1;
|
||||
|
||||
import "google/protobuf/field_mask.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/services/content/v1;content";
|
||||
|
||||
// Content provides access to a content addressable storage system.
|
||||
service Content {
|
||||
// Info returns information about a committed object.
|
||||
//
|
||||
// This call can be used for getting the size of content and checking for
|
||||
// existence.
|
||||
rpc Info(InfoRequest) returns (InfoResponse);
|
||||
|
||||
// Update updates content metadata.
|
||||
//
|
||||
// This call can be used to manage the mutable content labels. The
|
||||
// immutable metadata such as digest, size, and committed at cannot
|
||||
// be updated.
|
||||
rpc Update(UpdateRequest) returns (UpdateResponse);
|
||||
|
||||
// List streams the entire set of content as Info objects and closes the
|
||||
// stream.
|
||||
//
|
||||
// Typically, this will yield a large response, chunked into messages.
|
||||
// Clients should make provisions to ensure they can handle the entire data
|
||||
// set.
|
||||
rpc List(ListContentRequest) returns (stream ListContentResponse);
|
||||
|
||||
// Delete will delete the referenced object.
|
||||
rpc Delete(DeleteContentRequest) returns (google.protobuf.Empty);
|
||||
|
||||
// Read allows one to read an object based on the offset into the content.
|
||||
//
|
||||
// The requested data may be returned in one or more messages.
|
||||
rpc Read(ReadContentRequest) returns (stream ReadContentResponse);
|
||||
|
||||
// Status returns the status for a single reference.
|
||||
rpc Status(StatusRequest) returns (StatusResponse);
|
||||
|
||||
// ListStatuses returns the status of ongoing object ingestions, started via
|
||||
// Write.
|
||||
//
|
||||
// Only those matching the regular expression will be provided in the
|
||||
// response. If the provided regular expression is empty, all ingestions
|
||||
// will be provided.
|
||||
rpc ListStatuses(ListStatusesRequest) returns (ListStatusesResponse);
|
||||
|
||||
// Write begins or resumes writes to a resource identified by a unique ref.
|
||||
// Only one active stream may exist at a time for each ref.
|
||||
//
|
||||
// Once a write stream has started, it may only write to a single ref, thus
|
||||
// once a stream is started, the ref may be omitted on subsequent writes.
|
||||
//
|
||||
// For any write transaction represented by a ref, only a single write may
|
||||
// be made to a given offset. If overlapping writes occur, it is an error.
|
||||
// Writes should be sequential and implementations may throw an error if
|
||||
// this is required.
|
||||
//
|
||||
// If expected_digest is set and already part of the content store, the
|
||||
// write will fail.
|
||||
//
|
||||
// When completed, the commit flag should be set to true. If expected size
|
||||
// or digest is set, the content will be validated against those values.
|
||||
rpc Write(stream WriteContentRequest) returns (stream WriteContentResponse);
|
||||
|
||||
// Abort cancels the ongoing write named in the request. Any resources
|
||||
// associated with the write will be collected.
|
||||
rpc Abort(AbortRequest) returns (google.protobuf.Empty);
|
||||
}
|
||||
|
||||
message Info {
|
||||
// Digest is the hash identity of the blob.
|
||||
string digest = 1;
|
||||
|
||||
// Size is the total number of bytes in the blob.
|
||||
int64 size = 2;
|
||||
|
||||
// CreatedAt provides the time at which the blob was committed.
|
||||
google.protobuf.Timestamp created_at = 3;
|
||||
|
||||
// UpdatedAt provides the time the info was last updated.
|
||||
google.protobuf.Timestamp updated_at = 4;
|
||||
|
||||
// Labels are arbitrary data on snapshots.
|
||||
//
|
||||
// The combined size of a key/value pair cannot exceed 4096 bytes.
|
||||
map<string, string> labels = 5;
|
||||
}
|
||||
|
||||
message InfoRequest {
|
||||
string digest = 1;
|
||||
}
|
||||
|
||||
message InfoResponse {
|
||||
Info info = 1;
|
||||
}
|
||||
|
||||
message UpdateRequest {
|
||||
Info info = 1;
|
||||
|
||||
// UpdateMask specifies which fields to perform the update on. If empty,
|
||||
// the operation applies to all fields.
|
||||
//
|
||||
// In info, Digest, Size, and CreatedAt are immutable,
|
||||
// other field may be updated using this mask.
|
||||
// If no mask is provided, all mutable field are updated.
|
||||
google.protobuf.FieldMask update_mask = 2;
|
||||
}
|
||||
|
||||
message UpdateResponse {
|
||||
Info info = 1;
|
||||
}
|
||||
|
||||
message ListContentRequest {
|
||||
// Filters contains one or more filters using the syntax defined in the
|
||||
// containerd filter package.
|
||||
//
|
||||
// The returned result will be those that match any of the provided
|
||||
// filters. Expanded, containers that match the following will be
|
||||
// returned:
|
||||
//
|
||||
// filters[0] or filters[1] or ... or filters[n-1] or filters[n]
|
||||
//
|
||||
// If filters is zero-length or nil, all items will be returned.
|
||||
repeated string filters = 1;
|
||||
}
|
||||
|
||||
message ListContentResponse {
|
||||
repeated Info info = 1;
|
||||
}
|
||||
|
||||
message DeleteContentRequest {
|
||||
// Digest specifies which content to delete.
|
||||
string digest = 1;
|
||||
}
|
||||
|
||||
// ReadContentRequest defines the fields that make up a request to read a portion of
|
||||
// data from a stored object.
|
||||
message ReadContentRequest {
|
||||
// Digest is the hash identity to read.
|
||||
string digest = 1;
|
||||
|
||||
// Offset specifies the number of bytes from the start at which to begin
|
||||
// the read. If zero or less, the read will be from the start. This uses
|
||||
// standard zero-indexed semantics.
|
||||
int64 offset = 2;
|
||||
|
||||
// size is the total size of the read. If zero, the entire blob will be
|
||||
// returned by the service.
|
||||
int64 size = 3;
|
||||
}
|
||||
|
||||
// ReadContentResponse carries byte data for a read request.
|
||||
message ReadContentResponse {
|
||||
int64 offset = 1; // offset of the returned data
|
||||
bytes data = 2; // actual data
|
||||
}
|
||||
|
||||
message Status {
|
||||
google.protobuf.Timestamp started_at = 1;
|
||||
google.protobuf.Timestamp updated_at = 2;
|
||||
string ref = 3;
|
||||
int64 offset = 4;
|
||||
int64 total = 5;
|
||||
string expected = 6;
|
||||
}
|
||||
|
||||
|
||||
message StatusRequest {
|
||||
string ref = 1;
|
||||
}
|
||||
|
||||
message StatusResponse {
|
||||
Status status = 1;
|
||||
}
|
||||
|
||||
message ListStatusesRequest {
|
||||
repeated string filters = 1;
|
||||
}
|
||||
|
||||
message ListStatusesResponse {
|
||||
repeated Status statuses = 1;
|
||||
}
|
||||
|
||||
// WriteAction defines the behavior of a WriteRequest.
|
||||
enum WriteAction {
|
||||
// WriteActionStat instructs the writer to return the current status while
|
||||
// holding the lock on the write.
|
||||
STAT = 0;
|
||||
|
||||
// WriteActionWrite sets the action for the write request to write data.
|
||||
//
|
||||
// Any data included will be written at the provided offset. The
|
||||
// transaction will be left open for further writes.
|
||||
//
|
||||
// This is the default.
|
||||
WRITE = 1;
|
||||
|
||||
// WriteActionCommit will write any outstanding data in the message and
|
||||
// commit the write, storing it under the digest.
|
||||
//
|
||||
// This can be used in a single message to send the data, verify it and
|
||||
// commit it.
|
||||
//
|
||||
// This action will always terminate the write.
|
||||
COMMIT = 2;
|
||||
}
|
||||
|
||||
// WriteContentRequest writes data to the request ref at offset.
|
||||
message WriteContentRequest {
|
||||
// Action sets the behavior of the write.
|
||||
//
|
||||
// When this is a write and the ref is not yet allocated, the ref will be
|
||||
// allocated and the data will be written at offset.
|
||||
//
|
||||
// If the action is write and the ref is allocated, it will accept data to
|
||||
// an offset that has not yet been written.
|
||||
//
|
||||
// If the action is write and there is no data, the current write status
|
||||
// will be returned. This works differently from status because the stream
|
||||
// holds a lock.
|
||||
WriteAction action = 1;
|
||||
|
||||
// Ref identifies the pre-commit object to write to.
|
||||
string ref = 2;
|
||||
|
||||
// Total can be set to have the service validate the total size of the
|
||||
// committed content.
|
||||
//
|
||||
// The latest value before or with the commit action message will be use to
|
||||
// validate the content. If the offset overflows total, the service may
|
||||
// report an error. It is only required on one message for the write.
|
||||
//
|
||||
// If the value is zero or less, no validation of the final content will be
|
||||
// performed.
|
||||
int64 total = 3;
|
||||
|
||||
// Expected can be set to have the service validate the final content against
|
||||
// the provided digest.
|
||||
//
|
||||
// If the digest is already present in the object store, an AlreadyExists
|
||||
// error will be returned.
|
||||
//
|
||||
// Only the latest version will be used to check the content against the
|
||||
// digest. It is only required to include it on a single message, before or
|
||||
// with the commit action message.
|
||||
string expected = 4;
|
||||
|
||||
// Offset specifies the number of bytes from the start at which to begin
|
||||
// the write. For most implementations, this means from the start of the
|
||||
// file. This uses standard, zero-indexed semantics.
|
||||
//
|
||||
// If the action is write, the remote may remove all previously written
|
||||
// data after the offset. Implementations may support arbitrary offsets but
|
||||
// MUST support reseting this value to zero with a write. If an
|
||||
// implementation does not support a write at a particular offset, an
|
||||
// OutOfRange error must be returned.
|
||||
int64 offset = 5;
|
||||
|
||||
// Data is the actual bytes to be written.
|
||||
//
|
||||
// If this is empty and the message is not a commit, a response will be
|
||||
// returned with the current write state.
|
||||
bytes data = 6;
|
||||
|
||||
// Labels are arbitrary data on snapshots.
|
||||
//
|
||||
// The combined size of a key/value pair cannot exceed 4096 bytes.
|
||||
map<string, string> labels = 7;
|
||||
}
|
||||
|
||||
// WriteContentResponse is returned on the culmination of a write call.
|
||||
message WriteContentResponse {
|
||||
// Action contains the action for the final message of the stream. A writer
|
||||
// should confirm that they match the intended result.
|
||||
WriteAction action = 1;
|
||||
|
||||
// StartedAt provides the time at which the write began.
|
||||
//
|
||||
// This must be set for stat and commit write actions. All other write
|
||||
// actions may omit this.
|
||||
google.protobuf.Timestamp started_at = 2;
|
||||
|
||||
// UpdatedAt provides the last time of a successful write.
|
||||
//
|
||||
// This must be set for stat and commit write actions. All other write
|
||||
// actions may omit this.
|
||||
google.protobuf.Timestamp updated_at = 3;
|
||||
|
||||
// Offset is the current committed size for the write.
|
||||
int64 offset = 4;
|
||||
|
||||
// Total provides the current, expected total size of the write.
|
||||
//
|
||||
// We include this to provide consistency with the Status structure on the
|
||||
// client writer.
|
||||
//
|
||||
// This is only valid on the Stat and Commit response.
|
||||
int64 total = 5;
|
||||
|
||||
// Digest, if present, includes the digest up to the currently committed
|
||||
// bytes. If action is commit, this field will be set. It is implementation
|
||||
// defined if this is set for other actions.
|
||||
string digest = 6;
|
||||
}
|
||||
|
||||
message AbortRequest {
|
||||
string ref = 1;
|
||||
}
|
90
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/diff/v1/diff.proto
generated
vendored
Normal file
90
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/diff/v1/diff.proto
generated
vendored
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.services.diff.v1;
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "github.com/containerd/containerd/api/types/mount.proto";
|
||||
import "github.com/containerd/containerd/api/types/descriptor.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/services/diff/v1;diff";
|
||||
|
||||
// Diff service creates and applies diffs
|
||||
service Diff {
|
||||
// Apply applies the content associated with the provided digests onto
|
||||
// the provided mounts. Archive content will be extracted and
|
||||
// decompressed if necessary.
|
||||
rpc Apply(ApplyRequest) returns (ApplyResponse);
|
||||
|
||||
// Diff creates a diff between the given mounts and uploads the result
|
||||
// to the content store.
|
||||
rpc Diff(DiffRequest) returns (DiffResponse);
|
||||
}
|
||||
|
||||
message ApplyRequest {
|
||||
// Diff is the descriptor of the diff to be extracted
|
||||
containerd.types.Descriptor diff = 1;
|
||||
|
||||
repeated containerd.types.Mount mounts = 2;
|
||||
|
||||
map<string, google.protobuf.Any> payloads = 3;
|
||||
// SyncFs is to synchronize the underlying filesystem containing files.
|
||||
bool sync_fs = 4;
|
||||
}
|
||||
|
||||
message ApplyResponse {
|
||||
// Applied is the descriptor for the object which was applied.
|
||||
// If the input was a compressed blob then the result will be
|
||||
// the descriptor for the uncompressed blob.
|
||||
containerd.types.Descriptor applied = 1;
|
||||
}
|
||||
|
||||
message DiffRequest {
|
||||
// Left are the mounts which represent the older copy
|
||||
// in which is the base of the computed changes.
|
||||
repeated containerd.types.Mount left = 1;
|
||||
|
||||
// Right are the mounts which represents the newer copy
|
||||
// in which changes from the left were made into.
|
||||
repeated containerd.types.Mount right = 2;
|
||||
|
||||
// MediaType is the media type descriptor for the created diff
|
||||
// object
|
||||
string media_type = 3;
|
||||
|
||||
// Ref identifies the pre-commit content store object. This
|
||||
// reference can be used to get the status from the content store.
|
||||
string ref = 4;
|
||||
|
||||
// Labels are the labels to apply to the generated content
|
||||
// on content store commit.
|
||||
map<string, string> labels = 5;
|
||||
|
||||
// SourceDateEpoch specifies the timestamp used to provide control for reproducibility.
|
||||
// See also https://reproducible-builds.org/docs/source-date-epoch/ .
|
||||
//
|
||||
// Since containerd v2.0, the whiteout timestamps are set to zero (1970-01-01),
|
||||
// not to the source date epoch.
|
||||
google.protobuf.Timestamp source_date_epoch = 6;
|
||||
}
|
||||
|
||||
message DiffResponse {
|
||||
// Diff is the descriptor of the diff which can be applied
|
||||
containerd.types.Descriptor diff = 3;
|
||||
}
|
62
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/events/v1/events.proto
generated
vendored
Normal file
62
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/events/v1/events.proto
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.services.events.v1;
|
||||
|
||||
import "github.com/containerd/containerd/api/types/event.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/services/events/v1;events";
|
||||
|
||||
service Events {
|
||||
// Publish an event to a topic.
|
||||
//
|
||||
// The event will be packed into a timestamp envelope with the namespace
|
||||
// introspected from the context. The envelope will then be dispatched.
|
||||
rpc Publish(PublishRequest) returns (google.protobuf.Empty);
|
||||
|
||||
// Forward sends an event that has already been packaged into an envelope
|
||||
// with a timestamp and namespace.
|
||||
//
|
||||
// This is useful if earlier timestamping is required or when forwarding on
|
||||
// behalf of another component, namespace or publisher.
|
||||
rpc Forward(ForwardRequest) returns (google.protobuf.Empty);
|
||||
|
||||
// Subscribe to a stream of events, possibly returning only that match any
|
||||
// of the provided filters.
|
||||
//
|
||||
// Unlike many other methods in containerd, subscribers will get messages
|
||||
// from all namespaces unless otherwise specified. If this is not desired,
|
||||
// a filter can be provided in the format 'namespace==<namespace>' to
|
||||
// restrict the received events.
|
||||
rpc Subscribe(SubscribeRequest) returns (stream containerd.types.Envelope);
|
||||
}
|
||||
|
||||
message PublishRequest {
|
||||
string topic = 1;
|
||||
google.protobuf.Any event = 2;
|
||||
}
|
||||
|
||||
message ForwardRequest {
|
||||
containerd.types.Envelope envelope = 1;
|
||||
}
|
||||
|
||||
message SubscribeRequest {
|
||||
repeated string filters = 1;
|
||||
}
|
149
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/images/v1/images.proto
generated
vendored
Normal file
149
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/images/v1/images.proto
generated
vendored
Normal file
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.services.images.v1;
|
||||
|
||||
import "google/protobuf/empty.proto";
|
||||
import "google/protobuf/field_mask.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "github.com/containerd/containerd/api/types/descriptor.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/services/images/v1;images";
|
||||
|
||||
// Images is a service that allows one to register images with containerd.
|
||||
//
|
||||
// In containerd, an image is merely the mapping of a name to a content root,
|
||||
// described by a descriptor. The behavior and state of image is purely
|
||||
// dictated by the type of the descriptor.
|
||||
//
|
||||
// From the perspective of this service, these references are mostly shallow,
|
||||
// in that the existence of the required content won't be validated until
|
||||
// required by consuming services.
|
||||
//
|
||||
// As such, this can really be considered a "metadata service".
|
||||
service Images {
|
||||
// Get returns an image by name.
|
||||
rpc Get(GetImageRequest) returns (GetImageResponse);
|
||||
|
||||
// List returns a list of all images known to containerd.
|
||||
rpc List(ListImagesRequest) returns (ListImagesResponse);
|
||||
|
||||
// Create an image record in the metadata store.
|
||||
//
|
||||
// The name of the image must be unique.
|
||||
rpc Create(CreateImageRequest) returns (CreateImageResponse);
|
||||
|
||||
// Update assigns the name to a given target image based on the provided
|
||||
// image.
|
||||
rpc Update(UpdateImageRequest) returns (UpdateImageResponse);
|
||||
|
||||
// Delete deletes the image by name.
|
||||
rpc Delete(DeleteImageRequest) returns (google.protobuf.Empty);
|
||||
}
|
||||
|
||||
message Image {
|
||||
// Name provides a unique name for the image.
|
||||
//
|
||||
// Containerd treats this as the primary identifier.
|
||||
string name = 1;
|
||||
|
||||
// Labels provides free form labels for the image. These are runtime only
|
||||
// and do not get inherited into the package image in any way.
|
||||
//
|
||||
// Labels may be updated using the field mask.
|
||||
// The combined size of a key/value pair cannot exceed 4096 bytes.
|
||||
map<string, string> labels = 2;
|
||||
|
||||
// Target describes the content entry point of the image.
|
||||
containerd.types.Descriptor target = 3;
|
||||
|
||||
// CreatedAt is the time the image was first created.
|
||||
google.protobuf.Timestamp created_at = 7;
|
||||
|
||||
// UpdatedAt is the last time the image was mutated.
|
||||
google.protobuf.Timestamp updated_at = 8;
|
||||
}
|
||||
|
||||
message GetImageRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message GetImageResponse {
|
||||
Image image = 1;
|
||||
}
|
||||
|
||||
message CreateImageRequest {
|
||||
Image image = 1;
|
||||
|
||||
google.protobuf.Timestamp source_date_epoch = 2;
|
||||
}
|
||||
|
||||
message CreateImageResponse {
|
||||
Image image = 1;
|
||||
}
|
||||
|
||||
message UpdateImageRequest {
|
||||
// Image provides a full or partial image for update.
|
||||
//
|
||||
// The name field must be set or an error will be returned.
|
||||
Image image = 1;
|
||||
|
||||
// UpdateMask specifies which fields to perform the update on. If empty,
|
||||
// the operation applies to all fields.
|
||||
google.protobuf.FieldMask update_mask = 2;
|
||||
|
||||
google.protobuf.Timestamp source_date_epoch = 3;
|
||||
}
|
||||
|
||||
message UpdateImageResponse {
|
||||
Image image = 1;
|
||||
}
|
||||
|
||||
message ListImagesRequest {
|
||||
// Filters contains one or more filters using the syntax defined in the
|
||||
// containerd filter package.
|
||||
//
|
||||
// The returned result will be those that match any of the provided
|
||||
// filters. Expanded, images that match the following will be
|
||||
// returned:
|
||||
//
|
||||
// filters[0] or filters[1] or ... or filters[n-1] or filters[n]
|
||||
//
|
||||
// If filters is zero-length or nil, all items will be returned.
|
||||
repeated string filters = 1;
|
||||
}
|
||||
|
||||
message ListImagesResponse {
|
||||
repeated Image images = 1;
|
||||
}
|
||||
|
||||
message DeleteImageRequest {
|
||||
string name = 1;
|
||||
|
||||
// Sync indicates that the delete and cleanup should be done
|
||||
// synchronously before returning to the caller
|
||||
//
|
||||
// Default is false
|
||||
bool sync = 2;
|
||||
|
||||
// Target value for image to be deleted
|
||||
//
|
||||
// If image descriptor does not match the same digest,
|
||||
// the delete operation will return "not found" error.
|
||||
optional containerd.types.Descriptor target = 3;
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.services.introspection.v1;
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
import "github.com/containerd/containerd/api/types/introspection.proto";
|
||||
import "github.com/containerd/containerd/api/types/platform.proto";
|
||||
import "google/rpc/status.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/services/introspection/v1;introspection";
|
||||
|
||||
service Introspection {
|
||||
// Plugins returns a list of plugins in containerd.
|
||||
//
|
||||
// Clients can use this to detect features and capabilities when using
|
||||
// containerd.
|
||||
rpc Plugins(PluginsRequest) returns (PluginsResponse);
|
||||
// Server returns information about the containerd server
|
||||
rpc Server(google.protobuf.Empty) returns (ServerResponse);
|
||||
// PluginInfo returns information directly from a plugin if the plugin supports it
|
||||
rpc PluginInfo(PluginInfoRequest) returns (PluginInfoResponse);
|
||||
}
|
||||
|
||||
message Plugin {
|
||||
// Type defines the type of plugin.
|
||||
//
|
||||
// See package plugin for a list of possible values. Non core plugins may
|
||||
// define their own values during registration.
|
||||
string type = 1;
|
||||
|
||||
// ID identifies the plugin uniquely in the system.
|
||||
string id = 2;
|
||||
|
||||
// Requires lists the plugin types required by this plugin.
|
||||
repeated string requires = 3;
|
||||
|
||||
// Platforms enumerates the platforms this plugin will support.
|
||||
//
|
||||
// If values are provided here, the plugin will only be operable under the
|
||||
// provided platforms.
|
||||
//
|
||||
// If this is empty, the plugin will work across all platforms.
|
||||
//
|
||||
// If the plugin prefers certain platforms over others, they should be
|
||||
// listed from most to least preferred.
|
||||
repeated types.Platform platforms = 4;
|
||||
|
||||
// Exports allows plugins to provide values about state or configuration to
|
||||
// interested parties.
|
||||
//
|
||||
// One example is exposing the configured path of a snapshotter plugin.
|
||||
map<string, string> exports = 5;
|
||||
|
||||
// Capabilities allows plugins to communicate feature switches to allow
|
||||
// clients to detect features that may not be on be default or may be
|
||||
// different from version to version.
|
||||
//
|
||||
// Use this sparingly.
|
||||
repeated string capabilities = 6;
|
||||
|
||||
// InitErr will be set if the plugin fails initialization.
|
||||
//
|
||||
// This means the plugin may have been registered but a non-terminal error
|
||||
// was encountered during initialization.
|
||||
//
|
||||
// Plugins that have this value set cannot be used.
|
||||
google.rpc.Status init_err = 7;
|
||||
}
|
||||
|
||||
message PluginsRequest {
|
||||
// Filters contains one or more filters using the syntax defined in the
|
||||
// containerd filter package.
|
||||
//
|
||||
// The returned result will be those that match any of the provided
|
||||
// filters. Expanded, plugins that match the following will be
|
||||
// returned:
|
||||
//
|
||||
// filters[0] or filters[1] or ... or filters[n-1] or filters[n]
|
||||
//
|
||||
// If filters is zero-length or nil, all items will be returned.
|
||||
repeated string filters = 1;
|
||||
}
|
||||
|
||||
message PluginsResponse {
|
||||
repeated Plugin plugins = 1;
|
||||
}
|
||||
|
||||
message ServerResponse {
|
||||
string uuid = 1;
|
||||
uint64 pid = 2;
|
||||
uint64 pidns = 3; // PID namespace, such as 4026531836
|
||||
repeated DeprecationWarning deprecations = 4;
|
||||
}
|
||||
|
||||
message DeprecationWarning {
|
||||
string id = 1;
|
||||
string message = 2;
|
||||
google.protobuf.Timestamp last_occurrence = 3;
|
||||
}
|
||||
|
||||
message PluginInfoRequest {
|
||||
string type = 1;
|
||||
string id = 2;
|
||||
|
||||
// Options may be used to request extra dynamic information from
|
||||
// a plugin.
|
||||
// This object is determined by the plugin and the plugin may return
|
||||
// NotImplemented or InvalidArgument if it is not supported
|
||||
google.protobuf.Any options = 3;
|
||||
}
|
||||
|
||||
message PluginInfoResponse {
|
||||
Plugin plugin = 1;
|
||||
google.protobuf.Any extra = 2;
|
||||
}
|
116
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/leases/v1/leases.proto
generated
vendored
Normal file
116
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/leases/v1/leases.proto
generated
vendored
Normal file
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.services.leases.v1;
|
||||
|
||||
import "google/protobuf/empty.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/services/leases/v1;leases";
|
||||
|
||||
// Leases service manages resources leases within the metadata store.
|
||||
service Leases {
|
||||
// Create creates a new lease for managing changes to metadata. A lease
|
||||
// can be used to protect objects from being removed.
|
||||
rpc Create(CreateRequest) returns (CreateResponse);
|
||||
|
||||
// Delete deletes the lease and makes any unreferenced objects created
|
||||
// during the lease eligible for garbage collection if not referenced
|
||||
// or retained by other resources during the lease.
|
||||
rpc Delete(DeleteRequest) returns (google.protobuf.Empty);
|
||||
|
||||
// List lists all active leases, returning the full list of
|
||||
// leases and optionally including the referenced resources.
|
||||
rpc List(ListRequest) returns (ListResponse);
|
||||
|
||||
// AddResource references the resource by the provided lease.
|
||||
rpc AddResource(AddResourceRequest) returns (google.protobuf.Empty);
|
||||
|
||||
// DeleteResource dereferences the resource by the provided lease.
|
||||
rpc DeleteResource(DeleteResourceRequest) returns (google.protobuf.Empty);
|
||||
|
||||
// ListResources lists all the resources referenced by the lease.
|
||||
rpc ListResources(ListResourcesRequest) returns (ListResourcesResponse);
|
||||
}
|
||||
|
||||
// Lease is an object which retains resources while it exists.
|
||||
message Lease {
|
||||
string id = 1;
|
||||
|
||||
google.protobuf.Timestamp created_at = 2;
|
||||
|
||||
map<string, string> labels = 3;
|
||||
}
|
||||
|
||||
message CreateRequest {
|
||||
// ID is used to identity the lease, when the id is not set the service
|
||||
// generates a random identifier for the lease.
|
||||
string id = 1;
|
||||
|
||||
map<string, string> labels = 3;
|
||||
}
|
||||
|
||||
message CreateResponse {
|
||||
Lease lease = 1;
|
||||
}
|
||||
|
||||
message DeleteRequest {
|
||||
string id = 1;
|
||||
|
||||
// Sync indicates that the delete and cleanup should be done
|
||||
// synchronously before returning to the caller
|
||||
//
|
||||
// Default is false
|
||||
bool sync = 2;
|
||||
}
|
||||
|
||||
message ListRequest {
|
||||
repeated string filters = 1;
|
||||
}
|
||||
|
||||
message ListResponse {
|
||||
repeated Lease leases = 1;
|
||||
}
|
||||
|
||||
message Resource {
|
||||
string id = 1;
|
||||
|
||||
// For snapshotter resource, there are many snapshotter types here, like
|
||||
// overlayfs, devmapper etc. The type will be formatted with type,
|
||||
// like "snapshotter/overlayfs".
|
||||
string type = 2;
|
||||
}
|
||||
|
||||
message AddResourceRequest {
|
||||
string id = 1;
|
||||
|
||||
Resource resource = 2;
|
||||
}
|
||||
|
||||
message DeleteResourceRequest {
|
||||
string id = 1;
|
||||
|
||||
Resource resource = 2;
|
||||
}
|
||||
|
||||
message ListResourcesRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message ListResourcesResponse {
|
||||
repeated Resource resources = 1 ;
|
||||
}
|
107
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/namespaces/v1/namespace.proto
generated
vendored
Normal file
107
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/namespaces/v1/namespace.proto
generated
vendored
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.services.namespaces.v1;
|
||||
|
||||
import "google/protobuf/empty.proto";
|
||||
import "google/protobuf/field_mask.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/services/namespaces/v1;namespaces";
|
||||
|
||||
// Namespaces provides the ability to manipulate containerd namespaces.
|
||||
//
|
||||
// All objects in the system are required to be a member of a namespace. If a
|
||||
// namespace is deleted, all objects, including containers, images and
|
||||
// snapshots, will be deleted, as well.
|
||||
//
|
||||
// Unless otherwise noted, operations in containerd apply only to the namespace
|
||||
// supplied per request.
|
||||
//
|
||||
// I hope this goes without saying, but namespaces are themselves NOT
|
||||
// namespaced.
|
||||
service Namespaces {
|
||||
rpc Get(GetNamespaceRequest) returns (GetNamespaceResponse);
|
||||
rpc List(ListNamespacesRequest) returns (ListNamespacesResponse);
|
||||
rpc Create(CreateNamespaceRequest) returns (CreateNamespaceResponse);
|
||||
rpc Update(UpdateNamespaceRequest) returns (UpdateNamespaceResponse);
|
||||
rpc Delete(DeleteNamespaceRequest) returns (google.protobuf.Empty);
|
||||
}
|
||||
|
||||
message Namespace {
|
||||
string name = 1;
|
||||
|
||||
// Labels provides an area to include arbitrary data on namespaces.
|
||||
//
|
||||
// The combined size of a key/value pair cannot exceed 4096 bytes.
|
||||
//
|
||||
// Note that to add a new value to this field, read the existing set and
|
||||
// include the entire result in the update call.
|
||||
map<string, string> labels = 2;
|
||||
}
|
||||
|
||||
message GetNamespaceRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message GetNamespaceResponse {
|
||||
Namespace namespace = 1;
|
||||
}
|
||||
|
||||
message ListNamespacesRequest {
|
||||
string filter = 1;
|
||||
}
|
||||
|
||||
message ListNamespacesResponse {
|
||||
repeated Namespace namespaces = 1;
|
||||
}
|
||||
|
||||
message CreateNamespaceRequest {
|
||||
Namespace namespace = 1;
|
||||
}
|
||||
|
||||
message CreateNamespaceResponse {
|
||||
Namespace namespace = 1;
|
||||
}
|
||||
|
||||
// UpdateNamespaceRequest updates the metadata for a namespace.
|
||||
//
|
||||
// The operation should follow semantics described in
|
||||
// https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/field-mask,
|
||||
// unless otherwise qualified.
|
||||
message UpdateNamespaceRequest {
|
||||
// Namespace provides the target value, as declared by the mask, for the update.
|
||||
//
|
||||
// The namespace field must be set.
|
||||
Namespace namespace = 1;
|
||||
|
||||
// UpdateMask specifies which fields to perform the update on. If empty,
|
||||
// the operation applies to all fields.
|
||||
//
|
||||
// For the most part, this applies only to selectively updating labels on
|
||||
// the namespace. While field masks are typically limited to ascii alphas
|
||||
// and digits, we just take everything after the "labels." as the map key.
|
||||
google.protobuf.FieldMask update_mask = 2;
|
||||
}
|
||||
|
||||
message UpdateNamespaceResponse {
|
||||
Namespace namespace = 1;
|
||||
}
|
||||
|
||||
message DeleteNamespaceRequest {
|
||||
string name = 1;
|
||||
}
|
204
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/sandbox/v1/sandbox.proto
generated
vendored
Normal file
204
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/sandbox/v1/sandbox.proto
generated
vendored
Normal file
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
// Sandbox is a v2 runtime extension that allows more complex execution environments for containers.
|
||||
// This adds a notion of groups of containers that share same lifecycle and/or resources.
|
||||
// A few good fits for sandbox can be:
|
||||
// - A "pause" container in k8s, that acts as a parent process for child containers to hold network namespace.
|
||||
// - (micro)VMs that launch a VM process and executes containers inside guest OS.
|
||||
// containerd in this case remains implementation agnostic and delegates sandbox handling to runtimes.
|
||||
// See proposal and discussion here: https://github.com/containerd/containerd/issues/4131
|
||||
package containerd.services.sandbox.v1;
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
import "github.com/containerd/containerd/api/types/sandbox.proto";
|
||||
import "github.com/containerd/containerd/api/types/mount.proto";
|
||||
import "github.com/containerd/containerd/api/types/platform.proto";
|
||||
import "github.com/containerd/containerd/api/types/metrics.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/services/sandbox/v1;sandbox";
|
||||
|
||||
// Store provides a metadata storage interface for sandboxes. Similarly to `Containers`,
|
||||
// sandbox object includes info required to start a new instance, but no runtime state.
|
||||
// When running a new sandbox instance, store objects are used as base type to create from.
|
||||
service Store {
|
||||
rpc Create(StoreCreateRequest) returns (StoreCreateResponse);
|
||||
rpc Update(StoreUpdateRequest) returns (StoreUpdateResponse);
|
||||
rpc Delete(StoreDeleteRequest) returns (StoreDeleteResponse);
|
||||
rpc List(StoreListRequest) returns (StoreListResponse);
|
||||
rpc Get(StoreGetRequest) returns (StoreGetResponse);
|
||||
}
|
||||
|
||||
message StoreCreateRequest {
|
||||
containerd.types.Sandbox sandbox = 1;
|
||||
}
|
||||
|
||||
message StoreCreateResponse {
|
||||
containerd.types.Sandbox sandbox = 1;
|
||||
}
|
||||
|
||||
message StoreUpdateRequest {
|
||||
containerd.types.Sandbox sandbox = 1;
|
||||
repeated string fields = 2;
|
||||
}
|
||||
|
||||
message StoreUpdateResponse {
|
||||
containerd.types.Sandbox sandbox = 1;
|
||||
}
|
||||
|
||||
message StoreDeleteRequest {
|
||||
string sandbox_id = 1;
|
||||
}
|
||||
|
||||
message StoreDeleteResponse {}
|
||||
|
||||
message StoreListRequest {
|
||||
repeated string filters = 1;
|
||||
}
|
||||
|
||||
message StoreListResponse {
|
||||
repeated containerd.types.Sandbox list = 1;
|
||||
}
|
||||
|
||||
message StoreGetRequest {
|
||||
string sandbox_id = 1;
|
||||
}
|
||||
|
||||
message StoreGetResponse {
|
||||
containerd.types.Sandbox sandbox = 1;
|
||||
}
|
||||
|
||||
// Controller is an interface to manage runtime sandbox instances.
|
||||
service Controller {
|
||||
rpc Create(ControllerCreateRequest) returns (ControllerCreateResponse);
|
||||
rpc Start(ControllerStartRequest) returns (ControllerStartResponse);
|
||||
rpc Platform(ControllerPlatformRequest) returns (ControllerPlatformResponse);
|
||||
rpc Stop(ControllerStopRequest) returns (ControllerStopResponse);
|
||||
rpc Wait(ControllerWaitRequest) returns (ControllerWaitResponse);
|
||||
rpc Status(ControllerStatusRequest) returns (ControllerStatusResponse);
|
||||
rpc Shutdown(ControllerShutdownRequest) returns (ControllerShutdownResponse);
|
||||
rpc Metrics(ControllerMetricsRequest) returns (ControllerMetricsResponse);
|
||||
rpc Update(ControllerUpdateRequest) returns (ControllerUpdateResponse);
|
||||
}
|
||||
|
||||
message ControllerCreateRequest {
|
||||
string sandbox_id = 1;
|
||||
repeated containerd.types.Mount rootfs = 2;
|
||||
google.protobuf.Any options = 3;
|
||||
string netns_path = 4;
|
||||
map<string, string> annotations = 5;
|
||||
containerd.types.Sandbox sandbox = 6;
|
||||
string sandboxer = 10;
|
||||
}
|
||||
|
||||
message ControllerCreateResponse {
|
||||
string sandbox_id = 1;
|
||||
}
|
||||
|
||||
message ControllerStartRequest {
|
||||
string sandbox_id = 1;
|
||||
string sandboxer = 10;
|
||||
}
|
||||
|
||||
message ControllerStartResponse {
|
||||
string sandbox_id = 1;
|
||||
uint32 pid = 2;
|
||||
google.protobuf.Timestamp created_at = 3;
|
||||
map<string, string> labels = 4;
|
||||
// Address of the sandbox for containerd to connect,
|
||||
// for calling Task or other APIs serving in the sandbox.
|
||||
// it is in the form of ttrpc+unix://path/to/uds or grpc+vsock://<vsock cid>:<port>.
|
||||
string address = 5;
|
||||
uint32 version = 6;
|
||||
}
|
||||
|
||||
message ControllerPlatformRequest {
|
||||
string sandbox_id = 1;
|
||||
string sandboxer = 10;
|
||||
}
|
||||
|
||||
message ControllerPlatformResponse {
|
||||
containerd.types.Platform platform = 1;
|
||||
}
|
||||
|
||||
message ControllerStopRequest {
|
||||
string sandbox_id = 1;
|
||||
uint32 timeout_secs = 2;
|
||||
string sandboxer = 10;
|
||||
}
|
||||
|
||||
message ControllerStopResponse {}
|
||||
|
||||
message ControllerWaitRequest {
|
||||
string sandbox_id = 1;
|
||||
string sandboxer = 10;
|
||||
}
|
||||
|
||||
message ControllerWaitResponse {
|
||||
uint32 exit_status = 1;
|
||||
google.protobuf.Timestamp exited_at = 2;
|
||||
}
|
||||
|
||||
message ControllerStatusRequest {
|
||||
string sandbox_id = 1;
|
||||
bool verbose = 2;
|
||||
string sandboxer = 10;
|
||||
}
|
||||
|
||||
message ControllerStatusResponse {
|
||||
string sandbox_id = 1;
|
||||
uint32 pid = 2;
|
||||
string state = 3;
|
||||
map<string, string> info = 4;
|
||||
google.protobuf.Timestamp created_at = 5;
|
||||
google.protobuf.Timestamp exited_at = 6;
|
||||
google.protobuf.Any extra = 7;
|
||||
// Address of the sandbox for containerd to connect,
|
||||
// for calling Task or other APIs serving in the sandbox.
|
||||
// it is in the form of ttrpc+unix://path/to/uds or grpc+vsock://<vsock cid>:<port>.
|
||||
string address = 8;
|
||||
uint32 version = 9;
|
||||
}
|
||||
|
||||
message ControllerShutdownRequest {
|
||||
string sandbox_id = 1;
|
||||
string sandboxer = 10;
|
||||
}
|
||||
|
||||
message ControllerShutdownResponse {}
|
||||
|
||||
message ControllerMetricsRequest {
|
||||
string sandbox_id = 1;
|
||||
string sandboxer = 10;
|
||||
}
|
||||
|
||||
message ControllerMetricsResponse {
|
||||
types.Metric metrics = 1;
|
||||
}
|
||||
|
||||
message ControllerUpdateRequest {
|
||||
string sandbox_id = 1;
|
||||
string sandboxer = 2;
|
||||
containerd.types.Sandbox sandbox = 3;
|
||||
repeated string fields = 4;
|
||||
}
|
||||
|
||||
message ControllerUpdateResponse {
|
||||
}
|
179
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/snapshots/v1/snapshots.proto
generated
vendored
Normal file
179
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/snapshots/v1/snapshots.proto
generated
vendored
Normal file
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.services.snapshots.v1;
|
||||
|
||||
import "google/protobuf/empty.proto";
|
||||
import "google/protobuf/field_mask.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "github.com/containerd/containerd/api/types/mount.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/services/snapshots/v1;snapshots";
|
||||
|
||||
// Snapshot service manages snapshots
|
||||
service Snapshots {
|
||||
rpc Prepare(PrepareSnapshotRequest) returns (PrepareSnapshotResponse);
|
||||
rpc View(ViewSnapshotRequest) returns (ViewSnapshotResponse);
|
||||
rpc Mounts(MountsRequest) returns (MountsResponse);
|
||||
rpc Commit(CommitSnapshotRequest) returns (google.protobuf.Empty);
|
||||
rpc Remove(RemoveSnapshotRequest) returns (google.protobuf.Empty);
|
||||
rpc Stat(StatSnapshotRequest) returns (StatSnapshotResponse);
|
||||
rpc Update(UpdateSnapshotRequest) returns (UpdateSnapshotResponse);
|
||||
rpc List(ListSnapshotsRequest) returns (stream ListSnapshotsResponse);
|
||||
rpc Usage(UsageRequest) returns (UsageResponse);
|
||||
rpc Cleanup(CleanupRequest) returns (google.protobuf.Empty);
|
||||
}
|
||||
|
||||
message PrepareSnapshotRequest {
|
||||
string snapshotter = 1;
|
||||
string key = 2;
|
||||
string parent = 3;
|
||||
|
||||
// Labels are arbitrary data on snapshots.
|
||||
//
|
||||
// The combined size of a key/value pair cannot exceed 4096 bytes.
|
||||
map<string, string> labels = 4;
|
||||
}
|
||||
|
||||
message PrepareSnapshotResponse {
|
||||
repeated containerd.types.Mount mounts = 1;
|
||||
}
|
||||
|
||||
message ViewSnapshotRequest {
|
||||
string snapshotter = 1;
|
||||
string key = 2;
|
||||
string parent = 3;
|
||||
|
||||
// Labels are arbitrary data on snapshots.
|
||||
//
|
||||
// The combined size of a key/value pair cannot exceed 4096 bytes.
|
||||
map<string, string> labels = 4;
|
||||
}
|
||||
|
||||
message ViewSnapshotResponse {
|
||||
repeated containerd.types.Mount mounts = 1;
|
||||
}
|
||||
|
||||
message MountsRequest {
|
||||
string snapshotter = 1;
|
||||
string key = 2;
|
||||
}
|
||||
|
||||
message MountsResponse {
|
||||
repeated containerd.types.Mount mounts = 1;
|
||||
}
|
||||
|
||||
message RemoveSnapshotRequest {
|
||||
string snapshotter = 1;
|
||||
string key = 2;
|
||||
}
|
||||
|
||||
message CommitSnapshotRequest {
|
||||
string snapshotter = 1;
|
||||
string name = 2;
|
||||
string key = 3;
|
||||
|
||||
// Labels are arbitrary data on snapshots.
|
||||
//
|
||||
// The combined size of a key/value pair cannot exceed 4096 bytes.
|
||||
map<string, string> labels = 4;
|
||||
}
|
||||
|
||||
message StatSnapshotRequest {
|
||||
string snapshotter = 1;
|
||||
string key = 2;
|
||||
}
|
||||
|
||||
enum Kind {
|
||||
UNKNOWN = 0;
|
||||
VIEW = 1;
|
||||
ACTIVE = 2;
|
||||
COMMITTED = 3;
|
||||
}
|
||||
|
||||
message Info {
|
||||
string name = 1;
|
||||
string parent = 2;
|
||||
Kind kind = 3;
|
||||
|
||||
// CreatedAt provides the time at which the snapshot was created.
|
||||
google.protobuf.Timestamp created_at = 4;
|
||||
|
||||
// UpdatedAt provides the time the info was last updated.
|
||||
google.protobuf.Timestamp updated_at = 5;
|
||||
|
||||
// Labels are arbitrary data on snapshots.
|
||||
//
|
||||
// The combined size of a key/value pair cannot exceed 4096 bytes.
|
||||
map<string, string> labels = 6;
|
||||
}
|
||||
|
||||
message StatSnapshotResponse {
|
||||
Info info = 1;
|
||||
}
|
||||
|
||||
message UpdateSnapshotRequest {
|
||||
string snapshotter = 1;
|
||||
Info info = 2;
|
||||
|
||||
// UpdateMask specifies which fields to perform the update on. If empty,
|
||||
// the operation applies to all fields.
|
||||
//
|
||||
// In info, Name, Parent, Kind, Created are immutable,
|
||||
// other field may be updated using this mask.
|
||||
// If no mask is provided, all mutable field are updated.
|
||||
google.protobuf.FieldMask update_mask = 3;
|
||||
}
|
||||
|
||||
message UpdateSnapshotResponse {
|
||||
Info info = 1;
|
||||
}
|
||||
|
||||
message ListSnapshotsRequest{
|
||||
string snapshotter = 1;
|
||||
|
||||
// Filters contains one or more filters using the syntax defined in the
|
||||
// containerd filter package.
|
||||
//
|
||||
// The returned result will be those that match any of the provided
|
||||
// filters. Expanded, images that match the following will be
|
||||
// returned:
|
||||
//
|
||||
// filters[0] or filters[1] or ... or filters[n-1] or filters[n]
|
||||
//
|
||||
// If filters is zero-length or nil, all items will be returned.
|
||||
repeated string filters = 2;
|
||||
}
|
||||
|
||||
message ListSnapshotsResponse {
|
||||
repeated Info info = 1;
|
||||
}
|
||||
|
||||
message UsageRequest {
|
||||
string snapshotter = 1;
|
||||
string key = 2;
|
||||
}
|
||||
|
||||
message UsageResponse {
|
||||
int64 size = 1;
|
||||
int64 inodes = 2;
|
||||
}
|
||||
|
||||
message CleanupRequest {
|
||||
string snapshotter = 1;
|
||||
}
|
31
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/streaming/v1/streaming.proto
generated
vendored
Normal file
31
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/streaming/v1/streaming.proto
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.services.streaming.v1;
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/services/streaming/v1;streaming";
|
||||
|
||||
service Streaming {
|
||||
rpc Stream(stream google.protobuf.Any) returns (stream google.protobuf.Any);
|
||||
}
|
||||
|
||||
message StreamInit {
|
||||
string id = 1;
|
||||
}
|
227
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/tasks/v1/tasks.proto
generated
vendored
Normal file
227
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/tasks/v1/tasks.proto
generated
vendored
Normal file
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.services.tasks.v1;
|
||||
|
||||
import "google/protobuf/empty.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
import "github.com/containerd/containerd/api/types/mount.proto";
|
||||
import "github.com/containerd/containerd/api/types/metrics.proto";
|
||||
import "github.com/containerd/containerd/api/types/descriptor.proto";
|
||||
import "github.com/containerd/containerd/api/types/task/task.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/services/tasks/v1;tasks";
|
||||
|
||||
service Tasks {
|
||||
// Create a task.
|
||||
rpc Create(CreateTaskRequest) returns (CreateTaskResponse);
|
||||
|
||||
// Start a process.
|
||||
rpc Start(StartRequest) returns (StartResponse);
|
||||
|
||||
// Delete a task and on disk state.
|
||||
rpc Delete(DeleteTaskRequest) returns (DeleteResponse);
|
||||
|
||||
rpc DeleteProcess(DeleteProcessRequest) returns (DeleteResponse);
|
||||
|
||||
rpc Get(GetRequest) returns (GetResponse);
|
||||
|
||||
rpc List(ListTasksRequest) returns (ListTasksResponse);
|
||||
|
||||
// Kill a task or process.
|
||||
rpc Kill(KillRequest) returns (google.protobuf.Empty);
|
||||
|
||||
rpc Exec(ExecProcessRequest) returns (google.protobuf.Empty);
|
||||
|
||||
rpc ResizePty(ResizePtyRequest) returns (google.protobuf.Empty);
|
||||
|
||||
rpc CloseIO(CloseIORequest) returns (google.protobuf.Empty);
|
||||
|
||||
rpc Pause(PauseTaskRequest) returns (google.protobuf.Empty);
|
||||
|
||||
rpc Resume(ResumeTaskRequest) returns (google.protobuf.Empty);
|
||||
|
||||
rpc ListPids(ListPidsRequest) returns (ListPidsResponse);
|
||||
|
||||
rpc Checkpoint(CheckpointTaskRequest) returns (CheckpointTaskResponse);
|
||||
|
||||
rpc Update(UpdateTaskRequest) returns (google.protobuf.Empty);
|
||||
|
||||
rpc Metrics(MetricsRequest) returns (MetricsResponse);
|
||||
|
||||
rpc Wait(WaitRequest) returns (WaitResponse);
|
||||
}
|
||||
|
||||
message CreateTaskRequest {
|
||||
string container_id = 1;
|
||||
|
||||
// RootFS provides the pre-chroot mounts to perform in the shim before
|
||||
// executing the container task.
|
||||
//
|
||||
// These are for mounts that cannot be performed in the user namespace.
|
||||
// Typically, these mounts should be resolved from snapshots specified on
|
||||
// the container object.
|
||||
repeated containerd.types.Mount rootfs = 3;
|
||||
|
||||
string stdin = 4;
|
||||
string stdout = 5;
|
||||
string stderr = 6;
|
||||
bool terminal = 7;
|
||||
|
||||
containerd.types.Descriptor checkpoint = 8;
|
||||
|
||||
google.protobuf.Any options = 9;
|
||||
|
||||
string runtime_path = 10;
|
||||
}
|
||||
|
||||
message CreateTaskResponse {
|
||||
string container_id = 1;
|
||||
uint32 pid = 2;
|
||||
}
|
||||
|
||||
message StartRequest {
|
||||
string container_id = 1;
|
||||
string exec_id = 2;
|
||||
}
|
||||
|
||||
message StartResponse {
|
||||
uint32 pid = 1;
|
||||
}
|
||||
|
||||
message DeleteTaskRequest {
|
||||
string container_id = 1;
|
||||
}
|
||||
|
||||
message DeleteResponse {
|
||||
string id = 1;
|
||||
uint32 pid = 2;
|
||||
uint32 exit_status = 3;
|
||||
google.protobuf.Timestamp exited_at = 4;
|
||||
}
|
||||
|
||||
message DeleteProcessRequest {
|
||||
string container_id = 1;
|
||||
string exec_id = 2;
|
||||
}
|
||||
|
||||
message GetRequest {
|
||||
string container_id = 1;
|
||||
string exec_id = 2;
|
||||
}
|
||||
|
||||
message GetResponse {
|
||||
containerd.v1.types.Process process = 1;
|
||||
}
|
||||
|
||||
message ListTasksRequest {
|
||||
string filter = 1;
|
||||
}
|
||||
|
||||
message ListTasksResponse {
|
||||
repeated containerd.v1.types.Process tasks = 1;
|
||||
}
|
||||
|
||||
message KillRequest {
|
||||
string container_id = 1;
|
||||
string exec_id = 2;
|
||||
uint32 signal = 3;
|
||||
bool all = 4;
|
||||
}
|
||||
|
||||
message ExecProcessRequest {
|
||||
string container_id = 1;
|
||||
string stdin = 2;
|
||||
string stdout = 3;
|
||||
string stderr = 4;
|
||||
bool terminal = 5;
|
||||
// Spec for starting a process in the target container.
|
||||
//
|
||||
// For runc, this is a process spec, for example.
|
||||
google.protobuf.Any spec = 6;
|
||||
// id of the exec process
|
||||
string exec_id = 7;
|
||||
}
|
||||
|
||||
message ExecProcessResponse {
|
||||
}
|
||||
|
||||
message ResizePtyRequest {
|
||||
string container_id = 1;
|
||||
string exec_id = 2;
|
||||
uint32 width = 3;
|
||||
uint32 height = 4;
|
||||
}
|
||||
|
||||
message CloseIORequest {
|
||||
string container_id = 1;
|
||||
string exec_id = 2;
|
||||
bool stdin = 3;
|
||||
}
|
||||
|
||||
message PauseTaskRequest {
|
||||
string container_id = 1;
|
||||
}
|
||||
|
||||
message ResumeTaskRequest {
|
||||
string container_id = 1;
|
||||
}
|
||||
|
||||
message ListPidsRequest {
|
||||
string container_id = 1;
|
||||
}
|
||||
|
||||
message ListPidsResponse {
|
||||
// Processes includes the process ID and additional process information
|
||||
repeated containerd.v1.types.ProcessInfo processes = 1;
|
||||
}
|
||||
|
||||
message CheckpointTaskRequest {
|
||||
string container_id = 1;
|
||||
string parent_checkpoint = 2;
|
||||
google.protobuf.Any options = 3;
|
||||
}
|
||||
|
||||
message CheckpointTaskResponse {
|
||||
repeated containerd.types.Descriptor descriptors = 1;
|
||||
}
|
||||
|
||||
message UpdateTaskRequest {
|
||||
string container_id = 1;
|
||||
google.protobuf.Any resources = 2;
|
||||
map<string, string> annotations = 3;
|
||||
}
|
||||
|
||||
message MetricsRequest {
|
||||
repeated string filters = 1;
|
||||
}
|
||||
|
||||
message MetricsResponse {
|
||||
repeated types.Metric metrics = 1;
|
||||
}
|
||||
|
||||
message WaitRequest {
|
||||
string container_id = 1;
|
||||
string exec_id = 2;
|
||||
}
|
||||
|
||||
message WaitResponse {
|
||||
uint32 exit_status = 1;
|
||||
google.protobuf.Timestamp exited_at = 2;
|
||||
}
|
39
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/transfer/v1/transfer.proto
generated
vendored
Normal file
39
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/transfer/v1/transfer.proto
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.services.transfer.v1;
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/services/transfer/v1;transfer";
|
||||
|
||||
service Transfer {
|
||||
rpc Transfer(TransferRequest) returns (google.protobuf.Empty);
|
||||
}
|
||||
|
||||
message TransferRequest {
|
||||
google.protobuf.Any source = 1;
|
||||
google.protobuf.Any destination = 2;
|
||||
TransferOptions options = 3;
|
||||
}
|
||||
|
||||
message TransferOptions {
|
||||
string progress_stream = 1;
|
||||
// Progress min interval
|
||||
}
|
37
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/ttrpc/events/v1/events.proto
generated
vendored
Normal file
37
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/ttrpc/events/v1/events.proto
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.services.events.ttrpc.v1;
|
||||
|
||||
import "github.com/containerd/containerd/api/types/event.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/services/ttrpc/events/v1;events";
|
||||
|
||||
service Events {
|
||||
// Forward sends an event that has already been packaged into an envelope
|
||||
// with a timestamp and namespace.
|
||||
//
|
||||
// This is useful if earlier timestamping is required or when forwarding on
|
||||
// behalf of another component, namespace or publisher.
|
||||
rpc Forward(ForwardRequest) returns (google.protobuf.Empty);
|
||||
}
|
||||
|
||||
message ForwardRequest {
|
||||
containerd.types.Envelope envelope = 1;
|
||||
}
|
33
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/version/v1/version.proto
generated
vendored
Normal file
33
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/services/version/v1/version.proto
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.services.version.v1;
|
||||
|
||||
import "google/protobuf/empty.proto";
|
||||
|
||||
// TODO(stevvooe): Should version service actually be versioned?
|
||||
option go_package = "github.com/containerd/containerd/api/services/version/v1;version";
|
||||
|
||||
service Version {
|
||||
rpc Version(google.protobuf.Empty) returns (VersionResponse);
|
||||
}
|
||||
|
||||
message VersionResponse {
|
||||
string version = 1;
|
||||
string revision = 2;
|
||||
}
|
33
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/descriptor.proto
generated
vendored
Normal file
33
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/descriptor.proto
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.types;
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/types;types";
|
||||
|
||||
// Descriptor describes a blob in a content store.
|
||||
//
|
||||
// This descriptor can be used to reference content from an
|
||||
// oci descriptor found in a manifest.
|
||||
// See https://godoc.org/github.com/opencontainers/image-spec/specs-go/v1#Descriptor
|
||||
message Descriptor {
|
||||
string media_type = 1;
|
||||
string digest = 2;
|
||||
int64 size = 3;
|
||||
map<string, string> annotations = 5;
|
||||
}
|
33
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/event.proto
generated
vendored
Normal file
33
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/event.proto
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.types;
|
||||
|
||||
import "github.com/containerd/containerd/api/types/fieldpath.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/types;types";
|
||||
|
||||
message Envelope {
|
||||
option (containerd.types.fieldpath) = true;
|
||||
google.protobuf.Timestamp timestamp = 1;
|
||||
string namespace = 2;
|
||||
string topic = 3;
|
||||
google.protobuf.Any event = 4;
|
||||
}
|
42
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/fieldpath.proto
generated
vendored
Normal file
42
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/fieldpath.proto
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Protocol Buffers for Go with Gadgets
|
||||
//
|
||||
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
||||
// http://github.com/gogo/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
syntax = "proto3";
|
||||
package containerd.types;
|
||||
|
||||
import "google/protobuf/descriptor.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/types;types";
|
||||
|
||||
extend google.protobuf.FileOptions {
|
||||
optional bool fieldpath_all = 63300;
|
||||
}
|
||||
|
||||
extend google.protobuf.MessageOptions {
|
||||
optional bool fieldpath = 64400;
|
||||
}
|
46
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/introspection.proto
generated
vendored
Normal file
46
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/introspection.proto
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.types;
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/types;types";
|
||||
|
||||
message RuntimeRequest {
|
||||
string runtime_path = 1;
|
||||
// Options correspond to CreateTaskRequest.options.
|
||||
// This is needed to pass the runc binary path, etc.
|
||||
google.protobuf.Any options = 2;
|
||||
}
|
||||
|
||||
message RuntimeVersion {
|
||||
string version = 1;
|
||||
string revision = 2;
|
||||
}
|
||||
|
||||
message RuntimeInfo {
|
||||
string name = 1;
|
||||
RuntimeVersion version = 2;
|
||||
// Options correspond to RuntimeInfoRequest.Options (contains runc binary path, etc.)
|
||||
google.protobuf.Any options = 3;
|
||||
// OCI-compatible runtimes should use https://github.com/opencontainers/runtime-spec/blob/main/features.md
|
||||
google.protobuf.Any features = 4;
|
||||
// Annotations of the shim. Irrelevant to features.Annotations.
|
||||
map<string, string> annotations = 5;
|
||||
}
|
30
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/metrics.proto
generated
vendored
Normal file
30
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/metrics.proto
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.types;
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/types;types";
|
||||
|
||||
message Metric {
|
||||
google.protobuf.Timestamp timestamp = 1;
|
||||
string id = 2;
|
||||
google.protobuf.Any data = 3;
|
||||
}
|
43
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/mount.proto
generated
vendored
Normal file
43
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/mount.proto
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.types;
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/types;types";
|
||||
|
||||
// Mount describes mounts for a container.
|
||||
//
|
||||
// This type is the lingua franca of ContainerD. All services provide mounts
|
||||
// to be used with the container at creation time.
|
||||
//
|
||||
// The Mount type follows the structure of the mount syscall, including a type,
|
||||
// source, target and options.
|
||||
message Mount {
|
||||
// Type defines the nature of the mount.
|
||||
string type = 1;
|
||||
|
||||
// Source specifies the name of the mount. Depending on mount type, this
|
||||
// may be a volume name or a host path, or even ignored.
|
||||
string source = 2;
|
||||
|
||||
// Target path in container
|
||||
string target = 3;
|
||||
|
||||
// Options specifies zero or more fstab style mount options.
|
||||
repeated string options = 4;
|
||||
}
|
30
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/platform.proto
generated
vendored
Normal file
30
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/platform.proto
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.types;
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/types;types";
|
||||
|
||||
// Platform follows the structure of the OCI platform specification, from
|
||||
// descriptors.
|
||||
message Platform {
|
||||
string os = 1;
|
||||
string architecture = 2;
|
||||
string variant = 3;
|
||||
string os_version = 4;
|
||||
}
|
63
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/runc/options/oci.proto
generated
vendored
Normal file
63
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/runc/options/oci.proto
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package containerd.runc.v1;
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/types/runc/options;options";
|
||||
|
||||
message Options {
|
||||
// disable pivot root when creating a container
|
||||
bool no_pivot_root = 1;
|
||||
// create a new keyring for the container
|
||||
bool no_new_keyring = 2;
|
||||
// place the shim in a cgroup
|
||||
string shim_cgroup = 3;
|
||||
// set the I/O's pipes uid
|
||||
uint32 io_uid = 4;
|
||||
// set the I/O's pipes gid
|
||||
uint32 io_gid = 5;
|
||||
// binary name of the runc binary
|
||||
string binary_name = 6;
|
||||
// runc root directory
|
||||
string root = 7;
|
||||
// criu binary path.
|
||||
//
|
||||
// Removed in containerd v2.0: string criu_path = 8;
|
||||
reserved 8;
|
||||
// enable systemd cgroups
|
||||
bool systemd_cgroup = 9;
|
||||
// criu image path
|
||||
string criu_image_path = 10;
|
||||
// criu work path
|
||||
string criu_work_path = 11;
|
||||
// task api address, can be a unix domain socket, or vsock address.
|
||||
// it is in the form of ttrpc+unix://path/to/uds or grpc+vsock://<vsock cid>:<port>.
|
||||
string task_api_address = 12;
|
||||
// task api version, currently supported value is 2 and 3.
|
||||
uint32 task_api_version = 13;
|
||||
}
|
||||
|
||||
message CheckpointOptions {
|
||||
// exit the container after a checkpoint
|
||||
bool exit = 1;
|
||||
// checkpoint open tcp connections
|
||||
bool open_tcp = 2;
|
||||
// checkpoint external unix sockets
|
||||
bool external_unix_sockets = 3;
|
||||
// checkpoint terminals (ptys)
|
||||
bool terminal = 4;
|
||||
// allow checkpointing of file locks
|
||||
bool file_locks = 5;
|
||||
// restore provided namespaces as empty namespaces
|
||||
repeated string empty_namespaces = 6;
|
||||
// set the cgroups mode, soft, full, strict
|
||||
string cgroups_mode = 7;
|
||||
// checkpoint image path
|
||||
string image_path = 8;
|
||||
// checkpoint work path
|
||||
string work_path = 9;
|
||||
}
|
||||
|
||||
message ProcessDetails {
|
||||
// exec process id if the process is managed by a shim
|
||||
string exec_id = 1;
|
||||
}
|
17
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/runtimeoptions/v1/api.proto
generated
vendored
Normal file
17
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/runtimeoptions/v1/api.proto
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
// To regenerate api.pb.go run `make protos`
|
||||
syntax = "proto3";
|
||||
|
||||
package runtimeoptions.v1;
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/types/runtimeoptions/v1;runtimeoptions";
|
||||
|
||||
message Options {
|
||||
// TypeUrl specifies the type of the content inside the config file.
|
||||
string type_url = 1;
|
||||
// ConfigPath specifies the filesystem location of the config file
|
||||
// used by the runtime.
|
||||
string config_path = 2;
|
||||
// Blob specifies an in-memory TOML blob passed from containerd's configuration section
|
||||
// for this runtime. This will be used if config_path is not specified.
|
||||
bytes config_body = 3;
|
||||
}
|
54
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/sandbox.proto
generated
vendored
Normal file
54
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/sandbox.proto
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.types;
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/types;types";
|
||||
|
||||
// Sandbox represents a sandbox metadata object that keeps all info required by controller to
|
||||
// work with a particular instance.
|
||||
message Sandbox {
|
||||
// SandboxID is a unique instance identifier within namespace
|
||||
string sandbox_id = 1;
|
||||
message Runtime {
|
||||
// Name is the name of the runtime.
|
||||
string name = 1;
|
||||
// Options specify additional runtime initialization options for the shim (this data will be available in StartShim).
|
||||
// Typically this data expected to be runtime shim implementation specific.
|
||||
google.protobuf.Any options = 2;
|
||||
}
|
||||
// Runtime specifies which runtime to use for executing this container.
|
||||
Runtime runtime = 2;
|
||||
// Spec is sandbox configuration (kin of OCI runtime spec), spec's data will be written to a config.json file in the
|
||||
// bundle directory (similary to OCI spec).
|
||||
google.protobuf.Any spec = 3;
|
||||
// Labels provides an area to include arbitrary data on containers.
|
||||
map<string, string> labels = 4;
|
||||
// CreatedAt is the time the container was first created.
|
||||
google.protobuf.Timestamp created_at = 5;
|
||||
// UpdatedAt is the last time the container was mutated.
|
||||
google.protobuf.Timestamp updated_at = 6;
|
||||
// Extensions allow clients to provide optional blobs that can be handled by runtime.
|
||||
map<string, google.protobuf.Any> extensions = 7;
|
||||
// Sandboxer is the name of the sandbox controller who manages the sandbox.
|
||||
string sandboxer = 10;
|
||||
|
||||
}
|
55
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/task/task.proto
generated
vendored
Normal file
55
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/task/task.proto
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.v1.types;
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/types/task";
|
||||
|
||||
enum Status {
|
||||
UNKNOWN = 0;
|
||||
CREATED = 1;
|
||||
RUNNING = 2;
|
||||
STOPPED = 3;
|
||||
PAUSED = 4;
|
||||
PAUSING = 5;
|
||||
}
|
||||
|
||||
message Process {
|
||||
string container_id = 1;
|
||||
string id = 2;
|
||||
uint32 pid = 3;
|
||||
Status status = 4;
|
||||
string stdin = 5;
|
||||
string stdout = 6;
|
||||
string stderr = 7;
|
||||
bool terminal = 8;
|
||||
uint32 exit_status = 9;
|
||||
google.protobuf.Timestamp exited_at = 10;
|
||||
}
|
||||
|
||||
message ProcessInfo {
|
||||
// PID is the process ID.
|
||||
uint32 pid = 1;
|
||||
// Info contains additional process information.
|
||||
//
|
||||
// Info varies by platform.
|
||||
google.protobuf.Any info = 2;
|
||||
}
|
82
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/transfer/imagestore.proto
generated
vendored
Normal file
82
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/transfer/imagestore.proto
generated
vendored
Normal file
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.types.transfer;
|
||||
|
||||
import "github.com/containerd/containerd/api/types/platform.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/types/transfer";
|
||||
|
||||
message ImageStore {
|
||||
string name = 1;
|
||||
map<string, string> labels = 2;
|
||||
|
||||
// Content filters
|
||||
|
||||
repeated types.Platform platforms = 3;
|
||||
bool all_metadata = 4;
|
||||
uint32 manifest_limit = 5;
|
||||
|
||||
// Import naming
|
||||
|
||||
// extra_references are used to set image names on imports of sub-images from the index
|
||||
repeated ImageReference extra_references = 6;
|
||||
|
||||
// Unpack Configuration, multiple allowed
|
||||
|
||||
repeated UnpackConfiguration unpacks = 10;
|
||||
}
|
||||
|
||||
message UnpackConfiguration {
|
||||
// platform is the platform to unpack for, used for resolving manifest and snapshotter
|
||||
// if not provided
|
||||
types.Platform platform = 1;
|
||||
|
||||
// snapshotter to unpack to, if not provided default for platform shoudl be used
|
||||
string snapshotter = 2;
|
||||
}
|
||||
|
||||
// ImageReference is used to create or find a reference for an image
|
||||
message ImageReference {
|
||||
string name = 1;
|
||||
|
||||
// is_prefix determines whether the Name should be considered
|
||||
// a prefix (without tag or digest).
|
||||
// For lookup, this may allow matching multiple tags.
|
||||
// For store, this must have a tag or digest added.
|
||||
bool is_prefix = 2;
|
||||
|
||||
// allow_overwrite allows overwriting or ignoring the name if
|
||||
// another reference is provided (such as through an annotation).
|
||||
// Only used if IsPrefix is true.
|
||||
bool allow_overwrite = 3;
|
||||
|
||||
// add_digest adds the manifest digest to the reference.
|
||||
// For lookup, this allows matching tags with any digest.
|
||||
// For store, this allows adding the digest to the name.
|
||||
// Only used if IsPrefix is true.
|
||||
bool add_digest = 4;
|
||||
|
||||
// skip_named_digest only considers digest references which do not
|
||||
// have a non-digested named reference.
|
||||
// For lookup, this will deduplicate digest references when there is a named match.
|
||||
// For store, this only adds this digest reference when there is no matching full
|
||||
// name reference from the prefix.
|
||||
// Only used if IsPrefix is true.
|
||||
bool skip_named_digest = 5;
|
||||
}
|
52
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/transfer/importexport.proto
generated
vendored
Normal file
52
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/transfer/importexport.proto
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.types.transfer;
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/types/transfer";
|
||||
|
||||
import "github.com/containerd/containerd/api/types/platform.proto";
|
||||
|
||||
message ImageImportStream {
|
||||
// Stream is used to identify the binary input stream for the import operation.
|
||||
// The stream uses the transfer binary stream protocol with the client as the sender.
|
||||
// The binary data is expected to be a raw tar stream.
|
||||
string stream = 1;
|
||||
|
||||
string media_type = 2;
|
||||
|
||||
bool force_compress = 3;
|
||||
}
|
||||
|
||||
message ImageExportStream {
|
||||
// Stream is used to identify the binary output stream for the export operation.
|
||||
// The stream uses the transfer binary stream protocol with the server as the sender.
|
||||
// The binary data is expected to be a raw tar stream.
|
||||
string stream = 1;
|
||||
|
||||
string media_type = 2;
|
||||
|
||||
// The specified platforms
|
||||
repeated types.Platform platforms = 3;
|
||||
// Whether to include all platforms
|
||||
bool all_platforms = 4;
|
||||
// Skips the creation of the Docker compatible manifest.json file
|
||||
bool skip_compatibility_manifest = 5;
|
||||
// Excludes non-distributable blobs such as Windows base layers.
|
||||
bool skip_non_distributable = 6;
|
||||
}
|
32
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/transfer/progress.proto
generated
vendored
Normal file
32
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/transfer/progress.proto
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.types.transfer;
|
||||
|
||||
import "github.com/containerd/containerd/api/types/descriptor.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/types/transfer";
|
||||
|
||||
message Progress {
|
||||
string event = 1;
|
||||
string name = 2;
|
||||
repeated string parents = 3;
|
||||
int64 progress = 4;
|
||||
int64 total = 5;
|
||||
containerd.types.Descriptor desc = 6;
|
||||
}
|
97
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/transfer/registry.proto
generated
vendored
Normal file
97
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/transfer/registry.proto
generated
vendored
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.types.transfer;
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/types/transfer";
|
||||
|
||||
message OCIRegistry {
|
||||
string reference = 1;
|
||||
RegistryResolver resolver = 2;
|
||||
}
|
||||
|
||||
enum HTTPDebug {
|
||||
DISABLED = 0;
|
||||
// Enable HTTP debugging
|
||||
DEBUG = 1;
|
||||
// Enable HTTP requests tracing
|
||||
TRACE = 2;
|
||||
// Enable both HTTP debugging and requests tracing
|
||||
BOTH = 3;
|
||||
}
|
||||
|
||||
message RegistryResolver {
|
||||
// auth_stream is used to refer to a stream which auth callbacks may be
|
||||
// made on.
|
||||
string auth_stream = 1;
|
||||
|
||||
// Headers
|
||||
map<string, string> headers = 2;
|
||||
|
||||
string host_dir = 3;
|
||||
|
||||
string default_scheme = 4;
|
||||
// Force skip verify
|
||||
// CA callback? Client TLS callback?
|
||||
|
||||
// Whether to debug/trace HTTP requests to OCI registry.
|
||||
HTTPDebug http_debug = 5;
|
||||
|
||||
// Stream ID to use for HTTP logs (when logs are streamed to client).
|
||||
// When empty, logs are written to containerd logs.
|
||||
string logs_stream = 6;
|
||||
}
|
||||
|
||||
// AuthRequest is sent as a callback on a stream
|
||||
message AuthRequest {
|
||||
// host is the registry host
|
||||
string host = 1;
|
||||
|
||||
// reference is the namespace and repository name requested from the registry
|
||||
string reference = 2;
|
||||
|
||||
// wwwauthenticate is the HTTP WWW-Authenticate header values returned from the registry
|
||||
repeated string wwwauthenticate = 3;
|
||||
}
|
||||
|
||||
enum AuthType {
|
||||
NONE = 0;
|
||||
|
||||
// CREDENTIALS is used to exchange username/password for access token
|
||||
// using an oauth or "Docker Registry Token" server
|
||||
CREDENTIALS = 1;
|
||||
|
||||
// REFRESH is used to exchange secret for access token using an oauth
|
||||
// or "Docker Registry Token" server
|
||||
REFRESH = 2;
|
||||
|
||||
// HEADER is used to set the HTTP Authorization header to secret
|
||||
// directly for the registry.
|
||||
// Value should be `<auth-scheme> <authorization-parameters>`
|
||||
HEADER = 3;
|
||||
}
|
||||
|
||||
message AuthResponse {
|
||||
AuthType authType = 1;
|
||||
string secret = 2;
|
||||
string username = 3;
|
||||
google.protobuf.Timestamp expire_at = 4;
|
||||
// TODO: Stream error
|
||||
}
|
29
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/transfer/streaming.proto
generated
vendored
Normal file
29
crates/client/vendor/github.com/containerd/containerd/vendor/github.com/containerd/containerd/api/types/transfer/streaming.proto
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.types.transfer;
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/types/transfer";
|
||||
|
||||
message Data {
|
||||
bytes data = 1;
|
||||
}
|
||||
|
||||
message WindowUpdate {
|
||||
int32 update = 1;
|
||||
}
|
|
@ -25,18 +25,17 @@ path = "src/main.rs"
|
|||
doc = false
|
||||
|
||||
[dependencies]
|
||||
containerd-shim = { path = "../shim", version = "0.7.1", features = ["async"] }
|
||||
containerd-shim = { path = "../shim", version = "0.8.0", features = ["async"] }
|
||||
libc.workspace = true
|
||||
log.workspace = true
|
||||
nix = { workspace = true, features = ["socket", "uio", "term"] }
|
||||
oci-spec.workspace = true
|
||||
prctl.workspace = true
|
||||
runc = { path = "../runc", version = "0.2.0", features = ["async"] }
|
||||
runc = { path = "../runc", version = "0.3.0", features = ["async"] }
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
time.workspace = true
|
||||
uuid.workspace = true
|
||||
|
||||
# Async dependencies
|
||||
async-trait.workspace = true
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
|
@ -44,3 +43,4 @@ tokio = { workspace = true, features = ["full"] }
|
|||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
cgroups-rs.workspace = true
|
||||
nix = { workspace = true, features = ["event"] }
|
||||
tokio-eventfd = "0.2.1"
|
||||
|
|
|
@ -16,21 +16,18 @@
|
|||
|
||||
#![cfg(target_os = "linux")]
|
||||
|
||||
use std::{
|
||||
os::unix::io::{AsRawFd, FromRawFd},
|
||||
path::Path,
|
||||
};
|
||||
use std::{os::unix::io::AsRawFd, path::Path};
|
||||
|
||||
use containerd_shim::{
|
||||
error::{Error, Result},
|
||||
io_error, other_error,
|
||||
};
|
||||
use nix::sys::eventfd::{EfdFlags, EventFd};
|
||||
use tokio::{
|
||||
fs::{self, read_to_string, File},
|
||||
fs::{self, read_to_string},
|
||||
io::AsyncReadExt,
|
||||
sync::mpsc::{self, Receiver},
|
||||
};
|
||||
use tokio_eventfd::EventFd;
|
||||
|
||||
pub async fn get_path_from_cgorup(pid: u32) -> Result<String> {
|
||||
let proc_path = format!("/proc/{}/cgroup", pid);
|
||||
|
@ -93,15 +90,13 @@ pub async fn register_memory_event(
|
|||
let path = cg_dir.join(event_name);
|
||||
let event_file = fs::File::open(path.clone())
|
||||
.await
|
||||
.map_err(other_error!(e, "Error get path:"))?;
|
||||
|
||||
let eventfd = EventFd::from_value_and_flags(0, EfdFlags::EFD_CLOEXEC)?;
|
||||
|
||||
.map_err(other_error!("Error get path:"))?;
|
||||
let mut eventfd = EventFd::new(0, false).map_err(other_error!("Error create eventfd:"))?;
|
||||
let event_control_path = cg_dir.join("cgroup.event_control");
|
||||
let data = format!("{} {}", eventfd.as_raw_fd(), event_file.as_raw_fd());
|
||||
fs::write(&event_control_path, data.clone())
|
||||
.await
|
||||
.map_err(other_error!(e, "Error write eventfd:"))?;
|
||||
.map_err(other_error!("Error write eventfd:"))?;
|
||||
|
||||
let mut buf = [0u8; 8];
|
||||
|
||||
|
@ -109,9 +104,8 @@ pub async fn register_memory_event(
|
|||
let key = key.to_string();
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut eventfd_file = unsafe { File::from_raw_fd(eventfd.as_raw_fd()) };
|
||||
loop {
|
||||
match eventfd_file.read(&mut buf).await {
|
||||
match eventfd.read(&mut buf).await {
|
||||
Ok(0) => return,
|
||||
Err(_) => return,
|
||||
_ => (),
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
use std::{
|
||||
env,
|
||||
fs::File,
|
||||
future::Future,
|
||||
io::IoSliceMut,
|
||||
ops::Deref,
|
||||
os::{
|
||||
|
@ -25,6 +25,7 @@ use std::{
|
|||
},
|
||||
path::Path,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use containerd_shim::{
|
||||
|
@ -59,6 +60,8 @@ pub const INIT_PID_FILE: &str = "init.pid";
|
|||
pub const LOG_JSON_FILE: &str = "log.json";
|
||||
pub const FIFO_SCHEME: &str = "fifo";
|
||||
|
||||
const TIMEOUT_DURATION: std::time::Duration = Duration::from_secs(3);
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Log {
|
||||
pub level: String,
|
||||
|
@ -70,8 +73,6 @@ pub struct ProcessIO {
|
|||
pub uri: Option<String>,
|
||||
pub io: Option<Arc<dyn Io>>,
|
||||
pub copy: bool,
|
||||
pub stdout_r: Option<File>,
|
||||
pub stderr_r: Option<File>,
|
||||
}
|
||||
|
||||
pub fn create_io(
|
||||
|
@ -181,7 +182,7 @@ pub fn create_runc(
|
|||
}
|
||||
gopts
|
||||
.build()
|
||||
.map_err(other_error!(e, "unable to create runc instance"))
|
||||
.map_err(other_error!("unable to create runc instance"))
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -248,3 +249,17 @@ pub(crate) fn xdg_runtime_dir() -> String {
|
|||
env::var("XDG_RUNTIME_DIR")
|
||||
.unwrap_or_else(|_| env::temp_dir().to_str().unwrap_or(".").to_string())
|
||||
}
|
||||
|
||||
pub async fn handle_file_open<F, Fut>(file_op: F) -> Result<tokio::fs::File, tokio::io::Error>
|
||||
where
|
||||
F: FnOnce() -> Fut,
|
||||
Fut: Future<Output = Result<tokio::fs::File, tokio::io::Error>> + Send,
|
||||
{
|
||||
match tokio::time::timeout(TIMEOUT_DURATION, file_op()).await {
|
||||
Ok(result) => result,
|
||||
Err(_) => Err(std::io::Error::new(
|
||||
std::io::ErrorKind::TimedOut,
|
||||
"File operation timed out",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,11 +18,12 @@ use std::collections::HashMap;
|
|||
|
||||
use async_trait::async_trait;
|
||||
use containerd_shim::{
|
||||
api::Status,
|
||||
error::Result,
|
||||
protos::{
|
||||
api::{CreateTaskRequest, ExecProcessRequest, ProcessInfo, StateResponse},
|
||||
cgroups::metrics::Metrics,
|
||||
protobuf::{well_known_types::any::Any, Message, MessageDyn},
|
||||
protobuf::{well_known_types::any::Any, EnumOrUnknown, Message, MessageDyn},
|
||||
shim::oci::ProcessDetails,
|
||||
},
|
||||
Error,
|
||||
|
@ -56,6 +57,9 @@ pub trait Container {
|
|||
async fn stats(&self) -> Result<Metrics>;
|
||||
async fn all_processes(&self) -> Result<Vec<ProcessInfo>>;
|
||||
async fn close_io(&mut self, exec_id: Option<&str>) -> Result<()>;
|
||||
async fn pause(&mut self) -> Result<()>;
|
||||
async fn resume(&mut self) -> Result<()>;
|
||||
async fn init_state(&self) -> EnumOrUnknown<Status>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
@ -93,6 +97,11 @@ where
|
|||
E: Process + Send + Sync,
|
||||
P: ProcessFactory<E> + Send + Sync,
|
||||
{
|
||||
async fn init_state(&self) -> EnumOrUnknown<Status> {
|
||||
// Default should be unknown
|
||||
self.init.state().await.unwrap_or_default().status
|
||||
}
|
||||
|
||||
async fn start(&mut self, exec_id: Option<&str>) -> Result<i32> {
|
||||
let process = self.get_mut_process(exec_id)?;
|
||||
process.start().await?;
|
||||
|
@ -102,6 +111,12 @@ where
|
|||
async fn state(&self, exec_id: Option<&str>) -> Result<StateResponse> {
|
||||
let process = self.get_process(exec_id)?;
|
||||
let mut resp = process.state().await?;
|
||||
let init_state = self.init.state().await?.status;
|
||||
if init_state == EnumOrUnknown::new(Status::PAUSING)
|
||||
|| init_state == EnumOrUnknown::new(Status::PAUSED)
|
||||
{
|
||||
resp.status = init_state;
|
||||
}
|
||||
resp.bundle = self.bundle.to_string();
|
||||
debug!("container state: {:?}", resp);
|
||||
Ok(resp)
|
||||
|
@ -137,7 +152,6 @@ where
|
|||
let process = self.get_mut_process(exec_id_opt);
|
||||
match process {
|
||||
Ok(p) => p.delete().await?,
|
||||
Err(Error::NotFoundError(_)) => return Ok((pid, code, exited_at)),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
if let Some(exec_id) = exec_id_opt {
|
||||
|
@ -212,6 +226,14 @@ where
|
|||
let process = self.get_mut_process(exec_id)?;
|
||||
process.close_io().await
|
||||
}
|
||||
|
||||
async fn pause(&mut self) -> Result<()> {
|
||||
self.init.pause().await
|
||||
}
|
||||
|
||||
async fn resume(&mut self) -> Result<()> {
|
||||
self.init.resume().await
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E, P> ContainerTemplate<T, E, P>
|
||||
|
@ -238,7 +260,7 @@ where
|
|||
match exec_id {
|
||||
Some(exec_id) => {
|
||||
let p = self.processes.get_mut(exec_id).ok_or_else(|| {
|
||||
Error::NotFoundError("can not find the exec by id".to_string())
|
||||
Error::NotFoundError(format!("can not find the exec by id {}", exec_id))
|
||||
})?;
|
||||
Ok(p)
|
||||
}
|
||||
|
|
|
@ -14,9 +14,14 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
use std::env;
|
||||
use std::{env, io::Write};
|
||||
|
||||
use containerd_shim::{asynchronous::run, parse};
|
||||
use containerd_shim::{
|
||||
asynchronous::run,
|
||||
parse,
|
||||
protos::protobuf::{well_known_types::any::Any, Message},
|
||||
run_info,
|
||||
};
|
||||
|
||||
mod cgroup_memory;
|
||||
mod common;
|
||||
|
@ -47,6 +52,30 @@ fn parse_version() {
|
|||
|
||||
std::process::exit(0);
|
||||
}
|
||||
if flags.info {
|
||||
let r = run_info();
|
||||
match r {
|
||||
Ok(rinfo) => {
|
||||
let mut info = Any::new();
|
||||
info.type_url = "io.containerd.runc.v2.Info".to_string();
|
||||
info.value = match rinfo.write_to_bytes() {
|
||||
Ok(bytes) => bytes,
|
||||
Err(e) => {
|
||||
eprintln!("Failed to write runtime info to bytes: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
std::io::stdout()
|
||||
.write_all(info.write_to_bytes().unwrap().as_slice())
|
||||
.expect("Failed to write to stdout");
|
||||
}
|
||||
Err(_) => {
|
||||
eprintln!("Failed to get runtime info");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
std::process::exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
|
|
|
@ -39,6 +39,7 @@ use tokio::{
|
|||
|
||||
use crate::io::Stdio;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[async_trait]
|
||||
pub trait Process {
|
||||
async fn start(&mut self) -> Result<()>;
|
||||
|
@ -55,6 +56,9 @@ pub trait Process {
|
|||
async fn stats(&self) -> Result<Metrics>;
|
||||
async fn ps(&self) -> Result<Vec<ProcessInfo>>;
|
||||
async fn close_io(&mut self) -> Result<()>;
|
||||
async fn pause(&mut self) -> Result<()>;
|
||||
async fn resume(&mut self) -> Result<()>;
|
||||
async fn id(&self) -> &str;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
@ -65,6 +69,8 @@ pub trait ProcessLifecycle<P: Process> {
|
|||
async fn update(&self, p: &mut P, resources: &LinuxResources) -> Result<()>;
|
||||
async fn stats(&self, p: &P) -> Result<Metrics>;
|
||||
async fn ps(&self, p: &P) -> Result<Vec<ProcessInfo>>;
|
||||
async fn pause(&self, p: &mut P) -> Result<()>;
|
||||
async fn resume(&self, p: &mut P) -> Result<()>;
|
||||
}
|
||||
|
||||
pub struct ProcessTemplate<S> {
|
||||
|
@ -119,6 +125,10 @@ where
|
|||
self.pid
|
||||
}
|
||||
|
||||
async fn id(&self) -> &str {
|
||||
self.id.as_str()
|
||||
}
|
||||
|
||||
async fn state(&self) -> Result<StateResponse> {
|
||||
let mut resp = StateResponse::new();
|
||||
resp.id = self.id.to_string();
|
||||
|
@ -198,4 +208,12 @@ where
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn pause(&mut self) -> Result<()> {
|
||||
self.lifecycle.clone().pause(self).await
|
||||
}
|
||||
|
||||
async fn resume(&mut self) -> Result<()> {
|
||||
self.lifecycle.clone().resume(self).await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ use containerd_shim::{
|
|||
asynchronous::monitor::{monitor_subscribe, monitor_unsubscribe, Subscription},
|
||||
io_error,
|
||||
monitor::{ExitEvent, Subject, Topic},
|
||||
mount::umount_recursive,
|
||||
other, other_error,
|
||||
protos::{
|
||||
api::ProcessInfo,
|
||||
|
@ -59,12 +60,25 @@ use super::{
|
|||
};
|
||||
use crate::{
|
||||
common::{
|
||||
check_kill_error, create_io, create_runc, get_spec_from_request, receive_socket,
|
||||
CreateConfig, Log, ProcessIO, ShimExecutor, INIT_PID_FILE, LOG_JSON_FILE,
|
||||
check_kill_error, create_io, create_runc, get_spec_from_request, handle_file_open,
|
||||
receive_socket, CreateConfig, Log, ProcessIO, ShimExecutor, INIT_PID_FILE, LOG_JSON_FILE,
|
||||
},
|
||||
io::Stdio,
|
||||
};
|
||||
|
||||
/// check the process is zombie
|
||||
#[cfg(target_os = "linux")]
|
||||
fn is_zombie_process(pid: i32) -> bool {
|
||||
if let Ok(status) = std::fs::read_to_string(format!("/proc/{}/status", pid)) {
|
||||
for line in status.lines() {
|
||||
if line.starts_with("State:") && line.contains('Z') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub type ExecProcess = ProcessTemplate<RuncExecLifecycle>;
|
||||
pub type InitProcess = ProcessTemplate<RuncInitLifecycle>;
|
||||
|
||||
|
@ -299,6 +313,7 @@ impl ProcessLifecycle<InitProcess> for RuncInitLifecycle {
|
|||
);
|
||||
}
|
||||
}
|
||||
umount_recursive(Path::new(&self.bundle).join("rootfs").to_str(), 0)?;
|
||||
self.exit_signal.signal();
|
||||
Ok(())
|
||||
}
|
||||
|
@ -311,6 +326,15 @@ impl ProcessLifecycle<InitProcess> for RuncInitLifecycle {
|
|||
p.pid
|
||||
));
|
||||
}
|
||||
|
||||
// check the process is zombie
|
||||
if is_zombie_process(p.pid) {
|
||||
return Err(other!(
|
||||
"failed to update resources because process {} is a zombie",
|
||||
p.pid
|
||||
));
|
||||
}
|
||||
|
||||
containerd_shim::cgroup::update_resources(p.pid as u32, resources)
|
||||
}
|
||||
|
||||
|
@ -327,6 +351,15 @@ impl ProcessLifecycle<InitProcess> for RuncInitLifecycle {
|
|||
p.pid
|
||||
));
|
||||
}
|
||||
|
||||
// check the process is zombie
|
||||
if is_zombie_process(p.pid) {
|
||||
return Err(other!(
|
||||
"failed to collect metrics because process {} is a zombie",
|
||||
p.pid
|
||||
));
|
||||
}
|
||||
|
||||
containerd_shim::cgroup::collect_metrics(p.pid as u32)
|
||||
}
|
||||
|
||||
|
@ -340,7 +373,7 @@ impl ProcessLifecycle<InitProcess> for RuncInitLifecycle {
|
|||
.runtime
|
||||
.ps(&p.id)
|
||||
.await
|
||||
.map_err(other_error!(e, "failed to execute runc ps"))?;
|
||||
.map_err(other_error!("failed to execute runc ps"))?;
|
||||
Ok(pids
|
||||
.iter()
|
||||
.map(|&x| ProcessInfo {
|
||||
|
@ -349,6 +382,46 @@ impl ProcessLifecycle<InitProcess> for RuncInitLifecycle {
|
|||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
async fn pause(&self, p: &mut InitProcess) -> Result<()> {
|
||||
match p.state {
|
||||
Status::RUNNING => {
|
||||
p.state = Status::PAUSING;
|
||||
if let Err(e) = self.runtime.pause(p.id.as_str()).await {
|
||||
p.state = Status::RUNNING;
|
||||
return Err(runtime_error(&self.bundle, e, "OCI runtime pause failed").await);
|
||||
}
|
||||
p.state = Status::PAUSED;
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(other!("cannot pause when in {:?} state", p.state)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
async fn pause(&self, _p: &mut InitProcess) -> Result<()> {
|
||||
Err(Error::Unimplemented("pause".to_string()))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
async fn resume(&self, p: &mut InitProcess) -> Result<()> {
|
||||
match p.state {
|
||||
Status::PAUSED => {
|
||||
if let Err(e) = self.runtime.resume(p.id.as_str()).await {
|
||||
return Err(runtime_error(&self.bundle, e, "OCI runtime pause failed").await);
|
||||
}
|
||||
p.state = Status::RUNNING;
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(other!("cannot resume when in {:?} state", p.state)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
async fn resume(&self, _p: &mut InitProcess) -> Result<()> {
|
||||
Err(Error::Unimplemented("resume".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl RuncInitLifecycle {
|
||||
|
@ -473,6 +546,14 @@ impl ProcessLifecycle<ExecProcess> for RuncExecLifecycle {
|
|||
async fn ps(&self, _p: &ExecProcess) -> Result<Vec<ProcessInfo>> {
|
||||
Err(Error::Unimplemented("exec ps".to_string()))
|
||||
}
|
||||
|
||||
async fn pause(&self, _p: &mut ExecProcess) -> Result<()> {
|
||||
Err(Error::Unimplemented("exec pause".to_string()))
|
||||
}
|
||||
|
||||
async fn resume(&self, _p: &mut ExecProcess) -> Result<()> {
|
||||
Err(Error::Unimplemented("exec resume".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
async fn copy_console(
|
||||
|
@ -490,11 +571,14 @@ async fn copy_console(
|
|||
.try_clone()
|
||||
.await
|
||||
.map_err(io_error!(e, "failed to clone console file"))?;
|
||||
let stdin = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(stdio.stdin.as_str())
|
||||
.await
|
||||
.map_err(io_error!(e, "failed to open stdin"))?;
|
||||
let stdin = handle_file_open(|| async {
|
||||
OpenOptions::new()
|
||||
.read(true)
|
||||
.open(stdio.stdin.as_str())
|
||||
.await
|
||||
})
|
||||
.await
|
||||
.map_err(io_error!(e, "failed to open stdin"))?;
|
||||
spawn_copy(stdin, console_stdin, exit_signal.clone(), None::<fn()>);
|
||||
}
|
||||
|
||||
|
@ -539,11 +623,14 @@ pub async fn copy_io(pio: &ProcessIO, stdio: &Stdio, exit_signal: Arc<ExitSignal
|
|||
if let Some(w) = io.stdin() {
|
||||
debug!("copy_io: pipe stdin from {}", stdio.stdin.as_str());
|
||||
if !stdio.stdin.is_empty() {
|
||||
let stdin = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(stdio.stdin.as_str())
|
||||
.await
|
||||
.map_err(io_error!(e, "open stdin"))?;
|
||||
let stdin = handle_file_open(|| async {
|
||||
OpenOptions::new()
|
||||
.read(true)
|
||||
.open(stdio.stdin.as_str())
|
||||
.await
|
||||
})
|
||||
.await
|
||||
.map_err(io_error!(e, "open stdin"))?;
|
||||
spawn_copy(stdin, w, exit_signal.clone(), None::<fn()>);
|
||||
}
|
||||
}
|
||||
|
@ -551,18 +638,24 @@ pub async fn copy_io(pio: &ProcessIO, stdio: &Stdio, exit_signal: Arc<ExitSignal
|
|||
if let Some(r) = io.stdout() {
|
||||
debug!("copy_io: pipe stdout from to {}", stdio.stdout.as_str());
|
||||
if !stdio.stdout.is_empty() {
|
||||
let stdout = OpenOptions::new()
|
||||
.write(true)
|
||||
.open(stdio.stdout.as_str())
|
||||
.await
|
||||
.map_err(io_error!(e, "open stdout"))?;
|
||||
let stdout = handle_file_open(|| async {
|
||||
OpenOptions::new()
|
||||
.write(true)
|
||||
.open(stdio.stdout.as_str())
|
||||
.await
|
||||
})
|
||||
.await
|
||||
.map_err(io_error!(e, "open stdout"))?;
|
||||
// open a read to make sure even if the read end of containerd shutdown,
|
||||
// copy still continue until the restart of containerd succeed
|
||||
let stdout_r = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(stdio.stdout.as_str())
|
||||
.await
|
||||
.map_err(io_error!(e, "open stdout for read"))?;
|
||||
let stdout_r = handle_file_open(|| async {
|
||||
OpenOptions::new()
|
||||
.read(true)
|
||||
.open(stdio.stdout.as_str())
|
||||
.await
|
||||
})
|
||||
.await
|
||||
.map_err(io_error!(e, "open stdout for read"))?;
|
||||
spawn_copy(
|
||||
r,
|
||||
stdout,
|
||||
|
@ -577,18 +670,24 @@ pub async fn copy_io(pio: &ProcessIO, stdio: &Stdio, exit_signal: Arc<ExitSignal
|
|||
if let Some(r) = io.stderr() {
|
||||
if !stdio.stderr.is_empty() {
|
||||
debug!("copy_io: pipe stderr from to {}", stdio.stderr.as_str());
|
||||
let stderr = OpenOptions::new()
|
||||
.write(true)
|
||||
.open(stdio.stderr.as_str())
|
||||
.await
|
||||
.map_err(io_error!(e, "open stderr"))?;
|
||||
let stderr = handle_file_open(|| async {
|
||||
OpenOptions::new()
|
||||
.write(true)
|
||||
.open(stdio.stderr.as_str())
|
||||
.await
|
||||
})
|
||||
.await
|
||||
.map_err(io_error!(e, "open stderr"))?;
|
||||
// open a read to make sure even if the read end of containerd shutdown,
|
||||
// copy still continue until the restart of containerd succeed
|
||||
let stderr_r = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(stdio.stderr.as_str())
|
||||
.await
|
||||
.map_err(io_error!(e, "open stderr for read"))?;
|
||||
let stderr_r = handle_file_open(|| async {
|
||||
OpenOptions::new()
|
||||
.read(true)
|
||||
.open(stdio.stderr.as_str())
|
||||
.await
|
||||
})
|
||||
.await
|
||||
.map_err(io_error!(e, "open stderr for read"))?;
|
||||
spawn_copy(
|
||||
r,
|
||||
stderr,
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
use std::{env::current_dir, sync::Arc};
|
||||
use std::{env::current_dir, sync::Arc, time::Duration};
|
||||
|
||||
use ::runc::options::DeleteOpts;
|
||||
use async_trait::async_trait;
|
||||
|
@ -27,12 +27,13 @@ use containerd_shim::{
|
|||
event::Event,
|
||||
io_error,
|
||||
monitor::{Subject, Topic},
|
||||
protos::{events::task::TaskExit, protobuf::MessageDyn},
|
||||
mount::umount_recursive,
|
||||
protos::{events::task::TaskExit, protobuf::MessageDyn, ttrpc::context::with_duration},
|
||||
util::{
|
||||
convert_to_timestamp, read_options, read_pid_from_file, read_runtime, read_spec, timestamp,
|
||||
write_str_to_file,
|
||||
},
|
||||
Config, Context, DeleteResponse, Error, Flags, StartOpts,
|
||||
Config, DeleteResponse, Error, Flags, StartOpts,
|
||||
};
|
||||
use log::{debug, error, warn};
|
||||
use tokio::sync::mpsc::{channel, Receiver, Sender};
|
||||
|
@ -108,7 +109,7 @@ impl Shim for Service {
|
|||
let namespace = self.namespace.as_str();
|
||||
let bundle = current_dir().map_err(io_error!(e, "get current dir"))?;
|
||||
let opts = read_options(&bundle).await?;
|
||||
let runtime = read_runtime(&bundle).await?;
|
||||
let runtime = read_runtime(&bundle).await.unwrap_or_default();
|
||||
|
||||
let runc = create_runc(
|
||||
&runtime,
|
||||
|
@ -117,11 +118,15 @@ impl Shim for Service {
|
|||
&opts,
|
||||
Some(Arc::new(ShimExecutor::default())),
|
||||
)?;
|
||||
let pid = read_pid_from_file(&bundle.join(INIT_PID_FILE)).await?;
|
||||
let pid = read_pid_from_file(&bundle.join(INIT_PID_FILE))
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
|
||||
runc.delete(&self.id, Some(&DeleteOpts { force: true }))
|
||||
.await
|
||||
.unwrap_or_else(|e| warn!("failed to remove runc container: {}", e));
|
||||
umount_recursive(bundle.join("rootfs").to_str(), 0)
|
||||
.unwrap_or_else(|e| warn!("failed to umount recursive rootfs: {}", e));
|
||||
let mut resp = DeleteResponse::new();
|
||||
// sigkill
|
||||
resp.set_exit_status(137);
|
||||
|
@ -159,8 +164,10 @@ async fn process_exits(
|
|||
if let Subject::Pid(pid) = e.subject {
|
||||
debug!("receive exit event: {}", &e);
|
||||
let exit_code = e.exit_code;
|
||||
for (_k, cont) in containers.lock().await.iter_mut() {
|
||||
for (_k, cont) in containers.write().await.iter_mut() {
|
||||
let bundle = cont.bundle.to_string();
|
||||
let container_id = cont.id.clone();
|
||||
let mut change_process: Vec<&mut (dyn Process + Send + Sync)> = Vec::new();
|
||||
// pid belongs to container init process
|
||||
if cont.init.pid == pid {
|
||||
// kill all children process if the container has a private PID namespace
|
||||
|
@ -169,20 +176,30 @@ async fn process_exits(
|
|||
error!("failed to kill init's children: {}", e)
|
||||
});
|
||||
}
|
||||
// set exit for init process
|
||||
cont.init.set_exited(exit_code).await;
|
||||
|
||||
if let Ok(process_d) = cont.get_mut_process(None) {
|
||||
change_process.push(process_d);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// pid belongs to container common process
|
||||
if let Some((_, p)) = cont.processes.iter_mut().find(|(_, p)| p.pid == pid)
|
||||
{
|
||||
change_process.push(p as &mut (dyn Process + Send + Sync));
|
||||
}
|
||||
}
|
||||
let process_len = change_process.len();
|
||||
for process in change_process {
|
||||
// set exit for process
|
||||
process.set_exited(exit_code).await;
|
||||
let code = process.exit_code().await;
|
||||
let exited_at = process.exited_at().await;
|
||||
// publish event
|
||||
let (_, code, exited_at) = match cont.get_exit_info(None).await {
|
||||
Ok(info) => info,
|
||||
Err(_) => break,
|
||||
};
|
||||
|
||||
let ts = convert_to_timestamp(exited_at);
|
||||
let event = TaskExit {
|
||||
container_id: cont.id.to_string(),
|
||||
id: cont.id.to_string(),
|
||||
pid: cont.pid().await as u32,
|
||||
container_id: container_id.clone(),
|
||||
id: process.id().await.to_string(),
|
||||
pid: process.pid().await as u32,
|
||||
exit_status: code as u32,
|
||||
exited_at: Some(ts).into(),
|
||||
..Default::default()
|
||||
|
@ -191,18 +208,10 @@ async fn process_exits(
|
|||
tx.send((topic.to_string(), Box::new(event)))
|
||||
.await
|
||||
.unwrap_or_else(|e| warn!("send {} to publisher: {}", topic, e));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// pid belongs to container common process
|
||||
for (_exec_id, p) in cont.processes.iter_mut() {
|
||||
// set exit for exec process
|
||||
if p.pid == pid {
|
||||
p.set_exited(exit_code).await;
|
||||
// TODO: publish event
|
||||
break;
|
||||
}
|
||||
//if process has been find , no need to keep search
|
||||
if process_len != 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -218,8 +227,11 @@ async fn forward(
|
|||
) {
|
||||
tokio::spawn(async move {
|
||||
while let Some((topic, e)) = rx.recv().await {
|
||||
// While ttrpc push the event,give it a 5 seconds timeout.
|
||||
// Prevent event reporting from taking too long time.
|
||||
// Learnd from goshim's containerd/runtime/v2/shim/publisher.go
|
||||
publisher
|
||||
.publish(Context::default(), &topic, &ns, e)
|
||||
.publish(with_duration(Duration::from_secs(5)), &topic, &ns, e)
|
||||
.await
|
||||
.unwrap_or_else(|e| warn!("publish {} to containerd: {}", topic, e));
|
||||
}
|
||||
|
|
|
@ -30,17 +30,18 @@ use containerd_shim::{
|
|||
PidsResponse, StatsRequest, StatsResponse, UpdateTaskRequest,
|
||||
},
|
||||
events::task::{TaskCreate, TaskDelete, TaskExecAdded, TaskExecStarted, TaskIO, TaskStart},
|
||||
protobuf::MessageDyn,
|
||||
protobuf::{EnumOrUnknown, MessageDyn},
|
||||
shim_async::Task,
|
||||
ttrpc,
|
||||
ttrpc::r#async::TtrpcContext,
|
||||
ttrpc::{self, r#async::TtrpcContext},
|
||||
},
|
||||
util::{convert_to_any, convert_to_timestamp, AsOption},
|
||||
TtrpcResult,
|
||||
};
|
||||
use log::{debug, info, warn};
|
||||
use oci_spec::runtime::LinuxResources;
|
||||
use tokio::sync::{mpsc::Sender, MappedMutexGuard, Mutex, MutexGuard};
|
||||
use tokio::sync::{
|
||||
mpsc::Sender, RwLock, RwLockMappedWriteGuard, RwLockReadGuard, RwLockWriteGuard,
|
||||
};
|
||||
|
||||
use super::container::{Container, ContainerFactory};
|
||||
type EventSender = Sender<(String, Box<dyn MessageDyn>)>;
|
||||
|
@ -50,6 +51,10 @@ use std::path::Path;
|
|||
|
||||
#[cfg(target_os = "linux")]
|
||||
use cgroups_rs::hierarchies::is_cgroup2_unified_mode;
|
||||
use containerd_shim::{
|
||||
api::{PauseRequest, ResumeRequest},
|
||||
protos::events::task::{TaskPaused, TaskResumed},
|
||||
};
|
||||
#[cfg(target_os = "linux")]
|
||||
use containerd_shim::{
|
||||
error::{Error, Result},
|
||||
|
@ -69,7 +74,10 @@ use crate::cgroup_memory;
|
|||
/// parameter of `Service`, and implements their own `ContainerFactory` and `Container`.
|
||||
pub struct TaskService<F, C> {
|
||||
pub factory: F,
|
||||
pub containers: Arc<Mutex<HashMap<String, C>>>,
|
||||
// In comparison, a Mutex does not distinguish between readers or writers that acquire the lock,
|
||||
// therefore causing any tasks waiting for the lock to become available to yield.
|
||||
// An RwLock will allow any number of readers to acquire the lock as long as a writer is not holding the lock.
|
||||
pub containers: Arc<RwLock<HashMap<String, C>>>,
|
||||
pub namespace: String,
|
||||
pub exit: Arc<ExitSignal>,
|
||||
pub tx: EventSender,
|
||||
|
@ -82,7 +90,7 @@ where
|
|||
pub fn new(ns: &str, exit: Arc<ExitSignal>, tx: EventSender) -> Self {
|
||||
Self {
|
||||
factory: Default::default(),
|
||||
containers: Arc::new(Mutex::new(Default::default())),
|
||||
containers: Arc::new(RwLock::new(Default::default())),
|
||||
namespace: ns.to_string(),
|
||||
exit,
|
||||
tx,
|
||||
|
@ -91,15 +99,27 @@ where
|
|||
}
|
||||
|
||||
impl<F, C> TaskService<F, C> {
|
||||
pub async fn get_container(&self, id: &str) -> TtrpcResult<MappedMutexGuard<'_, C>> {
|
||||
let mut containers = self.containers.lock().await;
|
||||
pub async fn container_mut(&self, id: &str) -> TtrpcResult<RwLockMappedWriteGuard<'_, C>> {
|
||||
let mut containers = self.containers.write().await;
|
||||
containers.get_mut(id).ok_or_else(|| {
|
||||
ttrpc::Error::RpcStatus(ttrpc::get_status(
|
||||
ttrpc::Code::NOT_FOUND,
|
||||
format!("can not find container by id {}", id),
|
||||
))
|
||||
})?;
|
||||
let container = MutexGuard::map(containers, |m| m.get_mut(id).unwrap());
|
||||
let container = RwLockWriteGuard::map(containers, |m| m.get_mut(id).unwrap());
|
||||
Ok(container)
|
||||
}
|
||||
|
||||
pub async fn container(&self, id: &str) -> TtrpcResult<RwLockReadGuard<'_, C>> {
|
||||
let containers = self.containers.read().await;
|
||||
containers.get(id).ok_or_else(|| {
|
||||
ttrpc::Error::RpcStatus(ttrpc::get_status(
|
||||
ttrpc::Code::NOT_FOUND,
|
||||
format!("can not find container by id {}", id),
|
||||
))
|
||||
})?;
|
||||
let container = RwLockReadGuard::map(containers, |m| m.get(id).unwrap());
|
||||
Ok(container)
|
||||
}
|
||||
|
||||
|
@ -143,7 +163,7 @@ async fn monitor_oom(id: &String, pid: u32, tx: EventSender) -> Result<()> {
|
|||
"memory.oom_control",
|
||||
)
|
||||
.await
|
||||
.map_err(other_error!(e, "register_memory_event failed:"))?;
|
||||
.map_err(other_error!("register_memory_event failed:"))?;
|
||||
|
||||
run_oom_monitor(rx, id.to_string(), tx);
|
||||
}
|
||||
|
@ -157,7 +177,7 @@ where
|
|||
C: Container + Sync + Send + 'static,
|
||||
{
|
||||
async fn state(&self, _ctx: &TtrpcContext, req: StateRequest) -> TtrpcResult<StateResponse> {
|
||||
let container = self.get_container(req.id()).await?;
|
||||
let container = self.container(req.id()).await?;
|
||||
let exec_id = req.exec_id().as_option();
|
||||
let resp = container.state(exec_id).await?;
|
||||
Ok(resp)
|
||||
|
@ -171,17 +191,17 @@ where
|
|||
info!("Create request for {:?}", &req);
|
||||
// Note: Get containers here is for getting the lock,
|
||||
// to make sure no other threads manipulate the containers metadata;
|
||||
let mut containers = self.containers.lock().await;
|
||||
|
||||
let ns = self.namespace.as_str();
|
||||
let id = req.id.as_str();
|
||||
|
||||
let container = self.factory.create(ns, &req).await?;
|
||||
let mut resp = CreateTaskResponse::new();
|
||||
let pid = container.pid().await as u32;
|
||||
resp.pid = pid;
|
||||
|
||||
containers.insert(id.to_string(), container);
|
||||
let pid = {
|
||||
let mut containers = self.containers.write().await;
|
||||
let container = self.factory.create(ns, &req).await?;
|
||||
let pid = container.pid().await as u32;
|
||||
resp.pid = pid;
|
||||
containers.insert(id.to_string(), container);
|
||||
pid
|
||||
};
|
||||
|
||||
self.send_event(TaskCreate {
|
||||
container_id: req.id.to_string(),
|
||||
|
@ -206,8 +226,19 @@ where
|
|||
|
||||
async fn start(&self, _ctx: &TtrpcContext, req: StartRequest) -> TtrpcResult<StartResponse> {
|
||||
info!("Start request for {:?}", &req);
|
||||
let mut container = self.get_container(req.id()).await?;
|
||||
let pid = container.start(req.exec_id.as_str().as_option()).await?;
|
||||
let pid = {
|
||||
let mut container = self.container_mut(req.id()).await?;
|
||||
// Prevent the init process from exiting and continuing with start
|
||||
// Return early to reduce the time it takes to return only when runc encounters an error
|
||||
if container.init_state().await == EnumOrUnknown::new(Status::STOPPED) {
|
||||
debug!("container init process has exited, start process should not continue");
|
||||
return Err(ttrpc::Error::RpcStatus(ttrpc::get_status(
|
||||
ttrpc::Code::FAILED_PRECONDITION,
|
||||
format!("container init process has exited {}", container.id().await),
|
||||
)));
|
||||
}
|
||||
container.start(req.exec_id.as_str().as_option()).await?
|
||||
};
|
||||
|
||||
let mut resp = StartResponse::new();
|
||||
resp.pid = pid as u32;
|
||||
|
@ -239,19 +270,17 @@ where
|
|||
|
||||
async fn delete(&self, _ctx: &TtrpcContext, req: DeleteRequest) -> TtrpcResult<DeleteResponse> {
|
||||
info!("Delete request for {:?}", &req);
|
||||
let mut containers = self.containers.lock().await;
|
||||
let container = containers.get_mut(req.id()).ok_or_else(|| {
|
||||
ttrpc::Error::RpcStatus(ttrpc::get_status(
|
||||
ttrpc::Code::NOT_FOUND,
|
||||
format!("can not find container by id {}", req.id()),
|
||||
))
|
||||
})?;
|
||||
let id = container.id().await;
|
||||
let exec_id_opt = req.exec_id().as_option();
|
||||
let (pid, exit_status, exited_at) = container.delete(exec_id_opt).await?;
|
||||
self.factory.cleanup(&self.namespace, container).await?;
|
||||
let (id, pid, exit_status, exited_at) = {
|
||||
let mut container = self.container_mut(req.id()).await?;
|
||||
let id = container.id().await;
|
||||
let exec_id_opt = req.exec_id().as_option();
|
||||
let (pid, exit_status, exited_at) = container.delete(exec_id_opt).await?;
|
||||
self.factory.cleanup(&self.namespace, &container).await?;
|
||||
(id, pid, exit_status, exited_at)
|
||||
};
|
||||
|
||||
if req.exec_id().is_empty() {
|
||||
containers.remove(req.id());
|
||||
self.containers.write().await.remove(req.id());
|
||||
}
|
||||
|
||||
let ts = convert_to_timestamp(exited_at);
|
||||
|
@ -279,8 +308,7 @@ where
|
|||
|
||||
async fn pids(&self, _ctx: &TtrpcContext, req: PidsRequest) -> TtrpcResult<PidsResponse> {
|
||||
debug!("Pids request for {:?}", req);
|
||||
let container = self.get_container(req.id()).await?;
|
||||
let processes = container.all_processes().await?;
|
||||
let processes = self.container(req.id()).await?.all_processes().await?;
|
||||
debug!("Pids request for {:?} returns successfully", req);
|
||||
Ok(PidsResponse {
|
||||
processes,
|
||||
|
@ -288,10 +316,34 @@ where
|
|||
})
|
||||
}
|
||||
|
||||
async fn pause(&self, _ctx: &TtrpcContext, req: PauseRequest) -> TtrpcResult<Empty> {
|
||||
info!("pause request for {:?}", req);
|
||||
self.container_mut(req.id()).await?.pause().await?;
|
||||
self.send_event(TaskPaused {
|
||||
container_id: req.id.to_string(),
|
||||
..Default::default()
|
||||
})
|
||||
.await;
|
||||
info!("pause request for {:?} returns successfully", req);
|
||||
Ok(Empty::new())
|
||||
}
|
||||
|
||||
async fn resume(&self, _ctx: &TtrpcContext, req: ResumeRequest) -> TtrpcResult<Empty> {
|
||||
info!("resume request for {:?}", req);
|
||||
self.container_mut(req.id()).await?.resume().await?;
|
||||
self.send_event(TaskResumed {
|
||||
container_id: req.id.to_string(),
|
||||
..Default::default()
|
||||
})
|
||||
.await;
|
||||
info!("resume request for {:?} returns successfully", req);
|
||||
Ok(Empty::new())
|
||||
}
|
||||
|
||||
async fn kill(&self, _ctx: &TtrpcContext, req: KillRequest) -> TtrpcResult<Empty> {
|
||||
info!("Kill request for {:?}", req);
|
||||
let mut container = self.get_container(req.id()).await?;
|
||||
container
|
||||
self.container_mut(req.id())
|
||||
.await?
|
||||
.kill(req.exec_id().as_option(), req.signal, req.all)
|
||||
.await?;
|
||||
info!("Kill request for {:?} returns successfully", req);
|
||||
|
@ -301,11 +353,15 @@ where
|
|||
async fn exec(&self, _ctx: &TtrpcContext, req: ExecProcessRequest) -> TtrpcResult<Empty> {
|
||||
info!("Exec request for {:?}", req);
|
||||
let exec_id = req.exec_id().to_string();
|
||||
let mut container = self.get_container(req.id()).await?;
|
||||
container.exec(req).await?;
|
||||
|
||||
let container_id = {
|
||||
let mut container = self.container_mut(req.id()).await?;
|
||||
container.exec(req).await?;
|
||||
container.id().await
|
||||
};
|
||||
|
||||
self.send_event(TaskExecAdded {
|
||||
container_id: container.id().await,
|
||||
container_id,
|
||||
exec_id,
|
||||
..Default::default()
|
||||
})
|
||||
|
@ -319,16 +375,18 @@ where
|
|||
"Resize pty request for container {}, exec_id: {}",
|
||||
&req.id, &req.exec_id
|
||||
);
|
||||
let mut container = self.get_container(req.id()).await?;
|
||||
container
|
||||
self.container_mut(req.id())
|
||||
.await?
|
||||
.resize_pty(req.exec_id().as_option(), req.height, req.width)
|
||||
.await?;
|
||||
Ok(Empty::new())
|
||||
}
|
||||
|
||||
async fn close_io(&self, _ctx: &TtrpcContext, req: CloseIORequest) -> TtrpcResult<Empty> {
|
||||
let mut container = self.get_container(req.id()).await?;
|
||||
container.close_io(req.exec_id().as_option()).await?;
|
||||
self.container_mut(req.id())
|
||||
.await?
|
||||
.close_io(req.exec_id().as_option())
|
||||
.await?;
|
||||
Ok(Empty::new())
|
||||
}
|
||||
|
||||
|
@ -349,9 +407,7 @@ where
|
|||
format!("failed to parse resource spec: {}", e),
|
||||
))
|
||||
})?;
|
||||
|
||||
let mut container = self.get_container(&id).await?;
|
||||
container.update(&resources).await?;
|
||||
self.container_mut(&id).await?.update(&resources).await?;
|
||||
Ok(Empty::new())
|
||||
}
|
||||
|
||||
|
@ -359,7 +415,7 @@ where
|
|||
info!("Wait request for {:?}", req);
|
||||
let exec_id = req.exec_id.as_str().as_option();
|
||||
let wait_rx = {
|
||||
let mut container = self.get_container(req.id()).await?;
|
||||
let mut container = self.container_mut(req.id()).await?;
|
||||
let state = container.state(exec_id).await?;
|
||||
if state.status() != Status::RUNNING && state.status() != Status::CREATED {
|
||||
let mut resp = WaitResponse::new();
|
||||
|
@ -373,8 +429,11 @@ where
|
|||
|
||||
wait_rx.await.unwrap_or_default();
|
||||
// get lock again.
|
||||
let container = self.get_container(req.id()).await?;
|
||||
let (_, code, exited_at) = container.get_exit_info(exec_id).await?;
|
||||
let (_, code, exited_at) = self
|
||||
.container(req.id())
|
||||
.await?
|
||||
.get_exit_info(exec_id)
|
||||
.await?;
|
||||
let mut resp = WaitResponse::new();
|
||||
resp.set_exit_status(code as u32);
|
||||
let ts = convert_to_timestamp(exited_at);
|
||||
|
@ -385,9 +444,7 @@ where
|
|||
|
||||
async fn stats(&self, _ctx: &TtrpcContext, req: StatsRequest) -> TtrpcResult<StatsResponse> {
|
||||
debug!("Stats request for {:?}", req);
|
||||
let container = self.get_container(req.id()).await?;
|
||||
let stats = container.stats().await?;
|
||||
|
||||
let stats = self.container(req.id()).await?.stats().await?;
|
||||
let mut resp = StatsResponse::new();
|
||||
resp.set_stats(convert_to_any(Box::new(stats))?);
|
||||
Ok(resp)
|
||||
|
@ -399,10 +456,12 @@ where
|
|||
req: ConnectRequest,
|
||||
) -> TtrpcResult<ConnectResponse> {
|
||||
info!("Connect request for {:?}", req);
|
||||
let mut pid: u32 = 0;
|
||||
if let Ok(container) = self.get_container(req.id()).await {
|
||||
pid = container.pid().await as u32;
|
||||
}
|
||||
|
||||
let pid = if let Ok(container) = self.container(req.id()).await {
|
||||
container.pid().await as u32
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
Ok(ConnectResponse {
|
||||
shim_pid: std::process::id(),
|
||||
|
@ -413,7 +472,7 @@ where
|
|||
|
||||
async fn shutdown(&self, _ctx: &TtrpcContext, _req: ShutdownRequest) -> TtrpcResult<Empty> {
|
||||
debug!("Shutdown request");
|
||||
let containers = self.containers.lock().await;
|
||||
let containers = self.containers.read().await;
|
||||
if containers.len() > 0 {
|
||||
return Ok(Empty::new());
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "runc"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
authors = ["Yuna Tomida <ytomida.mmm@gmail.com>", "The containerd Authors"]
|
||||
description = "A crate for consuming the runc binary in your Rust applications"
|
||||
keywords = ["containerd", "containers", "runc"]
|
||||
|
@ -20,7 +20,6 @@ libc.workspace = true
|
|||
log.workspace = true
|
||||
nix = { workspace = true, features = ["user", "fs"] }
|
||||
oci-spec.workspace = true
|
||||
os_pipe.workspace = true
|
||||
path-absolutize = "3.0.11"
|
||||
prctl.workspace = true
|
||||
serde.workspace = true
|
||||
|
@ -29,6 +28,7 @@ tempfile.workspace = true
|
|||
thiserror.workspace = true
|
||||
time.workspace = true
|
||||
uuid.workspace = true
|
||||
os_pipe.workspace = true
|
||||
|
||||
# Async dependencies
|
||||
async-trait = { workspace = true, optional = true }
|
||||
|
|
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
use std::{fmt::Debug, io::Result, os::unix::io::AsRawFd, process::Stdio};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use nix::unistd::{Gid, Uid};
|
||||
use tokio::fs::OpenOptions;
|
||||
|
||||
pub use crate::Io;
|
||||
use crate::{Command, Pipe, PipedIo};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IOOption {
|
||||
pub open_stdin: bool,
|
||||
pub open_stdout: bool,
|
||||
pub open_stderr: bool,
|
||||
}
|
||||
|
||||
impl Default for IOOption {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
open_stdin: true,
|
||||
open_stdout: true,
|
||||
open_stderr: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PipedIo {
|
||||
pub fn new(uid: u32, gid: u32, opts: &IOOption) -> std::io::Result<Self> {
|
||||
Ok(Self {
|
||||
stdin: if opts.open_stdin {
|
||||
Self::create_pipe(uid, gid, true)?
|
||||
} else {
|
||||
None
|
||||
},
|
||||
stdout: if opts.open_stdout {
|
||||
Self::create_pipe(uid, gid, true)?
|
||||
} else {
|
||||
None
|
||||
},
|
||||
stderr: if opts.open_stderr {
|
||||
Self::create_pipe(uid, gid, true)?
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn create_pipe(uid: u32, gid: u32, stdin: bool) -> std::io::Result<Option<Pipe>> {
|
||||
let pipe = Pipe::new()?;
|
||||
let uid = Some(Uid::from_raw(uid));
|
||||
let gid = Some(Gid::from_raw(gid));
|
||||
if stdin {
|
||||
let rd = pipe.rd.try_clone()?;
|
||||
nix::unistd::fchown(rd.as_raw_fd(), uid, gid)?;
|
||||
} else {
|
||||
let wr = pipe.wr.try_clone()?;
|
||||
nix::unistd::fchown(wr.as_raw_fd(), uid, gid)?;
|
||||
}
|
||||
Ok(Some(pipe))
|
||||
}
|
||||
}
|
||||
|
||||
/// IO driver to direct output/error messages to /dev/null.
|
||||
///
|
||||
/// With this Io driver, all methods of [crate::Runc] can't capture the output/error messages.
|
||||
#[derive(Debug)]
|
||||
pub struct NullIo {
|
||||
dev_null: std::sync::Mutex<Option<std::fs::File>>,
|
||||
}
|
||||
|
||||
impl NullIo {
|
||||
pub fn new() -> std::io::Result<Self> {
|
||||
let f = std::fs::OpenOptions::new().read(true).open("/dev/null")?;
|
||||
let dev_null = std::sync::Mutex::new(Some(f));
|
||||
Ok(Self { dev_null })
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Io for NullIo {
|
||||
async fn set(&self, cmd: &mut Command) -> std::io::Result<()> {
|
||||
if let Some(null) = self.dev_null.lock().unwrap().as_ref() {
|
||||
cmd.stdout(null.try_clone()?);
|
||||
cmd.stderr(null.try_clone()?);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn close_after_start(&self) {
|
||||
let mut m = self.dev_null.lock().unwrap();
|
||||
let _ = m.take();
|
||||
}
|
||||
}
|
||||
|
||||
/// Io driver based on Stdio::inherited(), to direct outputs/errors to stdio.
|
||||
///
|
||||
/// With this Io driver, all methods of [crate::Runc] can't capture the output/error messages.
|
||||
#[derive(Debug)]
|
||||
pub struct InheritedStdIo {}
|
||||
|
||||
impl InheritedStdIo {
|
||||
pub fn new() -> std::io::Result<Self> {
|
||||
Ok(InheritedStdIo {})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Io for InheritedStdIo {
|
||||
async fn set(&self, cmd: &mut Command) -> std::io::Result<()> {
|
||||
cmd.stdin(Stdio::null())
|
||||
.stdout(Stdio::inherit())
|
||||
.stderr(Stdio::inherit());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn close_after_start(&self) {}
|
||||
}
|
||||
|
||||
/// Io driver based on Stdio::piped(), to capture outputs/errors from runC.
|
||||
///
|
||||
/// With this Io driver, methods of [crate::Runc] may capture the output/error messages.
|
||||
#[derive(Debug)]
|
||||
pub struct PipedStdIo {}
|
||||
|
||||
impl PipedStdIo {
|
||||
pub fn new() -> std::io::Result<Self> {
|
||||
Ok(PipedStdIo {})
|
||||
}
|
||||
}
|
||||
#[async_trait]
|
||||
impl Io for PipedStdIo {
|
||||
async fn set(&self, cmd: &mut Command) -> std::io::Result<()> {
|
||||
cmd.stdin(Stdio::null())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn close_after_start(&self) {}
|
||||
}
|
||||
|
||||
/// FIFO for the scenario that set FIFO for command Io.
|
||||
#[derive(Debug)]
|
||||
pub struct FIFO {
|
||||
pub stdin: Option<String>,
|
||||
pub stdout: Option<String>,
|
||||
pub stderr: Option<String>,
|
||||
}
|
||||
#[async_trait]
|
||||
impl Io for FIFO {
|
||||
async fn set(&self, cmd: &mut Command) -> Result<()> {
|
||||
if let Some(path) = self.stdin.as_ref() {
|
||||
let stdin = OpenOptions::new()
|
||||
.read(true)
|
||||
.custom_flags(libc::O_NONBLOCK)
|
||||
.open(path)
|
||||
.await?;
|
||||
cmd.stdin(stdin.into_std().await);
|
||||
}
|
||||
|
||||
if let Some(path) = self.stdout.as_ref() {
|
||||
let stdout = OpenOptions::new().write(true).open(path).await?;
|
||||
cmd.stdout(stdout.into_std().await);
|
||||
}
|
||||
|
||||
if let Some(path) = self.stderr.as_ref() {
|
||||
let stderr = OpenOptions::new().write(true).open(path).await?;
|
||||
cmd.stderr(stderr.into_std().await);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn close_after_start(&self) {}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
#[test]
|
||||
fn test_io_option() {
|
||||
let opts = IOOption {
|
||||
open_stdin: false,
|
||||
open_stdout: false,
|
||||
open_stderr: false,
|
||||
};
|
||||
let io = PipedIo::new(1000, 1000, &opts).unwrap();
|
||||
|
||||
assert!(io.stdin().is_none());
|
||||
assert!(io.stdout().is_none());
|
||||
assert!(io.stderr().is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_null_io() {
|
||||
let io = NullIo::new().unwrap();
|
||||
assert!(io.stdin().is_none());
|
||||
assert!(io.stdout().is_none());
|
||||
assert!(io.stderr().is_none());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
pub mod io;
|
||||
mod pipe;
|
||||
mod runc;
|
||||
use std::{fmt::Debug, io::Result, os::fd::AsRawFd};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use log::debug;
|
||||
pub use pipe::Pipe;
|
||||
pub use runc::{DefaultExecutor, Spawner};
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use crate::Command;
|
||||
#[async_trait]
|
||||
pub trait Io: Debug + Send + Sync {
|
||||
fn stdin(&self) -> Option<Box<dyn AsyncWrite + Send + Sync + Unpin>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn stdout(&self) -> Option<Box<dyn AsyncRead + Send + Sync + Unpin>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn stderr(&self) -> Option<Box<dyn AsyncRead + Send + Sync + Unpin>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Set IO for passed command.
|
||||
/// Read side of stdin, write side of stdout and write side of stderr should be provided to command.
|
||||
async fn set(&self, cmd: &mut Command) -> Result<()>;
|
||||
|
||||
/// Only close write side (should be stdout/err "from" runc process)
|
||||
async fn close_after_start(&self);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PipedIo {
|
||||
pub stdin: Option<Pipe>,
|
||||
pub stdout: Option<Pipe>,
|
||||
pub stderr: Option<Pipe>,
|
||||
}
|
||||
#[async_trait]
|
||||
impl Io for PipedIo {
|
||||
fn stdin(&self) -> Option<Box<dyn AsyncWrite + Send + Sync + Unpin>> {
|
||||
self.stdin.as_ref().and_then(|pipe| {
|
||||
let fd = pipe.wr.as_raw_fd();
|
||||
tokio_pipe::PipeWrite::from_raw_fd_checked(fd)
|
||||
.map(|x| Box::new(x) as Box<dyn AsyncWrite + Send + Sync + Unpin>)
|
||||
.ok()
|
||||
})
|
||||
}
|
||||
|
||||
fn stdout(&self) -> Option<Box<dyn AsyncRead + Send + Sync + Unpin>> {
|
||||
self.stdout.as_ref().and_then(|pipe| {
|
||||
let fd = pipe.rd.as_raw_fd();
|
||||
tokio_pipe::PipeRead::from_raw_fd_checked(fd)
|
||||
.map(|x| Box::new(x) as Box<dyn AsyncRead + Send + Sync + Unpin>)
|
||||
.ok()
|
||||
})
|
||||
}
|
||||
|
||||
fn stderr(&self) -> Option<Box<dyn AsyncRead + Send + Sync + Unpin>> {
|
||||
self.stderr.as_ref().and_then(|pipe| {
|
||||
let fd = pipe.rd.as_raw_fd();
|
||||
tokio_pipe::PipeRead::from_raw_fd_checked(fd)
|
||||
.map(|x| Box::new(x) as Box<dyn AsyncRead + Send + Sync + Unpin>)
|
||||
.ok()
|
||||
})
|
||||
}
|
||||
|
||||
// Note that this internally use [`std::fs::File`]'s `try_clone()`.
|
||||
// Thus, the files passed to commands will be not closed after command exit.
|
||||
async fn set(&self, cmd: &mut Command) -> std::io::Result<()> {
|
||||
if let Some(p) = self.stdin.as_ref() {
|
||||
let pr = p.rd.try_clone()?;
|
||||
cmd.stdin(pr);
|
||||
}
|
||||
|
||||
if let Some(p) = self.stdout.as_ref() {
|
||||
let pw = p.wr.try_clone()?;
|
||||
cmd.stdout(pw);
|
||||
}
|
||||
|
||||
if let Some(p) = self.stderr.as_ref() {
|
||||
let pw = p.wr.try_clone()?;
|
||||
cmd.stdout(pw);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn close_after_start(&self) {
|
||||
if let Some(p) = self.stdout.as_ref() {
|
||||
nix::unistd::close(p.wr.as_raw_fd()).unwrap_or_else(|e| debug!("close stdout: {}", e));
|
||||
}
|
||||
|
||||
if let Some(p) = self.stderr.as_ref() {
|
||||
nix::unistd::close(p.wr.as_raw_fd()).unwrap_or_else(|e| debug!("close stderr: {}", e));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
use std::os::unix::io::OwnedFd;
|
||||
|
||||
use tokio::net::unix::pipe;
|
||||
|
||||
/// Struct to represent a pipe that can be used to transfer stdio inputs and outputs.
|
||||
///
|
||||
/// With this Io driver, methods of [crate::Runc] may capture the output/error messages.
|
||||
/// When one side of the pipe is closed, the state will be represented with [`None`].
|
||||
#[derive(Debug)]
|
||||
pub struct Pipe {
|
||||
pub rd: OwnedFd,
|
||||
pub wr: OwnedFd,
|
||||
}
|
||||
|
||||
impl Pipe {
|
||||
pub fn new() -> std::io::Result<Self> {
|
||||
let (tx, rx) = pipe::pipe()?;
|
||||
let rd = rx.into_blocking_fd()?;
|
||||
let wr = tx.into_blocking_fd()?;
|
||||
Ok(Self { rd, wr })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::os::fd::IntoRawFd;
|
||||
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_pipe_creation() {
|
||||
let pipe = Pipe::new().expect("Failed to create pipe");
|
||||
assert!(
|
||||
pipe.rd.into_raw_fd() >= 0,
|
||||
"Read file descriptor is invalid"
|
||||
);
|
||||
assert!(
|
||||
pipe.wr.into_raw_fd() >= 0,
|
||||
"Write file descriptor is invalid"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_pipe_write_read() {
|
||||
let pipe = Pipe::new().expect("Failed to create pipe");
|
||||
let mut read_end = pipe::Receiver::from_owned_fd(pipe.rd).unwrap();
|
||||
let mut write_end = pipe::Sender::from_owned_fd(pipe.wr).unwrap();
|
||||
let write_data = b"hello";
|
||||
|
||||
write_end
|
||||
.write_all(write_data)
|
||||
.await
|
||||
.expect("Failed to write to pipe");
|
||||
|
||||
let mut read_data = vec![0; write_data.len()];
|
||||
read_end
|
||||
.read_exact(&mut read_data)
|
||||
.await
|
||||
.expect("Failed to read from pipe");
|
||||
|
||||
assert_eq!(
|
||||
read_data, write_data,
|
||||
"Data read from pipe does not match data written"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_pipe_async_write_read() {
|
||||
let pipe = Pipe::new().expect("Failed to create pipe");
|
||||
let mut read_end = pipe::Receiver::from_owned_fd(pipe.rd).unwrap();
|
||||
let mut write_end = pipe::Sender::from_owned_fd(pipe.wr).unwrap();
|
||||
|
||||
let write_data = b"hello";
|
||||
tokio::spawn(async move {
|
||||
write_end
|
||||
.write_all(write_data)
|
||||
.await
|
||||
.expect("Failed to write to pipe");
|
||||
});
|
||||
|
||||
let mut read_data = vec![0; write_data.len()];
|
||||
read_end
|
||||
.read_exact(&mut read_data)
|
||||
.await
|
||||
.expect("Failed to read from pipe");
|
||||
|
||||
assert_eq!(
|
||||
&read_data, write_data,
|
||||
"Data read from pipe does not match data written"
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,525 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
use std::{fmt::Debug, path::Path, process::ExitStatus};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use log::debug;
|
||||
use oci_spec::runtime::{LinuxResources, Process};
|
||||
|
||||
use crate::{
|
||||
container::Container,
|
||||
error::Error,
|
||||
events,
|
||||
options::*,
|
||||
utils::{self, write_value_to_temp_file},
|
||||
Command, Response, Result, Runc,
|
||||
};
|
||||
|
||||
// a macro tool to cleanup the file with name $filename,
|
||||
// there is no async drop in async rust, so we have to call remove_file everytime
|
||||
// after a temp file created, before return of a function.
|
||||
// with this macro we don't have to write the match case codes everytime.
|
||||
macro_rules! tc {
|
||||
($b:expr, $filename: expr) => {
|
||||
match $b {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
let _ = tokio::fs::remove_file($filename).await;
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Async implementation for [Runc].
|
||||
///
|
||||
/// Note that you MUST use this client on tokio runtime, as this client internally use [`tokio::process::Command`]
|
||||
/// and some other utilities.
|
||||
impl Runc {
|
||||
pub(crate) async fn launch(&self, mut cmd: Command, combined_output: bool) -> Result<Response> {
|
||||
debug!("Execute command {:?}", cmd);
|
||||
unsafe {
|
||||
cmd.pre_exec(move || {
|
||||
#[cfg(target_os = "linux")]
|
||||
if let Ok(thp) = std::env::var("THP_DISABLED") {
|
||||
if let Ok(thp_disabled) = thp.parse::<bool>() {
|
||||
if let Err(e) = prctl::set_thp_disable(thp_disabled) {
|
||||
debug!("set_thp_disable err: {}", e);
|
||||
};
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
let (status, pid, stdout, stderr) = self.spawner.execute(cmd).await?;
|
||||
if status.success() {
|
||||
let output = if combined_output {
|
||||
stdout + stderr.as_str()
|
||||
} else {
|
||||
stdout
|
||||
};
|
||||
Ok(Response {
|
||||
pid,
|
||||
status,
|
||||
output,
|
||||
})
|
||||
} else {
|
||||
Err(Error::CommandFailed {
|
||||
status,
|
||||
stdout,
|
||||
stderr,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new container
|
||||
pub async fn create<P>(
|
||||
&self,
|
||||
id: &str,
|
||||
bundle: P,
|
||||
opts: Option<&CreateOpts>,
|
||||
) -> Result<Response>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut args = vec![
|
||||
"create".to_string(),
|
||||
"--bundle".to_string(),
|
||||
utils::abs_string(bundle)?,
|
||||
];
|
||||
if let Some(opts) = opts {
|
||||
args.append(&mut opts.args()?);
|
||||
}
|
||||
args.push(id.to_string());
|
||||
let mut cmd = self.command(&args)?;
|
||||
match opts {
|
||||
Some(CreateOpts { io: Some(io), .. }) => {
|
||||
io.set(&mut cmd).await.map_err(Error::UnavailableIO)?;
|
||||
let res = self.launch(cmd, true).await?;
|
||||
io.close_after_start().await;
|
||||
Ok(res)
|
||||
}
|
||||
_ => self.launch(cmd, true).await,
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete a container
|
||||
pub async fn delete(&self, id: &str, opts: Option<&DeleteOpts>) -> Result<()> {
|
||||
let mut args = vec!["delete".to_string()];
|
||||
if let Some(opts) = opts {
|
||||
args.append(&mut opts.args());
|
||||
}
|
||||
args.push(id.to_string());
|
||||
let _ = self.launch(self.command(&args)?, true).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return an event stream of container notifications
|
||||
pub async fn events(&self, _id: &str, _interval: &std::time::Duration) -> Result<()> {
|
||||
Err(Error::Unimplemented("events".to_string()))
|
||||
}
|
||||
|
||||
/// Execute an additional process inside the container
|
||||
pub async fn exec(&self, id: &str, spec: &Process, opts: Option<&ExecOpts>) -> Result<()> {
|
||||
let f = write_value_to_temp_file(spec).await?;
|
||||
let mut args = vec!["exec".to_string(), "--process".to_string(), f.clone()];
|
||||
if let Some(opts) = opts {
|
||||
args.append(&mut tc!(opts.args(), &f));
|
||||
}
|
||||
args.push(id.to_string());
|
||||
let mut cmd = self.command(&args)?;
|
||||
match opts {
|
||||
Some(ExecOpts { io: Some(io), .. }) => {
|
||||
tc!(
|
||||
io.set(&mut cmd)
|
||||
.await
|
||||
.map_err(|e| Error::IoSet(e.to_string())),
|
||||
&f
|
||||
);
|
||||
tc!(self.launch(cmd, true).await, &f);
|
||||
io.close_after_start().await;
|
||||
}
|
||||
_ => {
|
||||
tc!(self.launch(cmd, true).await, &f);
|
||||
}
|
||||
}
|
||||
let _ = tokio::fs::remove_file(&f).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send the specified signal to processes inside the container
|
||||
pub async fn kill(&self, id: &str, sig: u32, opts: Option<&KillOpts>) -> Result<()> {
|
||||
let mut args = vec!["kill".to_string()];
|
||||
if let Some(opts) = opts {
|
||||
args.append(&mut opts.args());
|
||||
}
|
||||
args.push(id.to_string());
|
||||
args.push(sig.to_string());
|
||||
let _ = self.launch(self.command(&args)?, true).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// List all containers associated with this runc instance
|
||||
pub async fn list(&self) -> Result<Vec<Container>> {
|
||||
let args = ["list".to_string(), "--format=json".to_string()];
|
||||
let res = self.launch(self.command(&args)?, true).await?;
|
||||
let output = res.output.trim();
|
||||
|
||||
// Ugly hack to work around golang
|
||||
Ok(if output == "null" {
|
||||
Vec::new()
|
||||
} else {
|
||||
serde_json::from_str(output).map_err(Error::JsonDeserializationFailed)?
|
||||
})
|
||||
}
|
||||
|
||||
/// Pause a container
|
||||
pub async fn pause(&self, id: &str) -> Result<()> {
|
||||
let args = ["pause".to_string(), id.to_string()];
|
||||
let _ = self.launch(self.command(&args)?, true).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resume a container
|
||||
pub async fn resume(&self, id: &str) -> Result<()> {
|
||||
let args = ["resume".to_string(), id.to_string()];
|
||||
let _ = self.launch(self.command(&args)?, true).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn checkpoint(&self) -> Result<()> {
|
||||
Err(Error::Unimplemented("checkpoint".to_string()))
|
||||
}
|
||||
|
||||
pub async fn restore(&self) -> Result<()> {
|
||||
Err(Error::Unimplemented("restore".to_string()))
|
||||
}
|
||||
|
||||
/// List all the processes inside the container, returning their pids
|
||||
pub async fn ps(&self, id: &str) -> Result<Vec<usize>> {
|
||||
let args = [
|
||||
"ps".to_string(),
|
||||
"--format=json".to_string(),
|
||||
id.to_string(),
|
||||
];
|
||||
let res = self.launch(self.command(&args)?, true).await?;
|
||||
let output = res.output.trim();
|
||||
|
||||
// Ugly hack to work around golang
|
||||
Ok(if output == "null" {
|
||||
Vec::new()
|
||||
} else {
|
||||
serde_json::from_str(output).map_err(Error::JsonDeserializationFailed)?
|
||||
})
|
||||
}
|
||||
|
||||
/// Run the create, start, delete lifecycle of the container and return its exit status
|
||||
pub async fn run<P>(&self, id: &str, bundle: P, opts: Option<&CreateOpts>) -> Result<()>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut args = vec![
|
||||
"run".to_string(),
|
||||
"--bundle".to_string(),
|
||||
utils::abs_string(bundle)?,
|
||||
];
|
||||
if let Some(opts) = opts {
|
||||
args.append(&mut opts.args()?);
|
||||
}
|
||||
args.push(id.to_string());
|
||||
let mut cmd = self.command(&args)?;
|
||||
if let Some(CreateOpts { io: Some(io), .. }) = opts {
|
||||
io.set(&mut cmd)
|
||||
.await
|
||||
.map_err(|e| Error::IoSet(e.to_string()))?;
|
||||
};
|
||||
let _ = self.launch(cmd, true).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Start an already created container
|
||||
pub async fn start(&self, id: &str) -> Result<()> {
|
||||
let args = vec!["start".to_string(), id.to_string()];
|
||||
let _ = self.launch(self.command(&args)?, true).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return the state of a container
|
||||
pub async fn state(&self, id: &str) -> Result<Container> {
|
||||
let args = vec!["state".to_string(), id.to_string()];
|
||||
let res = self.launch(self.command(&args)?, true).await?;
|
||||
serde_json::from_str(&res.output).map_err(Error::JsonDeserializationFailed)
|
||||
}
|
||||
|
||||
/// Return the latest statistics for a container
|
||||
pub async fn stats(&self, id: &str) -> Result<events::Stats> {
|
||||
let args = vec!["events".to_string(), "--stats".to_string(), id.to_string()];
|
||||
let res = self.launch(self.command(&args)?, true).await?;
|
||||
let event: events::Event =
|
||||
serde_json::from_str(&res.output).map_err(Error::JsonDeserializationFailed)?;
|
||||
if let Some(stats) = event.stats {
|
||||
Ok(stats)
|
||||
} else {
|
||||
Err(Error::MissingContainerStats)
|
||||
}
|
||||
}
|
||||
|
||||
/// Update a container with the provided resource spec
|
||||
pub async fn update(&self, id: &str, resources: &LinuxResources) -> Result<()> {
|
||||
let f = write_value_to_temp_file(resources).await?;
|
||||
let args = [
|
||||
"update".to_string(),
|
||||
"--resources".to_string(),
|
||||
f.to_string(),
|
||||
id.to_string(),
|
||||
];
|
||||
let _ = tc!(self.launch(self.command(&args)?, true).await, &f);
|
||||
let _ = tokio::fs::remove_file(&f).await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait Spawner: Debug {
|
||||
async fn execute(&self, cmd: Command) -> Result<(ExitStatus, u32, String, String)>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DefaultExecutor {}
|
||||
|
||||
#[async_trait]
|
||||
impl Spawner for DefaultExecutor {
|
||||
async fn execute(&self, cmd: Command) -> Result<(ExitStatus, u32, String, String)> {
|
||||
let mut cmd = cmd;
|
||||
let child = cmd.spawn().map_err(Error::ProcessSpawnFailed)?;
|
||||
let pid = child.id().unwrap();
|
||||
let result = child
|
||||
.wait_with_output()
|
||||
.await
|
||||
.map_err(Error::InvalidCommand)?;
|
||||
let status = result.status;
|
||||
let stdout = String::from_utf8_lossy(&result.stdout).to_string();
|
||||
let stderr = String::from_utf8_lossy(&result.stderr).to_string();
|
||||
Ok((status, pid, stdout, stderr))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(target_os = "linux")]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
error::Error,
|
||||
io::{InheritedStdIo, PipedStdIo},
|
||||
options::{CreateOpts, DeleteOpts, GlobalOpts},
|
||||
Runc,
|
||||
};
|
||||
|
||||
fn ok_client() -> Runc {
|
||||
GlobalOpts::new()
|
||||
.command("/bin/true")
|
||||
.build()
|
||||
.expect("unable to create runc instance")
|
||||
}
|
||||
|
||||
fn fail_client() -> Runc {
|
||||
GlobalOpts::new()
|
||||
.command("/bin/false")
|
||||
.build()
|
||||
.expect("unable to create runc instance")
|
||||
}
|
||||
|
||||
fn echo_client() -> Runc {
|
||||
GlobalOpts::new()
|
||||
.command("/bin/echo")
|
||||
.build()
|
||||
.expect("unable to create runc instance")
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_create() {
|
||||
let opts = CreateOpts::new();
|
||||
let ok_runc = ok_client();
|
||||
let ok_task = tokio::spawn(async move {
|
||||
let response = ok_runc
|
||||
.create("fake-id", "fake-bundle", Some(&opts))
|
||||
.await
|
||||
.expect("true failed.");
|
||||
assert_ne!(response.pid, 0);
|
||||
assert!(response.status.success());
|
||||
assert!(response.output.is_empty());
|
||||
});
|
||||
|
||||
let opts = CreateOpts::new();
|
||||
let fail_runc = fail_client();
|
||||
let fail_task = tokio::spawn(async move {
|
||||
match fail_runc
|
||||
.create("fake-id", "fake-bundle", Some(&opts))
|
||||
.await
|
||||
{
|
||||
Ok(_) => panic!("fail_runc returned exit status 0."),
|
||||
Err(Error::CommandFailed {
|
||||
status,
|
||||
stdout,
|
||||
stderr,
|
||||
}) => {
|
||||
if status.code().unwrap() == 1 && stdout.is_empty() && stderr.is_empty() {
|
||||
eprintln!("fail_runc succeeded.");
|
||||
} else {
|
||||
panic!("unexpected outputs from fail_runc.")
|
||||
}
|
||||
}
|
||||
Err(e) => panic!("unexpected error from fail_runc: {:?}", e),
|
||||
}
|
||||
});
|
||||
|
||||
ok_task.await.expect("ok_task failed.");
|
||||
fail_task.await.expect("fail_task unexpectedly succeeded.");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_start() {
|
||||
let ok_runc = ok_client();
|
||||
let ok_task = tokio::spawn(async move {
|
||||
ok_runc.start("fake-id").await.expect("true failed.");
|
||||
eprintln!("ok_runc succeeded.");
|
||||
});
|
||||
|
||||
let fail_runc = fail_client();
|
||||
let fail_task = tokio::spawn(async move {
|
||||
match fail_runc.start("fake-id").await {
|
||||
Ok(_) => panic!("fail_runc returned exit status 0."),
|
||||
Err(Error::CommandFailed {
|
||||
status,
|
||||
stdout,
|
||||
stderr,
|
||||
}) => {
|
||||
if status.code().unwrap() == 1 && stdout.is_empty() && stderr.is_empty() {
|
||||
eprintln!("fail_runc succeeded.");
|
||||
} else {
|
||||
panic!("unexpected outputs from fail_runc.")
|
||||
}
|
||||
}
|
||||
Err(e) => panic!("unexpected error from fail_runc: {:?}", e),
|
||||
}
|
||||
});
|
||||
|
||||
ok_task.await.expect("ok_task failed.");
|
||||
fail_task.await.expect("fail_task unexpectedly succeeded.");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_run() {
|
||||
let opts = CreateOpts::new();
|
||||
let ok_runc = ok_client();
|
||||
tokio::spawn(async move {
|
||||
ok_runc
|
||||
.create("fake-id", "fake-bundle", Some(&opts))
|
||||
.await
|
||||
.expect("true failed.");
|
||||
eprintln!("ok_runc succeeded.");
|
||||
});
|
||||
|
||||
let opts = CreateOpts::new();
|
||||
let fail_runc = fail_client();
|
||||
tokio::spawn(async move {
|
||||
match fail_runc
|
||||
.create("fake-id", "fake-bundle", Some(&opts))
|
||||
.await
|
||||
{
|
||||
Ok(_) => panic!("fail_runc returned exit status 0."),
|
||||
Err(Error::CommandFailed {
|
||||
status,
|
||||
stdout,
|
||||
stderr,
|
||||
}) => {
|
||||
if status.code().unwrap() == 1 && stdout.is_empty() && stderr.is_empty() {
|
||||
eprintln!("fail_runc succeeded.");
|
||||
} else {
|
||||
panic!("unexpected outputs from fail_runc.")
|
||||
}
|
||||
}
|
||||
Err(e) => panic!("unexpected error from fail_runc: {:?}", e),
|
||||
}
|
||||
})
|
||||
.await
|
||||
.expect("tokio spawn falied.");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_delete() {
|
||||
let opts = DeleteOpts::new();
|
||||
let ok_runc = ok_client();
|
||||
tokio::spawn(async move {
|
||||
ok_runc
|
||||
.delete("fake-id", Some(&opts))
|
||||
.await
|
||||
.expect("true failed.");
|
||||
eprintln!("ok_runc succeeded.");
|
||||
});
|
||||
|
||||
let opts = DeleteOpts::new();
|
||||
let fail_runc = fail_client();
|
||||
tokio::spawn(async move {
|
||||
match fail_runc.delete("fake-id", Some(&opts)).await {
|
||||
Ok(_) => panic!("fail_runc returned exit status 0."),
|
||||
Err(Error::CommandFailed {
|
||||
status,
|
||||
stdout,
|
||||
stderr,
|
||||
}) => {
|
||||
if status.code().unwrap() == 1 && stdout.is_empty() && stderr.is_empty() {
|
||||
eprintln!("fail_runc succeeded.");
|
||||
} else {
|
||||
panic!("unexpected outputs from fail_runc.")
|
||||
}
|
||||
}
|
||||
Err(e) => panic!("unexpected error from fail_runc: {:?}", e),
|
||||
}
|
||||
})
|
||||
.await
|
||||
.expect("tokio spawn falied.");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_output() {
|
||||
// test create cmd with inherit Io, expect empty cmd output
|
||||
let mut opts = CreateOpts::new();
|
||||
opts.io = Some(Arc::new(InheritedStdIo::new().unwrap()));
|
||||
let echo_runc = echo_client();
|
||||
let response = echo_runc
|
||||
.create("fake-id", "fake-bundle", Some(&opts))
|
||||
.await
|
||||
.expect("echo failed:");
|
||||
assert_ne!(response.pid, 0);
|
||||
assert!(response.status.success());
|
||||
assert!(response.output.is_empty());
|
||||
|
||||
// test create cmd with pipe Io, expect nonempty cmd output
|
||||
let mut opts = CreateOpts::new();
|
||||
opts.io = Some(Arc::new(PipedStdIo::new().unwrap()));
|
||||
let response = echo_runc
|
||||
.create("fake-id", "fake-bundle", Some(&opts))
|
||||
.await
|
||||
.expect("echo failed:");
|
||||
assert_ne!(response.pid, 0);
|
||||
assert!(response.status.success());
|
||||
assert!(!response.output.is_empty());
|
||||
}
|
||||
}
|
|
@ -39,28 +39,32 @@
|
|||
//! [go-runc](https://github.com/containerd/go-runc) for Go.
|
||||
use std::{
|
||||
fmt::{self, Debug, Display},
|
||||
path::{Path, PathBuf},
|
||||
path::PathBuf,
|
||||
process::{ExitStatus, Stdio},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use async_trait::async_trait;
|
||||
pub use crate::asynchronous::*;
|
||||
#[cfg(not(feature = "async"))]
|
||||
pub use crate::synchronous::*;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use log::debug;
|
||||
use oci_spec::runtime::{LinuxResources, Process};
|
||||
|
||||
use crate::{container::Container, error::Error, options::*, utils::write_value_to_temp_file};
|
||||
|
||||
pub mod asynchronous;
|
||||
pub mod container;
|
||||
pub mod error;
|
||||
pub mod events;
|
||||
pub mod io;
|
||||
#[cfg(not(feature = "async"))]
|
||||
pub mod synchronous;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub mod monitor;
|
||||
pub mod options;
|
||||
pub mod utils;
|
||||
|
||||
const JSON: &str = "json";
|
||||
const TEXT: &str = "text";
|
||||
|
||||
pub type Result<T> = std::result::Result<T, crate::error::Error>;
|
||||
|
||||
/// Response is for (pid, exit status, outputs).
|
||||
|
@ -123,910 +127,3 @@ impl Runc {
|
|||
Ok(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "async"))]
|
||||
impl Runc {
|
||||
fn launch(&self, cmd: Command, combined_output: bool) -> Result<Response> {
|
||||
let (status, pid, stdout, stderr) = self.spawner.execute(cmd)?;
|
||||
if status.success() {
|
||||
let output = if combined_output {
|
||||
stdout + stderr.as_str()
|
||||
} else {
|
||||
stdout
|
||||
};
|
||||
Ok(Response {
|
||||
pid,
|
||||
status,
|
||||
output,
|
||||
})
|
||||
} else {
|
||||
Err(Error::CommandFailed {
|
||||
status,
|
||||
stdout,
|
||||
stderr,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new container
|
||||
pub fn create<P>(&self, id: &str, bundle: P, opts: Option<&CreateOpts>) -> Result<Response>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut args = vec![
|
||||
"create".to_string(),
|
||||
"--bundle".to_string(),
|
||||
utils::abs_string(bundle)?,
|
||||
];
|
||||
if let Some(opts) = opts {
|
||||
args.append(&mut opts.args()?);
|
||||
}
|
||||
args.push(id.to_string());
|
||||
let mut cmd = self.command(&args)?;
|
||||
match opts {
|
||||
Some(CreateOpts { io: Some(io), .. }) => {
|
||||
io.set(&mut cmd).map_err(|e| Error::IoSet(e.to_string()))?;
|
||||
let res = self.launch(cmd, true)?;
|
||||
io.close_after_start();
|
||||
Ok(res)
|
||||
}
|
||||
_ => self.launch(cmd, true),
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete a container
|
||||
pub fn delete(&self, id: &str, opts: Option<&DeleteOpts>) -> Result<()> {
|
||||
let mut args = vec!["delete".to_string()];
|
||||
if let Some(opts) = opts {
|
||||
args.append(&mut opts.args());
|
||||
}
|
||||
args.push(id.to_string());
|
||||
self.launch(self.command(&args)?, true)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Execute an additional process inside the container
|
||||
pub fn exec(&self, id: &str, spec: &Process, opts: Option<&ExecOpts>) -> Result<()> {
|
||||
let (_temp_file, filename) = write_value_to_temp_file(spec)?;
|
||||
let mut args = vec!["exec".to_string(), "--process".to_string(), filename];
|
||||
if let Some(opts) = opts {
|
||||
args.append(&mut opts.args()?);
|
||||
}
|
||||
args.push(id.to_string());
|
||||
let mut cmd = self.command(&args)?;
|
||||
match opts {
|
||||
Some(ExecOpts { io: Some(io), .. }) => {
|
||||
io.set(&mut cmd).map_err(|e| Error::IoSet(e.to_string()))?;
|
||||
self.launch(cmd, true)?;
|
||||
io.close_after_start();
|
||||
}
|
||||
_ => {
|
||||
self.launch(cmd, true)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send the specified signal to processes inside the container
|
||||
pub fn kill(&self, id: &str, sig: u32, opts: Option<&KillOpts>) -> Result<()> {
|
||||
let mut args = vec!["kill".to_string()];
|
||||
if let Some(opts) = opts {
|
||||
args.append(&mut opts.args());
|
||||
}
|
||||
args.push(id.to_string());
|
||||
args.push(sig.to_string());
|
||||
let _ = self.launch(self.command(&args)?, true)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// List all containers associated with this runc instance
|
||||
pub fn list(&self) -> Result<Vec<Container>> {
|
||||
let args = ["list".to_string(), "--format=json".to_string()];
|
||||
let res = self.launch(self.command(&args)?, true)?;
|
||||
let output = res.output.trim();
|
||||
|
||||
// Ugly hack to work around golang
|
||||
Ok(if output == "null" {
|
||||
Vec::new()
|
||||
} else {
|
||||
serde_json::from_str(output).map_err(Error::JsonDeserializationFailed)?
|
||||
})
|
||||
}
|
||||
|
||||
/// Pause a container
|
||||
pub fn pause(&self, id: &str) -> Result<()> {
|
||||
let args = ["pause".to_string(), id.to_string()];
|
||||
let _ = self.launch(self.command(&args)?, true)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resume a container
|
||||
pub fn resume(&self, id: &str) -> Result<()> {
|
||||
let args = ["resume".to_string(), id.to_string()];
|
||||
let _ = self.launch(self.command(&args)?, true)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn checkpoint(&self) -> Result<()> {
|
||||
Err(Error::Unimplemented("checkpoint".to_string()))
|
||||
}
|
||||
|
||||
pub fn restore(&self) -> Result<()> {
|
||||
Err(Error::Unimplemented("restore".to_string()))
|
||||
}
|
||||
|
||||
/// List all the processes inside the container, returning their pids
|
||||
pub fn ps(&self, id: &str) -> Result<Vec<usize>> {
|
||||
let args = [
|
||||
"ps".to_string(),
|
||||
"--format=json".to_string(),
|
||||
id.to_string(),
|
||||
];
|
||||
let res = self.launch(self.command(&args)?, false)?;
|
||||
let output = res.output.trim();
|
||||
|
||||
// Ugly hack to work around golang
|
||||
Ok(if output == "null" {
|
||||
Vec::new()
|
||||
} else {
|
||||
serde_json::from_str(output).map_err(Error::JsonDeserializationFailed)?
|
||||
})
|
||||
}
|
||||
|
||||
/// Run the create, start, delete lifecycle of the container and return its exit status
|
||||
pub fn run<P>(&self, id: &str, bundle: P, opts: Option<&CreateOpts>) -> Result<Response>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut args = vec![
|
||||
"run".to_string(),
|
||||
"--bundle".to_string(),
|
||||
utils::abs_string(bundle)?,
|
||||
];
|
||||
if let Some(opts) = opts {
|
||||
args.append(&mut opts.args()?);
|
||||
}
|
||||
args.push(id.to_string());
|
||||
let mut cmd = self.command(&args)?;
|
||||
if let Some(CreateOpts { io: Some(io), .. }) = opts {
|
||||
io.set(&mut cmd).map_err(|e| Error::IoSet(e.to_string()))?;
|
||||
};
|
||||
self.launch(cmd, true)
|
||||
}
|
||||
|
||||
/// Start an already created container
|
||||
pub fn start(&self, id: &str) -> Result<Response> {
|
||||
let args = ["start".to_string(), id.to_string()];
|
||||
self.launch(self.command(&args)?, true)
|
||||
}
|
||||
|
||||
/// Return the state of a container
|
||||
pub fn state(&self, id: &str) -> Result<Container> {
|
||||
let args = ["state".to_string(), id.to_string()];
|
||||
let res = self.launch(self.command(&args)?, true)?;
|
||||
serde_json::from_str(&res.output).map_err(Error::JsonDeserializationFailed)
|
||||
}
|
||||
|
||||
/// Return the latest statistics for a container
|
||||
pub fn stats(&self, id: &str) -> Result<events::Stats> {
|
||||
let args = vec!["events".to_string(), "--stats".to_string(), id.to_string()];
|
||||
let res = self.launch(self.command(&args)?, true)?;
|
||||
let event: events::Event =
|
||||
serde_json::from_str(&res.output).map_err(Error::JsonDeserializationFailed)?;
|
||||
if let Some(stats) = event.stats {
|
||||
Ok(stats)
|
||||
} else {
|
||||
Err(Error::MissingContainerStats)
|
||||
}
|
||||
}
|
||||
|
||||
/// Update a container with the provided resource spec
|
||||
pub fn update(&self, id: &str, resources: &LinuxResources) -> Result<()> {
|
||||
let (_temp_file, filename) = write_value_to_temp_file(resources)?;
|
||||
let args = [
|
||||
"update".to_string(),
|
||||
"--resources".to_string(),
|
||||
filename,
|
||||
id.to_string(),
|
||||
];
|
||||
self.launch(self.command(&args)?, true)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// a macro tool to cleanup the file with name $filename,
|
||||
// there is no async drop in async rust, so we have to call remove_file everytime
|
||||
// after a temp file created, before return of a function.
|
||||
// with this macro we don't have to write the match case codes everytime.
|
||||
#[cfg(feature = "async")]
|
||||
macro_rules! tc {
|
||||
($b:expr, $filename: expr) => {
|
||||
match $b {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
let _ = tokio::fs::remove_file($filename).await;
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "async"))]
|
||||
pub trait Spawner: Debug {
|
||||
fn execute(&self, cmd: Command) -> Result<(ExitStatus, u32, String, String)>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[async_trait]
|
||||
pub trait Spawner: Debug {
|
||||
async fn execute(&self, cmd: Command) -> Result<(ExitStatus, u32, String, String)>;
|
||||
}
|
||||
|
||||
/// Async implementation for [Runc].
|
||||
///
|
||||
/// Note that you MUST use this client on tokio runtime, as this client internally use [`tokio::process::Command`]
|
||||
/// and some other utilities.
|
||||
#[cfg(feature = "async")]
|
||||
impl Runc {
|
||||
async fn launch(&self, mut cmd: Command, combined_output: bool) -> Result<Response> {
|
||||
debug!("Execute command {:?}", cmd);
|
||||
unsafe {
|
||||
cmd.pre_exec(move || {
|
||||
#[cfg(target_os = "linux")]
|
||||
if let Ok(thp) = std::env::var("THP_DISABLED") {
|
||||
if let Ok(thp_disabled) = thp.parse::<bool>() {
|
||||
if let Err(e) = prctl::set_thp_disable(thp_disabled) {
|
||||
debug!("set_thp_disable err: {}", e);
|
||||
};
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
let (status, pid, stdout, stderr) = self.spawner.execute(cmd).await?;
|
||||
if status.success() {
|
||||
let output = if combined_output {
|
||||
stdout + stderr.as_str()
|
||||
} else {
|
||||
stdout
|
||||
};
|
||||
Ok(Response {
|
||||
pid,
|
||||
status,
|
||||
output,
|
||||
})
|
||||
} else {
|
||||
Err(Error::CommandFailed {
|
||||
status,
|
||||
stdout,
|
||||
stderr,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new container
|
||||
pub async fn create<P>(
|
||||
&self,
|
||||
id: &str,
|
||||
bundle: P,
|
||||
opts: Option<&CreateOpts>,
|
||||
) -> Result<Response>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut args = vec![
|
||||
"create".to_string(),
|
||||
"--bundle".to_string(),
|
||||
utils::abs_string(bundle)?,
|
||||
];
|
||||
if let Some(opts) = opts {
|
||||
args.append(&mut opts.args()?);
|
||||
}
|
||||
args.push(id.to_string());
|
||||
let mut cmd = self.command(&args)?;
|
||||
match opts {
|
||||
Some(CreateOpts { io: Some(io), .. }) => {
|
||||
io.set(&mut cmd).map_err(Error::UnavailableIO)?;
|
||||
let res = self.launch(cmd, true).await?;
|
||||
io.close_after_start();
|
||||
Ok(res)
|
||||
}
|
||||
_ => self.launch(cmd, true).await,
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete a container
|
||||
pub async fn delete(&self, id: &str, opts: Option<&DeleteOpts>) -> Result<()> {
|
||||
let mut args = vec!["delete".to_string()];
|
||||
if let Some(opts) = opts {
|
||||
args.append(&mut opts.args());
|
||||
}
|
||||
args.push(id.to_string());
|
||||
let _ = self.launch(self.command(&args)?, true).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return an event stream of container notifications
|
||||
pub async fn events(&self, _id: &str, _interval: &std::time::Duration) -> Result<()> {
|
||||
Err(Error::Unimplemented("events".to_string()))
|
||||
}
|
||||
|
||||
/// Execute an additional process inside the container
|
||||
pub async fn exec(&self, id: &str, spec: &Process, opts: Option<&ExecOpts>) -> Result<()> {
|
||||
let f = write_value_to_temp_file(spec).await?;
|
||||
let mut args = vec!["exec".to_string(), "--process".to_string(), f.clone()];
|
||||
if let Some(opts) = opts {
|
||||
args.append(&mut tc!(opts.args(), &f));
|
||||
}
|
||||
args.push(id.to_string());
|
||||
let mut cmd = self.command(&args)?;
|
||||
match opts {
|
||||
Some(ExecOpts { io: Some(io), .. }) => {
|
||||
tc!(
|
||||
io.set(&mut cmd).map_err(|e| Error::IoSet(e.to_string())),
|
||||
&f
|
||||
);
|
||||
tc!(self.launch(cmd, true).await, &f);
|
||||
io.close_after_start();
|
||||
}
|
||||
_ => {
|
||||
tc!(self.launch(cmd, true).await, &f);
|
||||
}
|
||||
}
|
||||
let _ = tokio::fs::remove_file(&f).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send the specified signal to processes inside the container
|
||||
pub async fn kill(&self, id: &str, sig: u32, opts: Option<&KillOpts>) -> Result<()> {
|
||||
let mut args = vec!["kill".to_string()];
|
||||
if let Some(opts) = opts {
|
||||
args.append(&mut opts.args());
|
||||
}
|
||||
args.push(id.to_string());
|
||||
args.push(sig.to_string());
|
||||
let _ = self.launch(self.command(&args)?, true).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// List all containers associated with this runc instance
|
||||
pub async fn list(&self) -> Result<Vec<Container>> {
|
||||
let args = ["list".to_string(), "--format=json".to_string()];
|
||||
let res = self.launch(self.command(&args)?, true).await?;
|
||||
let output = res.output.trim();
|
||||
|
||||
// Ugly hack to work around golang
|
||||
Ok(if output == "null" {
|
||||
Vec::new()
|
||||
} else {
|
||||
serde_json::from_str(output).map_err(Error::JsonDeserializationFailed)?
|
||||
})
|
||||
}
|
||||
|
||||
/// Pause a container
|
||||
pub async fn pause(&self, id: &str) -> Result<()> {
|
||||
let args = ["pause".to_string(), id.to_string()];
|
||||
let _ = self.launch(self.command(&args)?, true).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resume a container
|
||||
pub async fn resume(&self, id: &str) -> Result<()> {
|
||||
let args = ["resume".to_string(), id.to_string()];
|
||||
let _ = self.launch(self.command(&args)?, true).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn checkpoint(&self) -> Result<()> {
|
||||
Err(Error::Unimplemented("checkpoint".to_string()))
|
||||
}
|
||||
|
||||
pub async fn restore(&self) -> Result<()> {
|
||||
Err(Error::Unimplemented("restore".to_string()))
|
||||
}
|
||||
|
||||
/// List all the processes inside the container, returning their pids
|
||||
pub async fn ps(&self, id: &str) -> Result<Vec<usize>> {
|
||||
let args = [
|
||||
"ps".to_string(),
|
||||
"--format=json".to_string(),
|
||||
id.to_string(),
|
||||
];
|
||||
let res = self.launch(self.command(&args)?, true).await?;
|
||||
let output = res.output.trim();
|
||||
|
||||
// Ugly hack to work around golang
|
||||
Ok(if output == "null" {
|
||||
Vec::new()
|
||||
} else {
|
||||
serde_json::from_str(output).map_err(Error::JsonDeserializationFailed)?
|
||||
})
|
||||
}
|
||||
|
||||
/// Run the create, start, delete lifecycle of the container and return its exit status
|
||||
pub async fn run<P>(&self, id: &str, bundle: P, opts: Option<&CreateOpts>) -> Result<()>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut args = vec![
|
||||
"run".to_string(),
|
||||
"--bundle".to_string(),
|
||||
utils::abs_string(bundle)?,
|
||||
];
|
||||
if let Some(opts) = opts {
|
||||
args.append(&mut opts.args()?);
|
||||
}
|
||||
args.push(id.to_string());
|
||||
let mut cmd = self.command(&args)?;
|
||||
if let Some(CreateOpts { io: Some(io), .. }) = opts {
|
||||
io.set(&mut cmd).map_err(|e| Error::IoSet(e.to_string()))?;
|
||||
};
|
||||
let _ = self.launch(cmd, true).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Start an already created container
|
||||
pub async fn start(&self, id: &str) -> Result<()> {
|
||||
let args = vec!["start".to_string(), id.to_string()];
|
||||
let _ = self.launch(self.command(&args)?, true).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return the state of a container
|
||||
pub async fn state(&self, id: &str) -> Result<Container> {
|
||||
let args = vec!["state".to_string(), id.to_string()];
|
||||
let res = self.launch(self.command(&args)?, true).await?;
|
||||
serde_json::from_str(&res.output).map_err(Error::JsonDeserializationFailed)
|
||||
}
|
||||
|
||||
/// Return the latest statistics for a container
|
||||
pub async fn stats(&self, id: &str) -> Result<events::Stats> {
|
||||
let args = vec!["events".to_string(), "--stats".to_string(), id.to_string()];
|
||||
let res = self.launch(self.command(&args)?, true).await?;
|
||||
let event: events::Event =
|
||||
serde_json::from_str(&res.output).map_err(Error::JsonDeserializationFailed)?;
|
||||
if let Some(stats) = event.stats {
|
||||
Ok(stats)
|
||||
} else {
|
||||
Err(Error::MissingContainerStats)
|
||||
}
|
||||
}
|
||||
|
||||
/// Update a container with the provided resource spec
|
||||
pub async fn update(&self, id: &str, resources: &LinuxResources) -> Result<()> {
|
||||
let f = write_value_to_temp_file(resources).await?;
|
||||
let args = [
|
||||
"update".to_string(),
|
||||
"--resources".to_string(),
|
||||
f.to_string(),
|
||||
id.to_string(),
|
||||
];
|
||||
let _ = tc!(self.launch(self.command(&args)?, true).await, &f);
|
||||
let _ = tokio::fs::remove_file(&f).await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DefaultExecutor {}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[async_trait]
|
||||
impl Spawner for DefaultExecutor {
|
||||
async fn execute(&self, cmd: Command) -> Result<(ExitStatus, u32, String, String)> {
|
||||
let mut cmd = cmd;
|
||||
let child = cmd.spawn().map_err(Error::ProcessSpawnFailed)?;
|
||||
let pid = child.id().unwrap();
|
||||
let result = child
|
||||
.wait_with_output()
|
||||
.await
|
||||
.map_err(Error::InvalidCommand)?;
|
||||
let status = result.status;
|
||||
let stdout = String::from_utf8_lossy(&result.stdout).to_string();
|
||||
let stderr = String::from_utf8_lossy(&result.stderr).to_string();
|
||||
Ok((status, pid, stdout, stderr))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "async"))]
|
||||
impl Spawner for DefaultExecutor {
|
||||
fn execute(&self, cmd: Command) -> Result<(ExitStatus, u32, String, String)> {
|
||||
let mut cmd = cmd;
|
||||
let child = cmd.spawn().map_err(Error::ProcessSpawnFailed)?;
|
||||
let pid = child.id();
|
||||
let result = child.wait_with_output().map_err(Error::InvalidCommand)?;
|
||||
let status = result.status;
|
||||
let stdout = String::from_utf8_lossy(&result.stdout).to_string();
|
||||
let stderr = String::from_utf8_lossy(&result.stderr).to_string();
|
||||
Ok((status, pid, stdout, stderr))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(all(target_os = "linux", not(feature = "async")))]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::{
|
||||
io::{InheritedStdIo, PipedStdIo},
|
||||
*,
|
||||
};
|
||||
|
||||
fn ok_client() -> Runc {
|
||||
GlobalOpts::new()
|
||||
.command("/bin/true")
|
||||
.build()
|
||||
.expect("unable to create runc instance")
|
||||
}
|
||||
|
||||
fn fail_client() -> Runc {
|
||||
GlobalOpts::new()
|
||||
.command("/bin/false")
|
||||
.build()
|
||||
.expect("unable to create runc instance")
|
||||
}
|
||||
|
||||
fn echo_client() -> Runc {
|
||||
GlobalOpts::new()
|
||||
.command("/bin/echo")
|
||||
.build()
|
||||
.expect("unable to create runc instance")
|
||||
}
|
||||
|
||||
fn dummy_process() -> Process {
|
||||
serde_json::from_str(
|
||||
"
|
||||
{
|
||||
\"user\": {
|
||||
\"uid\": 1000,
|
||||
\"gid\": 1000
|
||||
},
|
||||
\"cwd\": \"/path/to/dir\"
|
||||
}",
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create() {
|
||||
let opts = CreateOpts::new();
|
||||
let ok_runc = ok_client();
|
||||
let response = ok_runc
|
||||
.create("fake-id", "fake-bundle", Some(&opts))
|
||||
.expect("true failed.");
|
||||
assert_ne!(response.pid, 0);
|
||||
assert!(response.status.success());
|
||||
assert!(response.output.is_empty());
|
||||
|
||||
let fail_runc = fail_client();
|
||||
match fail_runc.create("fake-id", "fake-bundle", Some(&opts)) {
|
||||
Ok(_) => panic!("fail_runc returned exit status 0."),
|
||||
Err(Error::CommandFailed {
|
||||
status,
|
||||
stdout,
|
||||
stderr,
|
||||
}) => {
|
||||
if status.code().unwrap() == 1 && stdout.is_empty() && stderr.is_empty() {
|
||||
eprintln!("fail_runc succeeded.");
|
||||
} else {
|
||||
panic!("unexpected outputs from fail_runc.")
|
||||
}
|
||||
}
|
||||
Err(e) => panic!("unexpected error from fail_runc: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_run() {
|
||||
let opts = CreateOpts::new();
|
||||
let ok_runc = ok_client();
|
||||
let response = ok_runc
|
||||
.run("fake-id", "fake-bundle", Some(&opts))
|
||||
.expect("true failed.");
|
||||
assert_ne!(response.pid, 0);
|
||||
assert!(response.status.success());
|
||||
assert!(response.output.is_empty());
|
||||
|
||||
let fail_runc = fail_client();
|
||||
match fail_runc.run("fake-id", "fake-bundle", Some(&opts)) {
|
||||
Ok(_) => panic!("fail_runc returned exit status 0."),
|
||||
Err(Error::CommandFailed {
|
||||
status,
|
||||
stdout,
|
||||
stderr,
|
||||
}) => {
|
||||
if status.code().unwrap() == 1 && stdout.is_empty() && stderr.is_empty() {
|
||||
eprintln!("fail_runc succeeded.");
|
||||
} else {
|
||||
panic!("unexpected outputs from fail_runc.")
|
||||
}
|
||||
}
|
||||
Err(e) => panic!("unexpected error from fail_runc: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exec() {
|
||||
let opts = ExecOpts::new();
|
||||
let ok_runc = ok_client();
|
||||
let proc = dummy_process();
|
||||
ok_runc
|
||||
.exec("fake-id", &proc, Some(&opts))
|
||||
.expect("true failed.");
|
||||
eprintln!("ok_runc succeeded.");
|
||||
|
||||
let fail_runc = fail_client();
|
||||
match fail_runc.exec("fake-id", &proc, Some(&opts)) {
|
||||
Ok(_) => panic!("fail_runc returned exit status 0."),
|
||||
Err(Error::CommandFailed {
|
||||
status,
|
||||
stdout,
|
||||
stderr,
|
||||
}) => {
|
||||
if status.code().unwrap() == 1 && stdout.is_empty() && stderr.is_empty() {
|
||||
eprintln!("fail_runc succeeded.");
|
||||
} else {
|
||||
panic!("unexpected outputs from fail_runc.")
|
||||
}
|
||||
}
|
||||
Err(e) => panic!("unexpected error from fail_runc: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete() {
|
||||
let opts = DeleteOpts::new();
|
||||
let ok_runc = ok_client();
|
||||
ok_runc
|
||||
.delete("fake-id", Some(&opts))
|
||||
.expect("true failed.");
|
||||
eprintln!("ok_runc succeeded.");
|
||||
|
||||
let fail_runc = fail_client();
|
||||
match fail_runc.delete("fake-id", Some(&opts)) {
|
||||
Ok(_) => panic!("fail_runc returned exit status 0."),
|
||||
Err(Error::CommandFailed {
|
||||
status,
|
||||
stdout,
|
||||
stderr,
|
||||
}) => {
|
||||
if status.code().unwrap() == 1 && stdout.is_empty() && stderr.is_empty() {
|
||||
eprintln!("fail_runc succeeded.");
|
||||
} else {
|
||||
panic!("unexpected outputs from fail_runc.")
|
||||
}
|
||||
}
|
||||
Err(e) => panic!("unexpected error from fail_runc: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_output() {
|
||||
// test create cmd with inherit Io, expect empty cmd output
|
||||
let mut opts = CreateOpts::new();
|
||||
opts.io = Some(Arc::new(InheritedStdIo::new().unwrap()));
|
||||
let echo_runc = echo_client();
|
||||
let response = echo_runc
|
||||
.create("fake-id", "fake-bundle", Some(&opts))
|
||||
.expect("echo failed.");
|
||||
assert_ne!(response.pid, 0);
|
||||
assert!(response.status.success());
|
||||
assert!(response.output.is_empty());
|
||||
|
||||
// test create cmd with pipe Io, expect nonempty cmd output
|
||||
let mut opts = CreateOpts::new();
|
||||
opts.io = Some(Arc::new(PipedStdIo::new().unwrap()));
|
||||
let echo_runc = echo_client();
|
||||
let response = echo_runc
|
||||
.create("fake-id", "fake-bundle", Some(&opts))
|
||||
.expect("echo failed.");
|
||||
assert_ne!(response.pid, 0);
|
||||
assert!(response.status.success());
|
||||
assert!(!response.output.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
/// Tokio tests
|
||||
#[cfg(test)]
|
||||
#[cfg(all(target_os = "linux", feature = "async"))]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::{
|
||||
io::{InheritedStdIo, PipedStdIo},
|
||||
*,
|
||||
};
|
||||
|
||||
fn ok_client() -> Runc {
|
||||
GlobalOpts::new()
|
||||
.command("/bin/true")
|
||||
.build()
|
||||
.expect("unable to create runc instance")
|
||||
}
|
||||
|
||||
fn fail_client() -> Runc {
|
||||
GlobalOpts::new()
|
||||
.command("/bin/false")
|
||||
.build()
|
||||
.expect("unable to create runc instance")
|
||||
}
|
||||
|
||||
fn echo_client() -> Runc {
|
||||
GlobalOpts::new()
|
||||
.command("/bin/echo")
|
||||
.build()
|
||||
.expect("unable to create runc instance")
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_create() {
|
||||
let opts = CreateOpts::new();
|
||||
let ok_runc = ok_client();
|
||||
let ok_task = tokio::spawn(async move {
|
||||
let response = ok_runc
|
||||
.create("fake-id", "fake-bundle", Some(&opts))
|
||||
.await
|
||||
.expect("true failed.");
|
||||
assert_ne!(response.pid, 0);
|
||||
assert!(response.status.success());
|
||||
assert!(response.output.is_empty());
|
||||
});
|
||||
|
||||
let opts = CreateOpts::new();
|
||||
let fail_runc = fail_client();
|
||||
let fail_task = tokio::spawn(async move {
|
||||
match fail_runc
|
||||
.create("fake-id", "fake-bundle", Some(&opts))
|
||||
.await
|
||||
{
|
||||
Ok(_) => panic!("fail_runc returned exit status 0."),
|
||||
Err(Error::CommandFailed {
|
||||
status,
|
||||
stdout,
|
||||
stderr,
|
||||
}) => {
|
||||
if status.code().unwrap() == 1 && stdout.is_empty() && stderr.is_empty() {
|
||||
eprintln!("fail_runc succeeded.");
|
||||
} else {
|
||||
panic!("unexpected outputs from fail_runc.")
|
||||
}
|
||||
}
|
||||
Err(e) => panic!("unexpected error from fail_runc: {:?}", e),
|
||||
}
|
||||
});
|
||||
|
||||
ok_task.await.expect("ok_task failed.");
|
||||
fail_task.await.expect("fail_task unexpectedly succeeded.");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_start() {
|
||||
let ok_runc = ok_client();
|
||||
let ok_task = tokio::spawn(async move {
|
||||
ok_runc.start("fake-id").await.expect("true failed.");
|
||||
eprintln!("ok_runc succeeded.");
|
||||
});
|
||||
|
||||
let fail_runc = fail_client();
|
||||
let fail_task = tokio::spawn(async move {
|
||||
match fail_runc.start("fake-id").await {
|
||||
Ok(_) => panic!("fail_runc returned exit status 0."),
|
||||
Err(Error::CommandFailed {
|
||||
status,
|
||||
stdout,
|
||||
stderr,
|
||||
}) => {
|
||||
if status.code().unwrap() == 1 && stdout.is_empty() && stderr.is_empty() {
|
||||
eprintln!("fail_runc succeeded.");
|
||||
} else {
|
||||
panic!("unexpected outputs from fail_runc.")
|
||||
}
|
||||
}
|
||||
Err(e) => panic!("unexpected error from fail_runc: {:?}", e),
|
||||
}
|
||||
});
|
||||
|
||||
ok_task.await.expect("ok_task failed.");
|
||||
fail_task.await.expect("fail_task unexpectedly succeeded.");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_run() {
|
||||
let opts = CreateOpts::new();
|
||||
let ok_runc = ok_client();
|
||||
tokio::spawn(async move {
|
||||
ok_runc
|
||||
.create("fake-id", "fake-bundle", Some(&opts))
|
||||
.await
|
||||
.expect("true failed.");
|
||||
eprintln!("ok_runc succeeded.");
|
||||
});
|
||||
|
||||
let opts = CreateOpts::new();
|
||||
let fail_runc = fail_client();
|
||||
tokio::spawn(async move {
|
||||
match fail_runc
|
||||
.create("fake-id", "fake-bundle", Some(&opts))
|
||||
.await
|
||||
{
|
||||
Ok(_) => panic!("fail_runc returned exit status 0."),
|
||||
Err(Error::CommandFailed {
|
||||
status,
|
||||
stdout,
|
||||
stderr,
|
||||
}) => {
|
||||
if status.code().unwrap() == 1 && stdout.is_empty() && stderr.is_empty() {
|
||||
eprintln!("fail_runc succeeded.");
|
||||
} else {
|
||||
panic!("unexpected outputs from fail_runc.")
|
||||
}
|
||||
}
|
||||
Err(e) => panic!("unexpected error from fail_runc: {:?}", e),
|
||||
}
|
||||
})
|
||||
.await
|
||||
.expect("tokio spawn falied.");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_delete() {
|
||||
let opts = DeleteOpts::new();
|
||||
let ok_runc = ok_client();
|
||||
tokio::spawn(async move {
|
||||
ok_runc
|
||||
.delete("fake-id", Some(&opts))
|
||||
.await
|
||||
.expect("true failed.");
|
||||
eprintln!("ok_runc succeeded.");
|
||||
});
|
||||
|
||||
let opts = DeleteOpts::new();
|
||||
let fail_runc = fail_client();
|
||||
tokio::spawn(async move {
|
||||
match fail_runc.delete("fake-id", Some(&opts)).await {
|
||||
Ok(_) => panic!("fail_runc returned exit status 0."),
|
||||
Err(Error::CommandFailed {
|
||||
status,
|
||||
stdout,
|
||||
stderr,
|
||||
}) => {
|
||||
if status.code().unwrap() == 1 && stdout.is_empty() && stderr.is_empty() {
|
||||
eprintln!("fail_runc succeeded.");
|
||||
} else {
|
||||
panic!("unexpected outputs from fail_runc.")
|
||||
}
|
||||
}
|
||||
Err(e) => panic!("unexpected error from fail_runc: {:?}", e),
|
||||
}
|
||||
})
|
||||
.await
|
||||
.expect("tokio spawn falied.");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_output() {
|
||||
// test create cmd with inherit Io, expect empty cmd output
|
||||
let mut opts = CreateOpts::new();
|
||||
opts.io = Some(Arc::new(InheritedStdIo::new().unwrap()));
|
||||
let echo_runc = echo_client();
|
||||
let response = echo_runc
|
||||
.create("fake-id", "fake-bundle", Some(&opts))
|
||||
.await
|
||||
.expect("echo failed:");
|
||||
assert_ne!(response.pid, 0);
|
||||
assert!(response.status.success());
|
||||
assert!(response.output.is_empty());
|
||||
|
||||
// test create cmd with pipe Io, expect nonempty cmd output
|
||||
let mut opts = CreateOpts::new();
|
||||
opts.io = Some(Arc::new(PipedStdIo::new().unwrap()));
|
||||
let response = echo_runc
|
||||
.create("fake-id", "fake-bundle", Some(&opts))
|
||||
.await
|
||||
.expect("echo failed:");
|
||||
assert_ne!(response.pid, 0);
|
||||
assert!(response.status.success());
|
||||
assert!(!response.output.is_empty());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ use std::{
|
|||
time::Duration,
|
||||
};
|
||||
|
||||
use crate::{error::Error, io::Io, utils, DefaultExecutor, LogFormat, Runc, Spawner};
|
||||
use crate::{error::Error, utils, DefaultExecutor, Io, LogFormat, Runc, Spawner};
|
||||
|
||||
// constants for log format
|
||||
pub const JSON: &str = "json";
|
||||
|
@ -485,7 +485,7 @@ mod tests {
|
|||
fn create_opts_test() {
|
||||
assert_eq!(
|
||||
CreateOpts::new().args().expect(ARGS_FAIL_MSG),
|
||||
vec![String::new(); 0]
|
||||
Vec::<String>::new()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
|
@ -536,7 +536,7 @@ mod tests {
|
|||
fn exec_opts_test() {
|
||||
assert_eq!(
|
||||
ExecOpts::new().args().expect(ARGS_FAIL_MSG),
|
||||
vec![String::new(); 0]
|
||||
Vec::<String>::new()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
|
@ -576,10 +576,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn delete_opts_test() {
|
||||
assert_eq!(
|
||||
DeleteOpts::new().force(false).args(),
|
||||
vec![String::new(); 0]
|
||||
);
|
||||
assert_eq!(DeleteOpts::new().force(false).args(), Vec::<String>::new());
|
||||
|
||||
assert_eq!(
|
||||
DeleteOpts::new().force(true).args(),
|
||||
|
@ -589,7 +586,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn kill_opts_test() {
|
||||
assert_eq!(KillOpts::new().all(false).args(), vec![String::new(); 0]);
|
||||
assert_eq!(KillOpts::new().all(false).args(), Vec::<String>::new());
|
||||
|
||||
assert_eq!(KillOpts::new().all(true).args(), vec!["--all".to_string()],);
|
||||
}
|
||||
|
|
|
@ -13,8 +13,7 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#[cfg(not(feature = "async"))]
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
fs::{File, OpenOptions},
|
||||
|
@ -24,58 +23,10 @@ use std::{
|
|||
sync::Mutex,
|
||||
};
|
||||
|
||||
use log::debug;
|
||||
use nix::unistd::{Gid, Uid};
|
||||
use os_pipe::{PipeReader, PipeWriter};
|
||||
#[cfg(feature = "async")]
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use crate::Command;
|
||||
|
||||
pub trait Io: Debug + Send + Sync {
|
||||
/// Return write side of stdin
|
||||
#[cfg(not(feature = "async"))]
|
||||
fn stdin(&self) -> Option<Box<dyn Write + Send + Sync>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Return read side of stdout
|
||||
#[cfg(not(feature = "async"))]
|
||||
fn stdout(&self) -> Option<Box<dyn Read + Send>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Return read side of stderr
|
||||
#[cfg(not(feature = "async"))]
|
||||
fn stderr(&self) -> Option<Box<dyn Read + Send>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Return write side of stdin
|
||||
#[cfg(feature = "async")]
|
||||
fn stdin(&self) -> Option<Box<dyn AsyncWrite + Send + Sync + Unpin>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Return read side of stdout
|
||||
#[cfg(feature = "async")]
|
||||
fn stdout(&self) -> Option<Box<dyn AsyncRead + Send + Sync + Unpin>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Return read side of stderr
|
||||
#[cfg(feature = "async")]
|
||||
fn stderr(&self) -> Option<Box<dyn AsyncRead + Send + Sync + Unpin>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Set IO for passed command.
|
||||
/// Read side of stdin, write side of stdout and write side of stderr should be provided to command.
|
||||
fn set(&self, cmd: &mut Command) -> Result<()>;
|
||||
|
||||
/// Only close write side (should be stdout/err "from" runc process)
|
||||
fn close_after_start(&self);
|
||||
}
|
||||
use super::Io;
|
||||
use crate::{Command, Pipe, PipedIo};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IOOption {
|
||||
|
@ -94,49 +45,28 @@ impl Default for IOOption {
|
|||
}
|
||||
}
|
||||
|
||||
/// Struct to represent a pipe that can be used to transfer stdio inputs and outputs.
|
||||
///
|
||||
/// With this Io driver, methods of [crate::Runc] may capture the output/error messages.
|
||||
/// When one side of the pipe is closed, the state will be represented with [`None`].
|
||||
#[derive(Debug)]
|
||||
pub struct Pipe {
|
||||
rd: PipeReader,
|
||||
wr: PipeWriter,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PipedIo {
|
||||
stdin: Option<Pipe>,
|
||||
stdout: Option<Pipe>,
|
||||
stderr: Option<Pipe>,
|
||||
}
|
||||
|
||||
impl Pipe {
|
||||
fn new() -> std::io::Result<Self> {
|
||||
let (rd, wr) = os_pipe::pipe()?;
|
||||
Ok(Self { rd, wr })
|
||||
}
|
||||
}
|
||||
|
||||
impl PipedIo {
|
||||
pub fn new(uid: u32, gid: u32, opts: &IOOption) -> std::io::Result<Self> {
|
||||
Ok(Self {
|
||||
stdin: Self::create_pipe(uid, gid, opts.open_stdin, true)?,
|
||||
stdout: Self::create_pipe(uid, gid, opts.open_stdout, false)?,
|
||||
stderr: Self::create_pipe(uid, gid, opts.open_stderr, false)?,
|
||||
stdin: if opts.open_stdin {
|
||||
Self::create_pipe(uid, gid, true)?
|
||||
} else {
|
||||
None
|
||||
},
|
||||
stdout: if opts.open_stdout {
|
||||
Self::create_pipe(uid, gid, true)?
|
||||
} else {
|
||||
None
|
||||
},
|
||||
stderr: if opts.open_stderr {
|
||||
Self::create_pipe(uid, gid, true)?
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn create_pipe(
|
||||
uid: u32,
|
||||
gid: u32,
|
||||
enabled: bool,
|
||||
stdin: bool,
|
||||
) -> std::io::Result<Option<Pipe>> {
|
||||
if !enabled {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
fn create_pipe(uid: u32, gid: u32, stdin: bool) -> std::io::Result<Option<Pipe>> {
|
||||
let pipe = Pipe::new()?;
|
||||
let uid = Some(Uid::from_raw(uid));
|
||||
let gid = Some(Gid::from_raw(gid));
|
||||
|
@ -151,99 +81,6 @@ impl PipedIo {
|
|||
}
|
||||
}
|
||||
|
||||
impl Io for PipedIo {
|
||||
#[cfg(not(feature = "async"))]
|
||||
fn stdin(&self) -> Option<Box<dyn Write + Send + Sync>> {
|
||||
self.stdin.as_ref().and_then(|pipe| {
|
||||
pipe.wr
|
||||
.try_clone()
|
||||
.map(|x| Box::new(x) as Box<dyn Write + Send + Sync>)
|
||||
.ok()
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn stdin(&self) -> Option<Box<dyn AsyncWrite + Send + Sync + Unpin>> {
|
||||
self.stdin.as_ref().and_then(|pipe| {
|
||||
let fd = pipe.wr.as_raw_fd();
|
||||
tokio_pipe::PipeWrite::from_raw_fd_checked(fd)
|
||||
.map(|x| Box::new(x) as Box<dyn AsyncWrite + Send + Sync + Unpin>)
|
||||
.ok()
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "async"))]
|
||||
fn stdout(&self) -> Option<Box<dyn Read + Send>> {
|
||||
self.stdout.as_ref().and_then(|pipe| {
|
||||
pipe.rd
|
||||
.try_clone()
|
||||
.map(|x| Box::new(x) as Box<dyn Read + Send>)
|
||||
.ok()
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn stdout(&self) -> Option<Box<dyn AsyncRead + Send + Sync + Unpin>> {
|
||||
self.stdout.as_ref().and_then(|pipe| {
|
||||
let fd = pipe.rd.as_raw_fd();
|
||||
tokio_pipe::PipeRead::from_raw_fd_checked(fd)
|
||||
.map(|x| Box::new(x) as Box<dyn AsyncRead + Send + Sync + Unpin>)
|
||||
.ok()
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "async"))]
|
||||
fn stderr(&self) -> Option<Box<dyn Read + Send>> {
|
||||
self.stderr.as_ref().and_then(|pipe| {
|
||||
pipe.rd
|
||||
.try_clone()
|
||||
.map(|x| Box::new(x) as Box<dyn Read + Send>)
|
||||
.ok()
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn stderr(&self) -> Option<Box<dyn AsyncRead + Send + Sync + Unpin>> {
|
||||
self.stderr.as_ref().and_then(|pipe| {
|
||||
let fd = pipe.rd.as_raw_fd();
|
||||
tokio_pipe::PipeRead::from_raw_fd_checked(fd)
|
||||
.map(|x| Box::new(x) as Box<dyn AsyncRead + Send + Sync + Unpin>)
|
||||
.ok()
|
||||
})
|
||||
}
|
||||
|
||||
// Note that this internally use [`std::fs::File`]'s `try_clone()`.
|
||||
// Thus, the files passed to commands will be not closed after command exit.
|
||||
fn set(&self, cmd: &mut Command) -> std::io::Result<()> {
|
||||
if let Some(p) = self.stdin.as_ref() {
|
||||
let pr = p.rd.try_clone()?;
|
||||
cmd.stdin(pr);
|
||||
}
|
||||
|
||||
if let Some(p) = self.stdout.as_ref() {
|
||||
let pw = p.wr.try_clone()?;
|
||||
cmd.stdout(pw);
|
||||
}
|
||||
|
||||
if let Some(p) = self.stderr.as_ref() {
|
||||
let pw = p.wr.try_clone()?;
|
||||
cmd.stdout(pw);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn close_after_start(&self) {
|
||||
if let Some(p) = self.stdout.as_ref() {
|
||||
nix::unistd::close(p.wr.as_raw_fd()).unwrap_or_else(|e| debug!("close stdout: {}", e));
|
||||
}
|
||||
|
||||
if let Some(p) = self.stderr.as_ref() {
|
||||
nix::unistd::close(p.wr.as_raw_fd()).unwrap_or_else(|e| debug!("close stderr: {}", e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// IO driver to direct output/error messages to /dev/null.
|
||||
///
|
||||
/// With this Io driver, all methods of [crate::Runc] can't capture the output/error messages.
|
||||
|
@ -375,9 +212,10 @@ mod tests {
|
|||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(not(feature = "async"))]
|
||||
#[test]
|
||||
fn test_create_piped_io() {
|
||||
use std::io::{Read, Write};
|
||||
|
||||
let opts = IOOption::default();
|
||||
let uid = nix::unistd::getuid();
|
||||
let gid = nix::unistd::getgid();
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
pub mod io;
|
||||
mod pipe;
|
||||
mod runc;
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
io::{Read, Result, Write},
|
||||
os::fd::AsRawFd,
|
||||
};
|
||||
|
||||
use log::debug;
|
||||
pub use pipe::Pipe;
|
||||
pub use runc::{DefaultExecutor, Spawner};
|
||||
|
||||
use crate::Command;
|
||||
|
||||
pub trait Io: Debug + Send + Sync {
|
||||
/// Return write side of stdin
|
||||
fn stdin(&self) -> Option<Box<dyn Write + Send + Sync>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Return read side of stdout
|
||||
fn stdout(&self) -> Option<Box<dyn Read + Send>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Return read side of stderr
|
||||
fn stderr(&self) -> Option<Box<dyn Read + Send>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Set IO for passed command.
|
||||
/// Read side of stdin, write side of stdout and write side of stderr should be provided to command.
|
||||
fn set(&self, cmd: &mut Command) -> Result<()>;
|
||||
|
||||
/// Only close write side (should be stdout/err "from" runc process)
|
||||
fn close_after_start(&self);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PipedIo {
|
||||
pub stdin: Option<Pipe>,
|
||||
pub stdout: Option<Pipe>,
|
||||
pub stderr: Option<Pipe>,
|
||||
}
|
||||
|
||||
impl Io for PipedIo {
|
||||
fn stdin(&self) -> Option<Box<dyn Write + Send + Sync>> {
|
||||
self.stdin.as_ref().and_then(|pipe| {
|
||||
pipe.wr
|
||||
.try_clone()
|
||||
.map(|x| Box::new(x) as Box<dyn Write + Send + Sync>)
|
||||
.ok()
|
||||
})
|
||||
}
|
||||
|
||||
fn stdout(&self) -> Option<Box<dyn Read + Send>> {
|
||||
self.stdout.as_ref().and_then(|pipe| {
|
||||
pipe.rd
|
||||
.try_clone()
|
||||
.map(|x| Box::new(x) as Box<dyn Read + Send>)
|
||||
.ok()
|
||||
})
|
||||
}
|
||||
|
||||
fn stderr(&self) -> Option<Box<dyn Read + Send>> {
|
||||
self.stderr.as_ref().and_then(|pipe| {
|
||||
pipe.rd
|
||||
.try_clone()
|
||||
.map(|x| Box::new(x) as Box<dyn Read + Send>)
|
||||
.ok()
|
||||
})
|
||||
}
|
||||
// Note that this internally use [`std::fs::File`]'s `try_clone()`.
|
||||
// Thus, the files passed to commands will be not closed after command exit.
|
||||
fn set(&self, cmd: &mut Command) -> std::io::Result<()> {
|
||||
if let Some(p) = self.stdin.as_ref() {
|
||||
let pr = p.rd.try_clone()?;
|
||||
cmd.stdin(pr);
|
||||
}
|
||||
|
||||
if let Some(p) = self.stdout.as_ref() {
|
||||
let pw = p.wr.try_clone()?;
|
||||
cmd.stdout(pw);
|
||||
}
|
||||
|
||||
if let Some(p) = self.stderr.as_ref() {
|
||||
let pw = p.wr.try_clone()?;
|
||||
cmd.stdout(pw);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn close_after_start(&self) {
|
||||
if let Some(p) = self.stdout.as_ref() {
|
||||
nix::unistd::close(p.wr.as_raw_fd()).unwrap_or_else(|e| debug!("close stdout: {}", e));
|
||||
}
|
||||
|
||||
if let Some(p) = self.stderr.as_ref() {
|
||||
nix::unistd::close(p.wr.as_raw_fd()).unwrap_or_else(|e| debug!("close stderr: {}", e));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,7 +14,17 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
#[cfg(windows)]
|
||||
pub(crate) mod windows;
|
||||
#[cfg(windows)]
|
||||
pub use crate::sys::windows::NamedPipeLogger;
|
||||
use os_pipe::{pipe, PipeReader, PipeWriter};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Pipe {
|
||||
pub rd: PipeReader,
|
||||
pub wr: PipeWriter,
|
||||
}
|
||||
|
||||
impl Pipe {
|
||||
pub fn new() -> std::io::Result<Self> {
|
||||
let (rd, wr) = pipe()?;
|
||||
Ok(Self { rd, wr })
|
||||
}
|
||||
}
|
|
@ -0,0 +1,445 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
use std::{fmt::Debug, path::Path, process::ExitStatus};
|
||||
|
||||
use oci_spec::runtime::{LinuxResources, Process};
|
||||
|
||||
use crate::{
|
||||
container::Container,
|
||||
error::Error,
|
||||
events,
|
||||
options::*,
|
||||
utils::{self, write_value_to_temp_file},
|
||||
Command, Response, Result, Runc,
|
||||
};
|
||||
|
||||
impl Runc {
|
||||
pub(crate) fn launch(&self, cmd: Command, combined_output: bool) -> Result<Response> {
|
||||
let (status, pid, stdout, stderr) = self.spawner.execute(cmd)?;
|
||||
if status.success() {
|
||||
let output = if combined_output {
|
||||
stdout + stderr.as_str()
|
||||
} else {
|
||||
stdout
|
||||
};
|
||||
Ok(Response {
|
||||
pid,
|
||||
status,
|
||||
output,
|
||||
})
|
||||
} else {
|
||||
Err(Error::CommandFailed {
|
||||
status,
|
||||
stdout,
|
||||
stderr,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new container
|
||||
pub fn create<P>(&self, id: &str, bundle: P, opts: Option<&CreateOpts>) -> Result<Response>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut args = vec![
|
||||
"create".to_string(),
|
||||
"--bundle".to_string(),
|
||||
utils::abs_string(bundle)?,
|
||||
];
|
||||
if let Some(opts) = opts {
|
||||
args.append(&mut opts.args()?);
|
||||
}
|
||||
args.push(id.to_string());
|
||||
let mut cmd = self.command(&args)?;
|
||||
match opts {
|
||||
Some(CreateOpts { io: Some(io), .. }) => {
|
||||
io.set(&mut cmd).map_err(|e| Error::IoSet(e.to_string()))?;
|
||||
let res = self.launch(cmd, true)?;
|
||||
io.close_after_start();
|
||||
Ok(res)
|
||||
}
|
||||
_ => self.launch(cmd, true),
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete a container
|
||||
pub fn delete(&self, id: &str, opts: Option<&DeleteOpts>) -> Result<()> {
|
||||
let mut args = vec!["delete".to_string()];
|
||||
if let Some(opts) = opts {
|
||||
args.append(&mut opts.args());
|
||||
}
|
||||
args.push(id.to_string());
|
||||
self.launch(self.command(&args)?, true)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Execute an additional process inside the container
|
||||
pub fn exec(&self, id: &str, spec: &Process, opts: Option<&ExecOpts>) -> Result<()> {
|
||||
let (_temp_file, filename) = write_value_to_temp_file(spec)?;
|
||||
let mut args = vec!["exec".to_string(), "--process".to_string(), filename];
|
||||
if let Some(opts) = opts {
|
||||
args.append(&mut opts.args()?);
|
||||
}
|
||||
args.push(id.to_string());
|
||||
let mut cmd = self.command(&args)?;
|
||||
match opts {
|
||||
Some(ExecOpts { io: Some(io), .. }) => {
|
||||
io.set(&mut cmd).map_err(|e| Error::IoSet(e.to_string()))?;
|
||||
self.launch(cmd, true)?;
|
||||
io.close_after_start();
|
||||
}
|
||||
_ => {
|
||||
self.launch(cmd, true)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send the specified signal to processes inside the container
|
||||
pub fn kill(&self, id: &str, sig: u32, opts: Option<&KillOpts>) -> Result<()> {
|
||||
let mut args = vec!["kill".to_string()];
|
||||
if let Some(opts) = opts {
|
||||
args.append(&mut opts.args());
|
||||
}
|
||||
args.push(id.to_string());
|
||||
args.push(sig.to_string());
|
||||
let _ = self.launch(self.command(&args)?, true)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// List all containers associated with this runc instance
|
||||
pub fn list(&self) -> Result<Vec<Container>> {
|
||||
let args = ["list".to_string(), "--format=json".to_string()];
|
||||
let res = self.launch(self.command(&args)?, true)?;
|
||||
let output = res.output.trim();
|
||||
|
||||
// Ugly hack to work around golang
|
||||
Ok(if output == "null" {
|
||||
Vec::new()
|
||||
} else {
|
||||
serde_json::from_str(output).map_err(Error::JsonDeserializationFailed)?
|
||||
})
|
||||
}
|
||||
|
||||
/// Pause a container
|
||||
pub fn pause(&self, id: &str) -> Result<()> {
|
||||
let args = ["pause".to_string(), id.to_string()];
|
||||
let _ = self.launch(self.command(&args)?, true)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resume a container
|
||||
pub fn resume(&self, id: &str) -> Result<()> {
|
||||
let args = ["resume".to_string(), id.to_string()];
|
||||
let _ = self.launch(self.command(&args)?, true)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn checkpoint(&self) -> Result<()> {
|
||||
Err(Error::Unimplemented("checkpoint".to_string()))
|
||||
}
|
||||
|
||||
pub fn restore(&self) -> Result<()> {
|
||||
Err(Error::Unimplemented("restore".to_string()))
|
||||
}
|
||||
|
||||
/// List all the processes inside the container, returning their pids
|
||||
pub fn ps(&self, id: &str) -> Result<Vec<usize>> {
|
||||
let args = [
|
||||
"ps".to_string(),
|
||||
"--format=json".to_string(),
|
||||
id.to_string(),
|
||||
];
|
||||
let res = self.launch(self.command(&args)?, false)?;
|
||||
let output = res.output.trim();
|
||||
|
||||
// Ugly hack to work around golang
|
||||
Ok(if output == "null" {
|
||||
Vec::new()
|
||||
} else {
|
||||
serde_json::from_str(output).map_err(Error::JsonDeserializationFailed)?
|
||||
})
|
||||
}
|
||||
|
||||
/// Run the create, start, delete lifecycle of the container and return its exit status
|
||||
pub fn run<P>(&self, id: &str, bundle: P, opts: Option<&CreateOpts>) -> Result<Response>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut args = vec![
|
||||
"run".to_string(),
|
||||
"--bundle".to_string(),
|
||||
utils::abs_string(bundle)?,
|
||||
];
|
||||
if let Some(opts) = opts {
|
||||
args.append(&mut opts.args()?);
|
||||
}
|
||||
args.push(id.to_string());
|
||||
let mut cmd = self.command(&args)?;
|
||||
if let Some(CreateOpts { io: Some(io), .. }) = opts {
|
||||
io.set(&mut cmd).map_err(|e| Error::IoSet(e.to_string()))?;
|
||||
};
|
||||
self.launch(cmd, true)
|
||||
}
|
||||
|
||||
/// Start an already created container
|
||||
pub fn start(&self, id: &str) -> Result<Response> {
|
||||
let args = ["start".to_string(), id.to_string()];
|
||||
self.launch(self.command(&args)?, true)
|
||||
}
|
||||
|
||||
/// Return the state of a container
|
||||
pub fn state(&self, id: &str) -> Result<Container> {
|
||||
let args = ["state".to_string(), id.to_string()];
|
||||
let res = self.launch(self.command(&args)?, true)?;
|
||||
serde_json::from_str(&res.output).map_err(Error::JsonDeserializationFailed)
|
||||
}
|
||||
|
||||
/// Return the latest statistics for a container
|
||||
pub fn stats(&self, id: &str) -> Result<events::Stats> {
|
||||
let args = vec!["events".to_string(), "--stats".to_string(), id.to_string()];
|
||||
let res = self.launch(self.command(&args)?, true)?;
|
||||
let event: events::Event =
|
||||
serde_json::from_str(&res.output).map_err(Error::JsonDeserializationFailed)?;
|
||||
if let Some(stats) = event.stats {
|
||||
Ok(stats)
|
||||
} else {
|
||||
Err(Error::MissingContainerStats)
|
||||
}
|
||||
}
|
||||
|
||||
/// Update a container with the provided resource spec
|
||||
pub fn update(&self, id: &str, resources: &LinuxResources) -> Result<()> {
|
||||
let (_temp_file, filename) = write_value_to_temp_file(resources)?;
|
||||
let args = [
|
||||
"update".to_string(),
|
||||
"--resources".to_string(),
|
||||
filename,
|
||||
id.to_string(),
|
||||
];
|
||||
self.launch(self.command(&args)?, true)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Spawner: Debug {
|
||||
fn execute(&self, cmd: Command) -> Result<(ExitStatus, u32, String, String)>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DefaultExecutor {}
|
||||
|
||||
impl Spawner for DefaultExecutor {
|
||||
fn execute(&self, cmd: Command) -> Result<(ExitStatus, u32, String, String)> {
|
||||
let mut cmd = cmd;
|
||||
let child = cmd.spawn().map_err(Error::ProcessSpawnFailed)?;
|
||||
let pid = child.id();
|
||||
let result = child.wait_with_output().map_err(Error::InvalidCommand)?;
|
||||
let status = result.status;
|
||||
let stdout = String::from_utf8_lossy(&result.stdout).to_string();
|
||||
let stderr = String::from_utf8_lossy(&result.stderr).to_string();
|
||||
Ok((status, pid, stdout, stderr))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(target_os = "linux")]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use oci_spec::runtime::Process;
|
||||
|
||||
use crate::{
|
||||
error::Error,
|
||||
io::{InheritedStdIo, PipedStdIo},
|
||||
options::{CreateOpts, DeleteOpts, ExecOpts, GlobalOpts},
|
||||
Runc,
|
||||
};
|
||||
|
||||
fn ok_client() -> Runc {
|
||||
GlobalOpts::new()
|
||||
.command("/bin/true")
|
||||
.build()
|
||||
.expect("unable to create runc instance")
|
||||
}
|
||||
|
||||
fn fail_client() -> Runc {
|
||||
GlobalOpts::new()
|
||||
.command("/bin/false")
|
||||
.build()
|
||||
.expect("unable to create runc instance")
|
||||
}
|
||||
|
||||
fn echo_client() -> Runc {
|
||||
GlobalOpts::new()
|
||||
.command("/bin/echo")
|
||||
.build()
|
||||
.expect("unable to create runc instance")
|
||||
}
|
||||
|
||||
fn dummy_process() -> Process {
|
||||
serde_json::from_str(
|
||||
"
|
||||
{
|
||||
\"user\": {
|
||||
\"uid\": 1000,
|
||||
\"gid\": 1000
|
||||
},
|
||||
\"cwd\": \"/path/to/dir\"
|
||||
}",
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create() {
|
||||
let opts = CreateOpts::new();
|
||||
let ok_runc = ok_client();
|
||||
let response = ok_runc
|
||||
.create("fake-id", "fake-bundle", Some(&opts))
|
||||
.expect("true failed.");
|
||||
assert_ne!(response.pid, 0);
|
||||
assert!(response.status.success());
|
||||
assert!(response.output.is_empty());
|
||||
|
||||
let fail_runc = fail_client();
|
||||
match fail_runc.create("fake-id", "fake-bundle", Some(&opts)) {
|
||||
Ok(_) => panic!("fail_runc returned exit status 0."),
|
||||
Err(Error::CommandFailed {
|
||||
status,
|
||||
stdout,
|
||||
stderr,
|
||||
}) => {
|
||||
if status.code().unwrap() == 1 && stdout.is_empty() && stderr.is_empty() {
|
||||
eprintln!("fail_runc succeeded.");
|
||||
} else {
|
||||
panic!("unexpected outputs from fail_runc.")
|
||||
}
|
||||
}
|
||||
Err(e) => panic!("unexpected error from fail_runc: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_run() {
|
||||
let opts = CreateOpts::new();
|
||||
let ok_runc = ok_client();
|
||||
let response = ok_runc
|
||||
.run("fake-id", "fake-bundle", Some(&opts))
|
||||
.expect("true failed.");
|
||||
assert_ne!(response.pid, 0);
|
||||
assert!(response.status.success());
|
||||
assert!(response.output.is_empty());
|
||||
|
||||
let fail_runc = fail_client();
|
||||
match fail_runc.run("fake-id", "fake-bundle", Some(&opts)) {
|
||||
Ok(_) => panic!("fail_runc returned exit status 0."),
|
||||
Err(Error::CommandFailed {
|
||||
status,
|
||||
stdout,
|
||||
stderr,
|
||||
}) => {
|
||||
if status.code().unwrap() == 1 && stdout.is_empty() && stderr.is_empty() {
|
||||
eprintln!("fail_runc succeeded.");
|
||||
} else {
|
||||
panic!("unexpected outputs from fail_runc.")
|
||||
}
|
||||
}
|
||||
Err(e) => panic!("unexpected error from fail_runc: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exec() {
|
||||
let opts = ExecOpts::new();
|
||||
let ok_runc = ok_client();
|
||||
let proc = dummy_process();
|
||||
ok_runc
|
||||
.exec("fake-id", &proc, Some(&opts))
|
||||
.expect("true failed.");
|
||||
eprintln!("ok_runc succeeded.");
|
||||
|
||||
let fail_runc = fail_client();
|
||||
match fail_runc.exec("fake-id", &proc, Some(&opts)) {
|
||||
Ok(_) => panic!("fail_runc returned exit status 0."),
|
||||
Err(Error::CommandFailed {
|
||||
status,
|
||||
stdout,
|
||||
stderr,
|
||||
}) => {
|
||||
if status.code().unwrap() == 1 && stdout.is_empty() && stderr.is_empty() {
|
||||
eprintln!("fail_runc succeeded.");
|
||||
} else {
|
||||
panic!("unexpected outputs from fail_runc.")
|
||||
}
|
||||
}
|
||||
Err(e) => panic!("unexpected error from fail_runc: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete() {
|
||||
let opts = DeleteOpts::new();
|
||||
let ok_runc = ok_client();
|
||||
ok_runc
|
||||
.delete("fake-id", Some(&opts))
|
||||
.expect("true failed.");
|
||||
eprintln!("ok_runc succeeded.");
|
||||
|
||||
let fail_runc = fail_client();
|
||||
match fail_runc.delete("fake-id", Some(&opts)) {
|
||||
Ok(_) => panic!("fail_runc returned exit status 0."),
|
||||
Err(Error::CommandFailed {
|
||||
status,
|
||||
stdout,
|
||||
stderr,
|
||||
}) => {
|
||||
if status.code().unwrap() == 1 && stdout.is_empty() && stderr.is_empty() {
|
||||
eprintln!("fail_runc succeeded.");
|
||||
} else {
|
||||
panic!("unexpected outputs from fail_runc.")
|
||||
}
|
||||
}
|
||||
Err(e) => panic!("unexpected error from fail_runc: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_output() {
|
||||
// test create cmd with inherit Io, expect empty cmd output
|
||||
let mut opts = CreateOpts::new();
|
||||
opts.io = Some(Arc::new(InheritedStdIo::new().unwrap()));
|
||||
let echo_runc = echo_client();
|
||||
let response = echo_runc
|
||||
.create("fake-id", "fake-bundle", Some(&opts))
|
||||
.expect("echo failed.");
|
||||
assert_ne!(response.pid, 0);
|
||||
assert!(response.status.success());
|
||||
assert!(response.output.is_empty());
|
||||
|
||||
// test create cmd with pipe Io, expect nonempty cmd output
|
||||
let mut opts = CreateOpts::new();
|
||||
opts.io = Some(Arc::new(PipedStdIo::new().unwrap()));
|
||||
let echo_runc = echo_client();
|
||||
let response = echo_runc
|
||||
.create("fake-id", "fake-bundle", Some(&opts))
|
||||
.expect("echo failed.");
|
||||
assert_ne!(response.pid, 0);
|
||||
assert!(response.status.success());
|
||||
assert!(!response.output.is_empty());
|
||||
}
|
||||
}
|
|
@ -98,6 +98,7 @@ pub async fn write_value_to_temp_file<T: Serialize>(value: &T) -> Result<String,
|
|||
let filename = format!("{}/runc-process-{}", xdg_runtime_dir(), Uuid::new_v4());
|
||||
let mut f = tokio::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.open(&filename)
|
||||
.await
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "containerd-shim-protos"
|
||||
version = "0.7.1"
|
||||
version = "0.8.0"
|
||||
authors = [
|
||||
"Maksym Pavlenko <pavlenko.maksym@gmail.com>",
|
||||
"The containerd Authors",
|
||||
|
@ -49,11 +49,11 @@ required-features = ["async"]
|
|||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true, optional = true }
|
||||
protobuf = "=3.5"
|
||||
ttrpc = "0.8.2"
|
||||
protobuf = "3.7.2"
|
||||
ttrpc = "0.8.3"
|
||||
|
||||
[build-dependencies]
|
||||
ttrpc-codegen = "0.4.2"
|
||||
ttrpc-codegen = "0.6.0"
|
||||
|
||||
[dev-dependencies]
|
||||
ctrlc = { version = "3.0", features = ["termination"] }
|
||||
|
|
|
@ -32,6 +32,7 @@ fn main() {
|
|||
"vendor/github.com/containerd/containerd/protobuf/plugin/fieldpath.proto",
|
||||
"vendor/github.com/containerd/containerd/api/types/mount.proto",
|
||||
"vendor/github.com/containerd/containerd/api/types/task/task.proto",
|
||||
"vendor/github.com/containerd/containerd/api/types/introspection.proto",
|
||||
#[cfg(feature = "sandbox")]
|
||||
"vendor/github.com/containerd/containerd/api/types/platform.proto",
|
||||
],
|
||||
|
|
|
@ -34,6 +34,9 @@ pub mod fieldpath {
|
|||
include!(concat!(env!("OUT_DIR"), "/types/fieldpath.rs"));
|
||||
}
|
||||
|
||||
pub mod introspection {
|
||||
include!(concat!(env!("OUT_DIR"), "/types/introspection.rs"));
|
||||
}
|
||||
#[cfg(feature = "sandbox")]
|
||||
pub mod platform {
|
||||
include!(concat!(env!("OUT_DIR"), "/types/platform.rs"));
|
||||
|
|
46
crates/shim-protos/vendor/github.com/containerd/containerd/api/types/introspection.proto
generated
vendored
Normal file
46
crates/shim-protos/vendor/github.com/containerd/containerd/api/types/introspection.proto
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package containerd.types;
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
|
||||
option go_package = "github.com/containerd/containerd/api/types;types";
|
||||
|
||||
message RuntimeRequest {
|
||||
string runtime_path = 1;
|
||||
// Options correspond to CreateTaskRequest.options.
|
||||
// This is needed to pass the runc binary path, etc.
|
||||
google.protobuf.Any options = 2;
|
||||
}
|
||||
|
||||
message RuntimeVersion {
|
||||
string version = 1;
|
||||
string revision = 2;
|
||||
}
|
||||
|
||||
message RuntimeInfo {
|
||||
string name = 1;
|
||||
RuntimeVersion version = 2;
|
||||
// Options correspond to RuntimeInfoRequest.Options (contains runc binary path, etc.)
|
||||
google.protobuf.Any options = 3;
|
||||
// OCI-compatible runtimes should use https://github.com/opencontainers/runtime-spec/blob/main/features.md
|
||||
google.protobuf.Any features = 4;
|
||||
// Annotations of the shim. Irrelevant to features.Annotations.
|
||||
map<string, string> annotations = 5;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "containerd-shim"
|
||||
version = "0.7.2"
|
||||
version = "0.8.0"
|
||||
authors = [
|
||||
"Maksym Pavlenko <pavlenko.maksym@gmail.com>",
|
||||
"The containerd Authors",
|
||||
|
@ -19,7 +19,6 @@ async = [
|
|||
"async-trait",
|
||||
"containerd-shim-protos/async",
|
||||
"futures",
|
||||
"signal-hook-tokio",
|
||||
"tokio",
|
||||
]
|
||||
tracing = ["dep:tracing"]
|
||||
|
@ -34,9 +33,11 @@ name = "windows-log-reader"
|
|||
path = "examples/windows_log_reader.rs"
|
||||
|
||||
[dependencies]
|
||||
containerd-shim-protos = { path = "../shim-protos", version = "0.7.0" }
|
||||
which = "7.0.1"
|
||||
containerd-shim-protos = { path = "../shim-protos", version = "0.8.0" }
|
||||
go-flag = "0.1.0"
|
||||
lazy_static = "1.4.0"
|
||||
sha2 = "0.10.2"
|
||||
libc.workspace = true
|
||||
log = { workspace = true, features = ["std", "kv_unstable" ] }
|
||||
nix = { workspace = true, features = [
|
||||
|
@ -53,6 +54,7 @@ prctl.workspace = true
|
|||
signal-hook = "0.3.13"
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
tempfile.workspace = true
|
||||
thiserror.workspace = true
|
||||
time.workspace = true
|
||||
|
||||
|
@ -62,20 +64,13 @@ tracing = { version = "0.1", optional = true }
|
|||
# Async dependencies
|
||||
async-trait = { workspace = true, optional = true }
|
||||
futures = { workspace = true, optional = true }
|
||||
signal-hook-tokio = { version = "0.3.1", optional = true, features = [
|
||||
"futures-v0_3",
|
||||
] }
|
||||
tokio = { workspace = true, features = ["full"], optional = true }
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
cgroups-rs.workspace = true
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
command-fds = "0.3.0"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
mio = { version = "1.0", features = ["os-ext", "os-poll"] }
|
||||
os_pipe.workspace = true
|
||||
windows-sys = { version = "0.52.0", features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_System_WindowsProgramming",
|
||||
|
|
|
@ -175,7 +175,7 @@ $ cat log
|
|||
$env:TTRPC_ADDRESS="\\.\pipe\containerd-containerd.ttrpc"
|
||||
|
||||
$ cargo run --example skeleton -- -namespace default -id 1234 -address "\\.\pipe\containerd-containerd" start
|
||||
\\.\pipe\containerd-shim-17630016127144989388-pipe
|
||||
\\.\pipe\containerd-shim-bc764c65e177434fcefe8257dc440be8b8acf7c96156320d965938f7e9ae1a35-pipe
|
||||
|
||||
# (Optional) Run the log collector in a separate command window
|
||||
# note: log reader won't work if containerd is connected to the named pipe, this works when running manually to help debug locally
|
||||
|
@ -183,8 +183,8 @@ $ cargo run --example windows-log-reader \\.\pipe\containerd-shim-default-1234-l
|
|||
Reading logs from: \\.\pipe\containerd-shim-default-1234-log
|
||||
<logs will appear after next command>
|
||||
|
||||
$ cargo run --example shim-proto-connect \\.\pipe\containerd-shim-17630016127144989388-pipe
|
||||
Connecting to \\.\pipe\containerd-shim-17630016127144989388-pipe...
|
||||
$ cargo run --example shim-proto-connect \\.\pipe\containerd-shim-bc764c65e177434fcefe8257dc440be8b8acf7c96156320d965938f7e9ae1a35-pipe
|
||||
Connecting to \\.\pipe\containerd-shim-bc764c65e177434fcefe8257dc440be8b8acf7c96156320d965938f7e9ae1a35-pipe...
|
||||
Sending `Connect` request...
|
||||
Connect response: version: "example"
|
||||
Sending `Shutdown` request...
|
||||
|
|
|
@ -41,6 +41,8 @@ pub struct Flags {
|
|||
pub action: String,
|
||||
/// Version of the shim.
|
||||
pub version: bool,
|
||||
/// get the option protobuf from stdin, print the shim info protobuf to stdout, and exit
|
||||
pub info: bool,
|
||||
}
|
||||
|
||||
/// Parses command line arguments passed to the shim.
|
||||
|
@ -57,10 +59,11 @@ pub fn parse<S: AsRef<OsStr>>(args: &[S]) -> Result<Flags> {
|
|||
f.add_flag("bundle", &mut flags.bundle);
|
||||
f.add_flag("address", &mut flags.address);
|
||||
f.add_flag("publish-binary", &mut flags.publish_binary);
|
||||
f.add_flag("info", &mut flags.info);
|
||||
})
|
||||
.map_err(|e| Error::InvalidArgument(e.to_string()))?;
|
||||
|
||||
if let Some(action) = args.get(0) {
|
||||
if let Some(action) = args.first() {
|
||||
flags.action = action.into();
|
||||
}
|
||||
|
||||
|
|
|
@ -15,27 +15,28 @@
|
|||
*/
|
||||
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
env,
|
||||
io::Read,
|
||||
os::unix::{fs::FileTypeExt, net::UnixListener},
|
||||
path::Path,
|
||||
process,
|
||||
process::{Command, Stdio},
|
||||
process::{self, Command as StdCommand, Stdio},
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
task::{ready, Poll},
|
||||
};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use command_fds::{CommandFdExt, FdMapping};
|
||||
use containerd_shim_protos::{
|
||||
api::DeleteResponse,
|
||||
protobuf::Message,
|
||||
protobuf::{well_known_types::any::Any, Message, MessageField},
|
||||
shim::oci::Options,
|
||||
shim_async::{create_task, Client, Task},
|
||||
ttrpc::r#async::Server,
|
||||
types::introspection::{self, RuntimeInfo},
|
||||
};
|
||||
use futures::StreamExt;
|
||||
use futures::stream::{poll_fn, BoxStream, SelectAll, StreamExt};
|
||||
use libc::{SIGCHLD, SIGINT, SIGPIPE, SIGTERM};
|
||||
use log::{debug, error, info, warn};
|
||||
use nix::{
|
||||
|
@ -46,8 +47,11 @@ use nix::{
|
|||
},
|
||||
unistd::Pid,
|
||||
};
|
||||
use signal_hook_tokio::Signals;
|
||||
use tokio::{io::AsyncWriteExt, sync::Notify};
|
||||
use oci_spec::runtime::Features;
|
||||
use tokio::{io::AsyncWriteExt, process::Command, sync::Notify};
|
||||
use which::which;
|
||||
|
||||
const DEFAULT_BINARY_NAME: &str = "runc";
|
||||
|
||||
use crate::{
|
||||
args,
|
||||
|
@ -55,7 +59,7 @@ use crate::{
|
|||
error::{Error, Result},
|
||||
logger, parse_sockaddr, reap, socket_address,
|
||||
util::{asyncify, read_file_to_str, write_str_to_file},
|
||||
Config, Flags, StartOpts, SOCKET_FD, TTRPC_ADDRESS,
|
||||
Config, Flags, StartOpts, TTRPC_ADDRESS,
|
||||
};
|
||||
|
||||
pub mod monitor;
|
||||
|
@ -109,6 +113,51 @@ where
|
|||
process::exit(1);
|
||||
}
|
||||
}
|
||||
/// get runtime info
|
||||
pub fn run_info() -> Result<RuntimeInfo> {
|
||||
let mut info = introspection::RuntimeInfo {
|
||||
name: "containerd-shim-runc-v2-rs".to_string(),
|
||||
version: MessageField::some(introspection::RuntimeVersion {
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
revision: String::default(),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
let mut binary_name = DEFAULT_BINARY_NAME.to_string();
|
||||
let mut data: Vec<u8> = Vec::new();
|
||||
std::io::stdin()
|
||||
.read_to_end(&mut data)
|
||||
.map_err(io_error!(e, "read stdin"))?;
|
||||
// get BinaryName from stdin
|
||||
if !data.is_empty() {
|
||||
let opts =
|
||||
Any::parse_from_bytes(&data).and_then(|any| Options::parse_from_bytes(&any.value))?;
|
||||
if !opts.binary_name().is_empty() {
|
||||
binary_name = opts.binary_name().to_string();
|
||||
}
|
||||
}
|
||||
let binary_path = which(binary_name).unwrap();
|
||||
|
||||
// get features
|
||||
let output = StdCommand::new(binary_path)
|
||||
.arg("features")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
let features: Features = serde_json::from_str(&String::from_utf8_lossy(&output.stdout))?;
|
||||
|
||||
// set features
|
||||
let features_any = Any {
|
||||
type_url: "types.containerd.io/opencontainers/runtime-spec/1/features/Features".to_string(),
|
||||
// features to json
|
||||
value: serde_json::to_vec(&features)?,
|
||||
..Default::default()
|
||||
};
|
||||
info.features = MessageField::some(features_any);
|
||||
|
||||
Ok(info)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(level = "info"))]
|
||||
async fn bootstrap<T>(runtime_id: &str, opts: Option<Config>) -> Result<()>
|
||||
|
@ -167,6 +216,12 @@ where
|
|||
Ok(())
|
||||
}
|
||||
_ => {
|
||||
if flags.socket.is_empty() {
|
||||
return Err(Error::InvalidArgument(String::from(
|
||||
"Shim socket cannot be empty",
|
||||
)));
|
||||
}
|
||||
|
||||
if !config.no_setup_logger {
|
||||
logger::init(
|
||||
flags.debug,
|
||||
|
@ -177,13 +232,18 @@ where
|
|||
}
|
||||
|
||||
let publisher = RemotePublisher::new(&ttrpc_address).await?;
|
||||
let task = shim.create_task_service(publisher).await;
|
||||
let task_service = create_task(Arc::new(task));
|
||||
let mut server = Server::new().register_service(task_service);
|
||||
server = server.add_listener(SOCKET_FD)?;
|
||||
server = server.set_domain_unix();
|
||||
let task = Box::new(shim.create_task_service(publisher).await)
|
||||
as Box<dyn containerd_shim_protos::shim_async::Task + Send + Sync>;
|
||||
let task_service = create_task(Arc::from(task));
|
||||
let Some(mut server) = create_server_with_retry(&flags).await? else {
|
||||
signal_server_started();
|
||||
return Ok(());
|
||||
};
|
||||
server = server.register_service(task_service);
|
||||
server.start().await?;
|
||||
|
||||
signal_server_started();
|
||||
|
||||
info!("Shim successfully started, waiting for exit signal...");
|
||||
tokio::spawn(async move {
|
||||
handle_signals(signals).await;
|
||||
|
@ -247,38 +307,18 @@ pub async fn spawn(opts: StartOpts, grouping: &str, vars: Vec<(&str, &str)>) ->
|
|||
let cwd = env::current_dir().map_err(io_error!(e, ""))?;
|
||||
let address = socket_address(&opts.address, &opts.namespace, grouping);
|
||||
|
||||
// Create socket and prepare listener.
|
||||
// We'll use `add_listener` when creating TTRPC server.
|
||||
let listener = match start_listener(&address).await {
|
||||
Ok(l) => l,
|
||||
Err(e) => {
|
||||
if let Error::IoError {
|
||||
err: ref io_err, ..
|
||||
} = e
|
||||
{
|
||||
if io_err.kind() != std::io::ErrorKind::AddrInUse {
|
||||
return Err(e);
|
||||
};
|
||||
}
|
||||
if let Ok(()) = wait_socket_working(&address, 5, 200).await {
|
||||
write_str_to_file("address", &address).await?;
|
||||
return Ok(address);
|
||||
}
|
||||
remove_socket(&address).await?;
|
||||
start_listener(&address).await?
|
||||
}
|
||||
};
|
||||
// Activation pattern comes from the hcsshim: https://github.com/microsoft/hcsshim/blob/v0.10.0-rc.7/cmd/containerd-shim-runhcs-v1/serve.go#L57-L70
|
||||
// another way to do it would to create named pipe and pass it to the child process through handle inheritence but that would require duplicating
|
||||
// the logic in Rust's 'command' for process creation. There is an issue in Rust to make it simplier to specify handle inheritence and this could
|
||||
// be revisited once https://github.com/rust-lang/rust/issues/54760 is implemented.
|
||||
|
||||
// tokio::process::Command do not have method `fd_mappings`,
|
||||
// and the `spawn()` is also not an async method,
|
||||
// so we use the std::process::Command here
|
||||
let mut command = Command::new(cmd);
|
||||
|
||||
command
|
||||
.current_dir(cwd)
|
||||
.stdout(Stdio::null())
|
||||
.stdout(Stdio::piped())
|
||||
.stdin(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.envs(vars)
|
||||
.args([
|
||||
"-namespace",
|
||||
&opts.namespace,
|
||||
|
@ -286,31 +326,139 @@ pub async fn spawn(opts: StartOpts, grouping: &str, vars: Vec<(&str, &str)>) ->
|
|||
&opts.id,
|
||||
"-address",
|
||||
&opts.address,
|
||||
])
|
||||
.fd_mappings(vec![FdMapping {
|
||||
parent_fd: listener.into(),
|
||||
child_fd: SOCKET_FD,
|
||||
}])?;
|
||||
"-socket",
|
||||
&address,
|
||||
]);
|
||||
|
||||
if opts.debug {
|
||||
command.arg("-debug");
|
||||
}
|
||||
command.envs(vars);
|
||||
|
||||
let _child = command.spawn().map_err(io_error!(e, "spawn shim"))?;
|
||||
let mut child = command.spawn().map_err(io_error!(e, "spawn shim"))?;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
crate::cgroup::set_cgroup_and_oom_score(_child.id())?;
|
||||
crate::cgroup::set_cgroup_and_oom_score(child.id().unwrap())?;
|
||||
|
||||
let mut reader = child.stdout.take().unwrap();
|
||||
tokio::io::copy(&mut reader, &mut tokio::io::stderr())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Ok(address)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(skip_all, level = "info"))]
|
||||
fn setup_signals_tokio(config: &Config) -> Signals {
|
||||
if config.no_reaper {
|
||||
Signals::new([SIGTERM, SIGINT, SIGPIPE]).expect("new signal failed")
|
||||
} else {
|
||||
Signals::new([SIGTERM, SIGINT, SIGPIPE, SIGCHLD]).expect("new signal failed")
|
||||
async fn create_server(flags: &args::Flags) -> Result<Server> {
|
||||
use std::os::fd::IntoRawFd;
|
||||
let listener = start_listener(&flags.socket).await?;
|
||||
let mut server = Server::new();
|
||||
server = server.add_listener(listener.into_raw_fd())?;
|
||||
server = server.set_domain_unix();
|
||||
Ok(server)
|
||||
}
|
||||
|
||||
async fn create_server_with_retry(flags: &args::Flags) -> Result<Option<Server>> {
|
||||
// Really try to create a server.
|
||||
let server = match create_server(flags).await {
|
||||
Ok(server) => server,
|
||||
Err(Error::IoError { err, .. }) if err.kind() == std::io::ErrorKind::AddrInUse => {
|
||||
// If the address is already in use then make sure it is up and running and return the address
|
||||
// This allows for running a single shim per container scenarios
|
||||
if let Ok(()) = wait_socket_working(&flags.socket, 5, 200).await {
|
||||
write_str_to_file("address", &flags.socket).await?;
|
||||
return Ok(None);
|
||||
}
|
||||
remove_socket(&flags.socket).await?;
|
||||
create_server(flags).await?
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
Ok(Some(server))
|
||||
}
|
||||
|
||||
fn signal_server_started() {
|
||||
use libc::{dup2, STDERR_FILENO, STDOUT_FILENO};
|
||||
|
||||
unsafe {
|
||||
if dup2(STDERR_FILENO, STDOUT_FILENO) < 0 {
|
||||
panic!("Error closing pipe: {}", std::io::Error::last_os_error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn signal_stream(kind: i32) -> std::io::Result<BoxStream<'static, i32>> {
|
||||
use tokio::signal::unix::{signal, SignalKind};
|
||||
let kind = SignalKind::from_raw(kind);
|
||||
signal(kind).map(|mut sig| {
|
||||
// The object returned by `signal` is not a `Stream`.
|
||||
// The `poll_fn` function constructs a `Stream` based on a polling function.
|
||||
// We need to create a `Stream` so that we can use the `SelectAll` stream "merge"
|
||||
// all the signal streams.
|
||||
poll_fn(move |cx| {
|
||||
ready!(sig.poll_recv(cx));
|
||||
Poll::Ready(Some(kind.as_raw_value()))
|
||||
})
|
||||
.boxed()
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn signal_stream(kind: i32) -> std::io::Result<BoxStream<'static, i32>> {
|
||||
use tokio::signal::windows::ctrl_c;
|
||||
|
||||
// Windows doesn't have similar signal like SIGCHLD
|
||||
// We could implement something if required but for now
|
||||
// just implement support for SIGINT
|
||||
if kind != SIGINT {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("Invalid signal {kind}"),
|
||||
));
|
||||
}
|
||||
|
||||
ctrl_c().map(|mut sig| {
|
||||
// The object returned by `signal` is not a `Stream`.
|
||||
// The `poll_fn` function constructs a `Stream` based on a polling function.
|
||||
// We need to create a `Stream` so that we can use the `SelectAll` stream "merge"
|
||||
// all the signal streams.
|
||||
poll_fn(move |cx| {
|
||||
ready!(sig.poll_recv(cx));
|
||||
Poll::Ready(Some(kind))
|
||||
})
|
||||
.boxed()
|
||||
})
|
||||
}
|
||||
|
||||
type Signals = SelectAll<BoxStream<'static, i32>>;
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(skip_all, level = "info"))]
|
||||
fn setup_signals_tokio(config: &Config) -> Signals {
|
||||
#[cfg(unix)]
|
||||
let signals: &[i32] = if config.no_reaper {
|
||||
&[SIGTERM, SIGINT, SIGPIPE]
|
||||
} else {
|
||||
&[SIGTERM, SIGINT, SIGPIPE, SIGCHLD]
|
||||
};
|
||||
|
||||
// Windows doesn't have similar signal like SIGCHLD
|
||||
// We could implement something if required but for now
|
||||
// just listen for SIGINT
|
||||
// Note: see comment at the counterpart in synchronous/mod.rs for details.
|
||||
#[cfg(windows)]
|
||||
let signals: &[i32] = &[SIGINT];
|
||||
|
||||
let signals: Vec<_> = signals
|
||||
.iter()
|
||||
.copied()
|
||||
.map(signal_stream)
|
||||
.collect::<std::io::Result<_>>()
|
||||
.expect("signal setup failed");
|
||||
|
||||
SelectAll::from_iter(signals)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(skip_all, level = "info"))]
|
||||
async fn handle_signals(signals: Signals) {
|
||||
let mut signals = signals.fuse();
|
||||
|
@ -322,14 +470,7 @@ async fn handle_signals(signals: Signals) {
|
|||
}
|
||||
SIGCHLD => loop {
|
||||
// Note: see comment at the counterpart in synchronous/mod.rs for details.
|
||||
match asyncify(move || {
|
||||
Ok(wait::waitpid(
|
||||
Some(Pid::from_raw(-1)),
|
||||
Some(WaitPidFlag::WNOHANG),
|
||||
)?)
|
||||
})
|
||||
.await
|
||||
{
|
||||
match wait::waitpid(Some(Pid::from_raw(-1)), Some(WaitPidFlag::WNOHANG)) {
|
||||
Ok(WaitStatus::Exited(pid, status)) => {
|
||||
monitor_notify_by_pid(pid.as_raw(), status)
|
||||
.await
|
||||
|
@ -345,7 +486,7 @@ async fn handle_signals(signals: Signals) {
|
|||
Ok(WaitStatus::StillAlive) => {
|
||||
break;
|
||||
}
|
||||
Err(Error::Nix(Errno::ECHILD)) => {
|
||||
Err(Errno::ECHILD) => {
|
||||
break;
|
||||
}
|
||||
Err(e) => {
|
||||
|
|
|
@ -117,7 +117,7 @@ impl Monitor {
|
|||
subject: subject.clone(),
|
||||
exit_code,
|
||||
})
|
||||
.map_err(other_error!(e, "failed to send exit code"));
|
||||
.map_err(other_error!("failed to send exit code"));
|
||||
results.push(res);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,24 +16,42 @@
|
|||
|
||||
use std::os::unix::io::RawFd;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use containerd_shim_protos::{
|
||||
api::Empty,
|
||||
api::Envelope,
|
||||
protobuf::MessageDyn,
|
||||
shim::events,
|
||||
shim_async::{Client, Events, EventsClient},
|
||||
shim_async::{Client, EventsClient},
|
||||
ttrpc,
|
||||
ttrpc::{context::Context, r#async::TtrpcContext},
|
||||
ttrpc::context::Context,
|
||||
};
|
||||
use log::{debug, error, warn};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::{
|
||||
error::Result,
|
||||
error::{self, Result},
|
||||
util::{asyncify, connect, convert_to_any, timestamp},
|
||||
};
|
||||
|
||||
/// The publisher reports events and uses a queue to retry the event reporting.
|
||||
/// The maximum number of attempts to report is 5 times.
|
||||
/// When the ttrpc client fails to report, it attempts to reconnect to the client and report.
|
||||
|
||||
/// Max queue size
|
||||
const QUEUE_SIZE: i64 = 1024;
|
||||
/// Max try five times
|
||||
const MAX_REQUEUE: i64 = 5;
|
||||
|
||||
/// Async Remote publisher connects to containerd's TTRPC endpoint to publish events from shim.
|
||||
pub struct RemotePublisher {
|
||||
client: EventsClient,
|
||||
pub address: String,
|
||||
sender: mpsc::Sender<Item>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Item {
|
||||
ev: Envelope,
|
||||
ctx: Context,
|
||||
count: i64,
|
||||
}
|
||||
|
||||
impl RemotePublisher {
|
||||
|
@ -41,11 +59,70 @@ impl RemotePublisher {
|
|||
///
|
||||
/// containerd uses `/run/containerd/containerd.sock.ttrpc` by default
|
||||
pub async fn new(address: impl AsRef<str>) -> Result<RemotePublisher> {
|
||||
let client = Self::connect(address).await?;
|
||||
let client = Self::connect(address.as_ref()).await?;
|
||||
// Init the queue channel
|
||||
let (sender, receiver) = mpsc::channel::<Item>(QUEUE_SIZE as usize);
|
||||
let rt = RemotePublisher {
|
||||
address: address.as_ref().to_string(),
|
||||
sender,
|
||||
};
|
||||
rt.process_queue(client, receiver).await;
|
||||
Ok(rt)
|
||||
}
|
||||
|
||||
Ok(RemotePublisher {
|
||||
client: EventsClient::new(client),
|
||||
})
|
||||
/// Process_queue for push events
|
||||
///
|
||||
/// This is a loop task for dealing event tasks
|
||||
pub async fn process_queue(&self, ttrpc_client: Client, mut receiver: mpsc::Receiver<Item>) {
|
||||
let mut client = EventsClient::new(ttrpc_client);
|
||||
let sender = self.sender.clone();
|
||||
let address = self.address.clone();
|
||||
tokio::spawn(async move {
|
||||
// only this use receiver
|
||||
while let Some(item) = receiver.recv().await {
|
||||
// drop this event after MAX_REQUEUE try
|
||||
if item.count > MAX_REQUEUE {
|
||||
debug!("drop event {:?}", item);
|
||||
continue;
|
||||
}
|
||||
let mut req = events::ForwardRequest::new();
|
||||
req.set_envelope(item.ev.clone());
|
||||
let new_item = Item {
|
||||
ev: item.ev.clone(),
|
||||
ctx: item.ctx.clone(),
|
||||
count: item.count + 1,
|
||||
};
|
||||
if let Err(e) = client.forward(new_item.ctx.clone(), &req).await {
|
||||
match e {
|
||||
ttrpc::error::Error::RemoteClosed | ttrpc::error::Error::LocalClosed => {
|
||||
warn!("publish fail because the server or client close {:?}", e);
|
||||
// reconnect client
|
||||
if let Ok(c) = Self::connect(address.as_str()).await.map_err(|e| {
|
||||
debug!("reconnect the ttrpc client {:?} fail", e);
|
||||
}) {
|
||||
client = EventsClient::new(c);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// TODO! if it is other error , May we should deal with socket file
|
||||
error!("the client forward err is {:?}", e);
|
||||
}
|
||||
}
|
||||
let sender_ref = sender.clone();
|
||||
// Take a another task requeue , for no blocking the recv task
|
||||
tokio::spawn(async move {
|
||||
// wait for few time and send for imporving the success ratio
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(new_item.count as u64))
|
||||
.await;
|
||||
// if channel is full and send fail ,release it after 3 seconds
|
||||
let _ = sender_ref
|
||||
.send_timeout(new_item, tokio::time::Duration::from_secs(3))
|
||||
.await;
|
||||
});
|
||||
}
|
||||
}
|
||||
debug!("publisher 'process_queue' quit complete");
|
||||
});
|
||||
}
|
||||
|
||||
async fn connect(address: impl AsRef<str>) -> Result<Client> {
|
||||
|
@ -76,26 +153,22 @@ impl RemotePublisher {
|
|||
envelope.set_timestamp(timestamp()?);
|
||||
envelope.set_event(convert_to_any(event)?);
|
||||
|
||||
let mut req = events::ForwardRequest::new();
|
||||
req.set_envelope(envelope);
|
||||
let item = Item {
|
||||
ev: envelope.clone(),
|
||||
ctx: ctx.clone(),
|
||||
count: 0,
|
||||
};
|
||||
|
||||
self.client.forward(ctx, &req).await?;
|
||||
//if channel is full and send fail ,release it after 3 seconds
|
||||
self.sender
|
||||
.send_timeout(item, tokio::time::Duration::from_secs(3))
|
||||
.await
|
||||
.map_err(|e| error::Error::Ttrpc(ttrpc::error::Error::Others(e.to_string())))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Events for RemotePublisher {
|
||||
async fn forward(
|
||||
&self,
|
||||
_ctx: &TtrpcContext,
|
||||
req: events::ForwardRequest,
|
||||
) -> ttrpc::Result<Empty> {
|
||||
self.client.forward(Context::default(), &req).await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{
|
||||
|
@ -103,10 +176,11 @@ mod tests {
|
|||
sync::Arc,
|
||||
};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use containerd_shim_protos::{
|
||||
api::{Empty, ForwardRequest},
|
||||
events::task::TaskOOM,
|
||||
shim_async::create_events,
|
||||
shim_async::{create_events, Events},
|
||||
ttrpc::asynchronous::Server,
|
||||
};
|
||||
use tokio::sync::{
|
||||
|
@ -115,6 +189,7 @@ mod tests {
|
|||
};
|
||||
|
||||
use super::*;
|
||||
use crate::publisher::ttrpc::r#async::TtrpcContext;
|
||||
|
||||
struct FakeServer {
|
||||
tx: Sender<i32>,
|
||||
|
|
|
@ -38,7 +38,7 @@ where
|
|||
{
|
||||
spawn_blocking(f)
|
||||
.await
|
||||
.map_err(other_error!(e, "failed to spawn blocking task"))?
|
||||
.map_err(other_error!("failed to spawn blocking task"))?
|
||||
}
|
||||
|
||||
pub async fn read_file_to_str(path: impl AsRef<Path>) -> Result<String> {
|
||||
|
@ -96,14 +96,19 @@ pub async fn read_pid_from_file(pid_path: &Path) -> Result<i32> {
|
|||
pub async fn read_spec(bundle: impl AsRef<Path>) -> Result<Spec> {
|
||||
let path = bundle.as_ref().join(CONFIG_FILE_NAME);
|
||||
let content = read_file_to_str(&path).await?;
|
||||
serde_json::from_str::<Spec>(content.as_str()).map_err(other_error!(e, "read spec"))
|
||||
serde_json::from_str::<Spec>(content.as_str()).map_err(other_error!("read spec"))
|
||||
}
|
||||
|
||||
// read_options reads the option information from the path.
|
||||
// When the file does not exist, read_options returns nil without an error.
|
||||
pub async fn read_options(bundle: impl AsRef<Path>) -> Result<Options> {
|
||||
let path = bundle.as_ref().join(OPTIONS_FILE_NAME);
|
||||
if !path.exists() {
|
||||
return Ok(Options::default());
|
||||
}
|
||||
let opts_str = read_file_to_str(path).await?;
|
||||
let opts =
|
||||
serde_json::from_str::<JsonOptions>(&opts_str).map_err(other_error!(e, "read options"))?;
|
||||
serde_json::from_str::<JsonOptions>(&opts_str).map_err(other_error!("read options"))?;
|
||||
Ok(opts.into())
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue