Compare commits

..

122 Commits

Author SHA1 Message Date
Eric Ernst
5ff5c7d6c3 Merge pull request #631 from egernst/1.11.3-branch-bump
# Kata Containers 1.11.3
2020-08-28 09:04:37 -07:00
Eric Ernst
6a27b1c79b release: Kata Containers 1.11.3
Version bump no changes

Signed-off-by: Eric Ernst <eric.g.ernst@gmail.com>
2020-08-27 09:00:24 -07:00
Archana Shinde
156e3bca9b Merge pull request #348 from bergwolf/1.11.2-branch-bump
# Kata Containers 1.11.2
2020-07-01 09:39:38 -07:00
Peng Tao
7b09bcf5a3 release: Kata Containers 1.11.2
Version bump no changes

Signed-off-by: Peng Tao <bergwolf@hyper.sh>
2020-06-29 03:14:22 +00:00
Salvador Fuentes
845ce4a727 Merge pull request #273 from amshinde/1.11.1-branch-bump
# Kata Containers 1.11.1
2020-06-05 17:38:08 -05:00
Archana Shinde
3355510feb release: Kata Containers 1.11.1
Version bump no changes

Signed-off-by: Archana Shinde <archana.m.shinde@intel.com>
2020-06-05 15:43:17 +00:00
Jose Carlos Venegas Munoz
71d25d530e Merge pull request #217 from jcvenegas/backport-release-fix
backport: release: actions: pin artifact to v1
2020-05-08 13:17:37 -05:00
Jose Carlos Venegas Munoz
55a004e6de release: actions: pin artifact to v1
the actions upload/download-artifact moved to a new version
and master now is not comptible.

Fixes: #211

Signed-off-by: Jose Carlos Venegas Munoz <jose.carlos.venegas.munoz@intel.com>
2020-05-08 17:18:50 +00:00
Jose Carlos Venegas Munoz
449236f7cd Merge pull request #207 from katabuilder/1.11.0-branch-bump
# Kata Containers 1.11.0
2020-05-06 11:55:58 -05:00
katabuilder
92a0f7a0b1 release: Kata Containers 1.11.0
Version bump no changes

Signed-off-by: katabuilder <katabuilder@katacontainers.io>
2020-05-05 20:13:35 +00:00
Archana Shinde
c95d09a34d Merge pull request #181 from chavafg/1.11.0-rc0-branch-bump
# Kata Containers 1.11.0-rc0
2020-04-17 14:55:51 -07:00
Salvador Fuentes
63d9a8696f release: Kata Containers 1.11.0-rc0
- Fix potentianl crash
- sandbox: fix the issue of missing setting hostname
- unify the rustjail's log to contain container id and exec id
- Refactor the way of creating container process

ba3c732 grpc: fix the issue of potential crashes
32431d7 rpc: fix the issue of kill container process
986e666 sandbox: fix the issue of missing setting hostname
7d9bdf7 grpc: Fix the issue passing wrong exec_id to exec process
9220fb8 rustjail: unify the rustjail's log to contain container id and exec id
c1b6838 rustjail: refactoring the way of creating container process
e56b10f rustjail: remove the unused imported crates
ded27f4 oci: add Default and Clone to oci spec objects
7df8ede rustjail: replace protocol spec with oci spec

Signed-off-by: Salvador Fuentes <salvador.fuentes@intel.com>
2020-04-17 17:51:05 +00:00
Yang Bo
c0dc7676e0 Merge pull request #179 from lifupan/fix_potentianl_crash
Fix potentianl crash
2020-04-07 19:58:52 +08:00
fupan.lfp
ba3c732f86 grpc: fix the issue of potential crashes
It's better to check whether the sandbox's get_container
result instead of unwrap it directly, otherwise it would
crash the agent if the conainer id is invalid.

Fixes: #178

Signed-off-by: fupan.lfp <fupan.lfp@antfin.com>
2020-04-02 18:58:24 +08:00
fupan.lfp
32431d701c rpc: fix the issue of kill container process
When kill a process, if the exec id is empty, then
it means to kill all processes in the container, if
the exec id isn't empty, then it will only kill the
specific exec process.

Signed-off-by: fupan.lfp <fupan.lfp@antfin.com>
2020-04-02 17:58:46 +08:00
Yang Bo
6d61ab439c Merge pull request #176 from lifupan/fix_hostname
sandbox: fix the issue of missing setting hostname
2020-04-01 10:00:31 +08:00
fupan.lfp
986e666b0b sandbox: fix the issue of missing setting hostname
When setup the persisten uts namespace, it's should
set the hostname for this ns.

Fixes: #175

Signed-off-by: fupan.lfp <fupan.lfp@antfin.com>
2020-03-31 17:22:24 +08:00
fupan.lfp
7d9bdf7b01 grpc: Fix the issue passing wrong exec_id to exec process
This issue was brought accidently by PR #174, fix this issue.

Signed-off-by: fupan.lfp <fupan.lfp@antfin.com>
2020-03-31 17:19:40 +08:00
James O. D. Hunt
c948d8a802 Merge pull request #174 from lifupan/unify_log
unify the rustjail's log to contain container id and exec id
2020-03-30 10:02:39 +01:00
fupan.lfp
9220fb8e0c rustjail: unify the rustjail's log to contain container id and exec id
Add the container id and exec id to start container's log
which would make it clearly to check the log.

Fixes: #173

Signed-off-by: fupan.lfp <fupan.lfp@antfin.com>
2020-03-27 20:10:50 +08:00
Yang Bo
1e15465012 Merge pull request #167 from lifupan/refactor
Refactor the way of creating container process
2020-03-24 11:18:42 +08:00
fupan.lfp
c1b6838e25 rustjail: refactoring the way of creating container process
In the previous implementation, create a container process
by forking the parent process as the container process,
and then at the forked child process do much more setting,
such as rootfs mounting, drop capabilities and so on, at
last exec the container entry cmd to switch into container
process.

But since the parent is a muti thread process, which would
cause a dead lock in the forked child. For example, if one
of the parent process's thread do some malloc operation, which
would take a mutex lock, and at the same time, the parent forked
a child process, since the mutex lock status would be inherited
by the child process but there's no chance to release the lock
in the child since the child process only has a single thread
which would meet a dead lock if it would do some malloc operation.

Thus, the new implementation would do exec directly after forked
and then do the setting in the exec process. Of course, this requred
a data communication between parent and child since the child cannot
depends on the shared memory by fork way.

Fixes: #166
Fixes: #133

Signed-off-by: fupan.lfp <fupan.lfp@antfin.com>
2020-03-23 17:12:10 +08:00
fupan.lfp
e56b10f835 rustjail: remove the unused imported crates
remove the unused imported crates

Signed-off-by: fupan.lfp <fupan.lfp@antfin.com>
2020-03-20 17:04:05 +08:00
fupan.lfp
ded27f48d5 oci: add Default and Clone to oci spec objects
Add the clone and default feature to oci
spec objects.

Signed-off-by: fupan.lfp <fupan.lfp@antfin.com>
2020-03-20 17:03:54 +08:00
fupan.lfp
7df8edef1b rustjail: replace protocol spec with oci spec
transform the rpc protocol spec to
oci spec.

Signed-off-by: fupan.lfp <fupan.lfp@antfin.com>
2020-03-20 16:26:32 +08:00
James O. D. Hunt
8280208443 Merge pull request #154 from awprice/issue-152
agent: add configurable container pipe size cmdline option
2020-03-18 08:36:23 +00:00
GabyCT
7087b5f43c Merge pull request #165 from bergwolf/1.11.0-alpha1-branch-bump
# Kata Containers 1.11.0-alpha1
2020-03-17 13:10:53 -06:00
James O. D. Hunt
fe0a3a0c7c Merge pull request #156 from lifupan/master
add a workspace and run all the tests in the workspace
2020-03-17 11:10:27 +00:00
Peng Tao
fbf1d015e7 release: Kata Containers 1.11.0-alpha1
- actions: Add verbose information
- systemd-service: build rust-agent systemd services
- grpc: fix the issue of crash agent when didn't find the process

cd233c0 actions: Add verbose information
f0eaeac path-absolutize: version update
3136712 systemd-service: build rust-agent systemd services
289d617 grpc: fix the issue of crash agent when didn't find the process

Signed-off-by: Peng Tao <bergwolf@hyper.sh>
2020-03-16 12:38:41 +00:00
fupan.lfp
245183cb28 cargo: add a workspace and run all the tests in the workspace
Add a worksapce and run all of the tests in
under this workspace.

Fixes:#155

Signed-off-by: fupan.lfp <fupan.lfp@antfin.com>
2020-03-16 16:34:59 +08:00
GabyCT
22afde1850 Merge pull request #158 from jcvenegas/fix-157
actions: Add verbose information
2020-03-04 15:15:42 -06:00
Jose Carlos Venegas Munoz
cd233c047a actions: Add verbose information
Add a logs to debug actions easily

Fixes: #157

Signed-off-by: Jose Carlos Venegas Munoz <jose.carlos.venegas.munoz@intel.com>
2020-03-04 16:02:06 +00:00
Alex Price
204edf0e51 agent: add configurable container pipe size cmdline option
Adds a cmdline option to configure the stdout/stderr pipe sizes.
Uses `F_SETPIPE_SZ` to resize the write side of the pipe after
creation.

Example Cmdline option: `agent.container_pipe_size=2097152`

fixes #152

Signed-off-by: Alex Price <aprice@atlassian.com>
2020-03-04 15:31:59 +11:00
GabyCT
35c33bba47 Merge pull request #145 from Pennyzct/build_service_for_rust_agent
systemd-service: build rust-agent systemd services
2020-03-03 13:17:27 -06:00
Penny Zheng
f0eaeac3be path-absolutize: version update
The latest tag version v1.2.0 fixes the error of inapporiately using
mutable static.

Fixes: #144

Signed-off-by: Penny Zheng <penny.zheng@arm.com>
2020-03-03 09:24:13 +08:00
Penny Zheng
3136712d8e systemd-service: build rust-agent systemd services
I add another sub-command `build-service` in Makefile to
generate rust-agent-related systemd service files, which
are necessary for building guest rootfs image.
The whole design is following the one in go-agent.

Fixes: #144

Signed-off-by: Penny Zheng <penny.zheng@arm.com>
2020-03-03 09:24:02 +08:00
James O. D. Hunt
7965445adf Merge pull request #138 from lifupan/master
grpc: fix the issue of crash agent when didn't find the process
2020-02-25 10:53:00 +00:00
Salvador Fuentes
9d7bbdc5a6 Merge pull request #143 from amshinde/1.11.0-alpha0-branch-bump
# Kata Containers 1.11.0-alpha0
2020-02-19 17:24:45 -06:00
Archana Shinde
83b1712fa9 release: Kata Containers 1.11.0-alpha0
- should ignore  invalid a key-value pair as an env
- Revert: "Makefile: Fix rust agent build using "--release"."
- Makefile: Fix rust agent build using "--release".
- vsock: support log_vport and debug_console_vport
- Agent: Separate logging into a single crate
- agent: fix the issue of crash agent without spec
- fix the issue of missing restore process's cwd
- Running rust-agent on AArch64
- ci: Remove run_rust_test functions as not being used
- add oci compatibility test case
- agent: Add unit tests for sandbox.rs
- version: Add VERSION file
- ci: Add minimal makefile to use central go test script
- netlink: pull out netlink as library crate.
- Fixup workflow 103

40b5a56 agent: ignore invalid a key-value pair as an env
269daa9 Revert: "Makefile: Fix rust agent build using "--release"."
a3e46a3 Makefile: Fix rust agent build using "--release".
3c1252e vsock: support log_vport and debug_console_vport
c373f84 agent: separate logging into a single crate
2be8661 agent: fix the issue of missing restore process's cwd
6c7453d agent: fix the issue of crash agent without spec
4edf537 ci: Remove run_rust_test functions as not being used
d222533 agent: add oci compatibility test case
7dfc4e0 linker: `no such file` linking error on AArch64
44b2caa AArch64: missing symbols on target `aarch64-unknown-linux-musl`
9621a7f ABI: only support arm 64-bit platform
8d60612 version: Add VERSION file
a5192a1 netlink: pull out netlink as library crate.
3881c06 ci: Add minimal makefile to use central go test script
1c57665 workflows: make sure we build the experimental kernel, CLH
cbd5fa0 workflows: fix step output usage
92301a6 agent: Add unit tests for sandbox.rs

Signed-off-by: Archana Shinde <archana.m.shinde@intel.com>
2020-02-18 19:36:52 +00:00
fupan.lfp
289d61730c grpc: fix the issue of crash agent when didn't find the process
It's better to catch the  error of couldn't find the process
in tty_win_resize service, other wise, an invalid process id
could crash the agent.

Fixes: #137

Signed-off-by: fupan.lfp <fupan.lfp@antfin.com>
2020-02-11 10:04:19 +08:00
Yang Bo
e2c9426ebf Merge pull request #134 from liubin/master
should ignore  invalid a key-value pair as an env
2020-02-10 11:14:36 +08:00
Fupan Li
31a97031f8 Merge pull request #136 from yyyeerbo/wip
Revert: "Makefile: Fix rust agent build using "--release"."
2020-02-10 09:17:11 +08:00
Kant
40b5a56688 agent: ignore invalid a key-value pair as an env
Fixes #135

Signed-off-by: Kant <lb203159@antfin.com>
2020-02-08 13:51:28 +08:00
Yang Bo
269daa94ef Revert: "Makefile: Fix rust agent build using "--release"."
This reverts commit a3e46a369f.

There is still problem with static link, built binary will
segmentfault on clearlinux. So revert this patch for now.

Depends-on: github.com/kata-containers/tests#2293

Fixes: #69

Signed-off-by: Yang Bo <bo@hyper.sh>
2020-02-08 12:56:34 +08:00
Yang Bo
afc7b4d523 Merge pull request #129 from yyyeerbo/wip
Makefile: Fix rust agent build using "--release".
2020-02-07 15:31:58 +08:00
Yang Bo
a3e46a369f Makefile: Fix rust agent build using "--release".
Based on @ericho's work on the bug

Depends-on: github.com/kata-containers/tests#2277

Fixes: #69

Signed-off-by: Yang Bo <bo@hyper.sh>
2020-02-07 11:38:03 +08:00
Fupan Li
356222fbba Merge pull request #132 from yyyeerbo/wip2
vsock: support log_vport and debug_console_vport
2020-02-07 10:06:42 +08:00
Fupan Li
7d667a92ee Merge pull request #130 from Tim-Zhang/separate-logging
Agent: Separate logging into a single crate
2020-02-04 22:29:50 +08:00
Yang Bo
3c1252ea79 vsock: support log_vport and debug_console_vport
Fixes: #61, #64

Signed-off-by: Yang Bo <bo@hyper.sh>
2020-02-04 20:32:07 +08:00
Tim Zhang
c373f846f5 agent: separate logging into a single crate
Since the codes in logging.rs is weakly related to the project,
separating it from the project will reduce coupling and make it reusable.

Fixes: #131

Signed-off-by: Tim Zhang <tim@hyper.sh>
2020-02-03 20:40:26 +08:00
James O. D. Hunt
b5e741ba8b Merge pull request #125 from lifupan/fix_agent_crash
agent: fix the issue of crash agent without spec
2020-01-20 11:29:16 +00:00
James O. D. Hunt
174f9abee8 Merge pull request #127 from lifupan/fix_cwd
fix the issue of missing restore process's cwd
2020-01-20 11:28:11 +00:00
fupan.lfp
2be8661ffa agent: fix the issue of missing restore process's cwd
It should restore to it's previous cwd after it
create container in which it would change it's
cwd to container's bundle path.

Fixes: #126

Signed-off-by: fupan.lfp <fupan.lfp@antfin.com>
2020-01-20 11:00:48 +08:00
fupan.lfp
6c7453db78 agent: fix the issue of crash agent without spec
To check is the oci spec passed in, other wise,
it would crash the agent unwrap it directly.

Fixes: #124

Signed-off-by: fupan.lfp <fupan.lfp@antfin.com>
2020-01-18 18:26:01 +08:00
Yang Bo
1b1e066083 Merge pull request #108 from Pennyzct/build_bug_fix
Running rust-agent on AArch64
2020-01-15 21:43:31 +08:00
Salvador Fuentes
7ce9c40c76 Merge pull request #122 from GabyCT/topic/removetest
ci: Remove run_rust_test functions as not being used
2020-01-15 07:21:43 -06:00
Gabriela Cervantes
4edf5379ca ci: Remove run_rust_test functions as not being used
This PR removes a function that is never used as the script that is
referring is also non existing at the test repository.

Fixes #113

Signed-off-by: Gabriela Cervantes <gabriela.cervantes.tellez@intel.com>
2020-01-14 14:23:14 -06:00
Fupan Li
8fbc673e68 Merge pull request #119 from quanweiZhou/add-test-case
add oci compatibility test case
2020-01-09 14:54:11 +08:00
Yang Bo
c4f15f1280 Merge pull request #91 from ericho/master
agent: Add unit tests for sandbox.rs
2020-01-09 12:51:41 +08:00
quanweiZhou
d2225334d9 agent: add oci compatibility test case
add oci compatibility test case for src/agent/oci/src/lib.rs
follow by Open Container Initiative Runtime Specification

Fixes: #118

Signed-off-by: quanweiZhou <quanweiZhou@linux.alibaba.com>
2020-01-09 11:14:24 +08:00
Penny Zheng
7dfc4e0219 linker: no such file linking error on AArch64
When using default cc linker, we will have segfault.
Debugging with `rust-gdb`, the specific error is as follows:
src/string/memcpy.c: No such file or directory.
Only changing linker with `aarch64-linux-musl-gcc`, the
`rust-agent` could be totally statically linked and run successfully.

Fixes: #107

Signed-off-by: Penny Zheng <penny.zheng@arm.com>
2020-01-09 11:08:23 +08:00
Penny Zheng
44b2caa2e5 AArch64: missing symbols on target aarch64-unknown-linux-musl
The __addtf3, __subtf3 and __multf3 symbols are used by aarch64-musl,
but are not provided by rust compiler-builtins.
For now, the only temporary but functional workaround accepted by rust
communities is to get them from libgcc.

Fixes: #107

Signed-off-by: Penny Zheng <penny.zheng@arm.com>
2020-01-09 11:06:04 +08:00
Penny Zheng
9621a7f3f5 ABI: only support arm 64-bit platform
We only support running Kata Containers on AArch64.

Fixes: #107

Signed-off-by: Penny Zheng <penny.zheng@arm.com>
2020-01-09 09:59:20 +08:00
Jose Carlos Venegas Munoz
3b6a837664 Merge pull request #115 from jcvenegas/fix-114
version: Add VERSION file
2020-01-07 14:42:55 -06:00
Jose Carlos Venegas Munoz
8d60612052 version: Add VERSION file
Needed by some CI scripts, like release or to verify stable
branches state.

Fixes: #114

Signed-off-by: Jose Carlos Venegas Munoz <jose.carlos.venegas.munoz@intel.com>
2020-01-07 19:25:33 +00:00
James O. D. Hunt
e0df9739bf Merge pull request #110 from GabyCT/topic/addmake
ci: Add minimal makefile to use central go test script
2020-01-06 09:19:59 +00:00
Hui Zhu
bf50d1811c Merge pull request #112 from yyyeerbo/wip
netlink: pull out netlink as library crate.
2020-01-06 13:44:06 +08:00
Yang Bo
a5192a16e8 netlink: pull out netlink as library crate.
Fixes: #111

Signed-off-by: Yang Bo <yb203166@antfin.com>
2020-01-04 06:45:52 -08:00
Gabriela Cervantes
3881c06578 ci: Add minimal makefile to use central go test script
This adds a basic Makefile where we can use a central go test script
in order to run the tests for the CI.

Fixes #109

Signed-off-by: Gabriela Cervantes <gabriela.cervantes.tellez@intel.com>
2020-01-03 10:04:09 -06:00
Jose Carlos Venegas Munoz
cfda17d529 Merge pull request #104 from egernst/fixup-workflow-103
Fixup workflow 103
2019-12-11 13:12:03 -06:00
Eric Ernst
1c576659de workflows: make sure we build the experimental kernel, CLH
gather job needs to take all build jobs into account, including
building of the experimental kernel and CLH. Added.

Fixes: #103

Signed-off-by: Eric Ernst <eric.ernst@intel.com>
2019-12-11 10:35:37 -08:00
Eric Ernst
cbd5fa008a workflows: fix step output usage
You cannot pass environment variables easily between steps/jobs.

Updated flow to define and set step outputs, and use the outputs of the
corresponding steps later in the flow, rather than env variables (which
never worked correctly - whoops).

Signed-off-by: Eric Ernst <eric.ernst@intel.com>
2019-12-11 08:02:36 -08:00
Erich Cordoba
92301a6382 agent: Add unit tests for sandbox.rs
These are the unit tests for the sandbox struct. This is the summary
of the most important changes:

  - To test containers it was needed to create a `LinuxContainer` type
    and this requires root privileges. So, some tests now requires root
    user to be run.
  - There was a bug in the `unset_sandbox_storage` method. The return
    type was wrapped in a `Result` to avoid this problem.

Fixes: #50

Signed-off-by: Erich Cordoba <erich.cordoba.malibran@intel.com>
2019-12-06 13:11:07 -06:00
Fupan Li
e025ba7d08 Merge pull request #99 from jiangliu/v2
Fix bug #98 and improve code readability
2019-12-05 10:01:29 +08:00
Jose Carlos Venegas Munoz
9b2fc09982 Merge pull request #58 from egernst/master-workflow
Master workflow
2019-12-02 11:01:25 -06:00
Liu Jiang
154c68eb93 agent: group Linux ABI constants into dedicated file
Group Linux ABI related constants into dedicated file for maintenance.

Signed-off-by: Liu Jiang <gerry@linux.alibaba.com>
2019-12-02 22:19:33 +08:00
Liu Jiang
000bb8592d agent: refine device.rs for better maintenance
1) pass reference instead of value when possible.
2) simplify code.
3) rename get_device_pci_address() as get_pci_device_address() to keep
   consistency get_pci_device_name().
4) refine get_device_name() for maintenance.

Signed-off-by: Liu Jiang <gerry@linux.alibaba.com>
2019-12-02 22:19:29 +08:00
Liu Jiang
94311e4997 agent: fix wrong return value of set_sandbox_storage()
Function set_sandbox_storage() is designed to return true when the
reference count drops from 1 to 0. But current implementation always
return true no matter the reference count is, which may cause removing
an in use mountpoint.

Fixes: #88

Signed-off-by: Liu Jiang <gerry@linux.alibaba.com>
2019-12-02 22:19:28 +08:00
Liu Jiang
b1748323f0 agent: refine namespace.rs/sandbox.rs for better maintenance
Refine namespace.rs for better maintenance:
1) avoid unnecessary clone
2) make NamespaceType::get() return &str instead of String
3) minor syntax changes
4) remove unused enable_grpc_trace

Signed-off-by: Liu Jiang <gerry@linux.alibaba.com>
2019-12-02 22:19:27 +08:00
Liu Jiang
a4adacaa10 agent: refine uevent.rs for better maintenance
Refine uevent.rs for better maintenance:
1) use dedicated function to handle uevents.
2) use dedicated function to handle blk add events.

Signed-off-by: Liu Jiang <gerry@linux.alibaba.com>
2019-12-02 10:05:44 +08:00
Liu Jiang
8868eaeb4c agent: clean up clippy warnings about '`static'
warning: Constants have by default a `'static` lifetime
  --> src/grpc.rs:59:24
   |
59 | const CONTAINER_BASE: &'static str = "/run/kata-containers";
   |                       -^^^^^^^---- help: consider removing `'static`: `&str`
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes

Signed-off-by: Liu Jiang <gerry@linux.alibaba.com>
2019-11-30 18:54:23 +08:00
Liu Jiang
eb6258b751 agent: improve logger implemetation
Improve loger implementation by:
1) avoid unnecessary clone() operations.
2) change Arc<Mutex<slog::Level>> to Mutex<slog::Level>. We should use
atomic<usize> instead of Mutex<slog::Level> for better performance here.
But with slog_async::Async drainer in the pipeline, RuntimeLevelFilter
drainer will only get from a single-thread context, so keep it as is.
3) minor syntax cleanups.

Signed-off-by: Liu Jiang <gerry@linux.alibaba.com>
2019-11-30 18:33:07 +08:00
James O. D. Hunt
b6c8b9d9f4 Merge pull request #97 from lifupan/fox_parse_cmdline
config: fix the issue of parse cmdline options
2019-11-29 08:16:47 +00:00
lifupan
4c051ed717 config: fix the issue of parse cmdline options
It's should use string.eq() to match option's
key words exactly instead of using string.starts_with()
for single key options and for "key=value" options it's
bettet to match "key=" instead of "key" with string.starts_with()
which would match wrongly such as passed options "agent.log_vsock"
which would match "agent.log" with string.starts_with()
and trigger parsing issues.

Fixes: #96

Signed-off-by: lifupan <lifupan@gmail.com>
2019-11-29 09:21:20 +08:00
Archana Shinde
c5a3fa76be actions: Add job for building nemu
Add job for nemu to support generating tarballs for 1.9 stable
branch.

Signed-off-by: Archana Shinde <archana.m.shinde@intel.com>
2019-11-26 13:16:22 -08:00
Archana Shinde
e005c37274 actions: Add job for building cloud-hypervisor
This will build and store the tarball for cloud-hypervisor.

Signed-off-by: Archana Shinde <archana.m.shinde@intel.com>
2019-11-26 13:16:22 -08:00
Archana Shinde
29c2ff8476 release: Move bash from the actions to separate bash file
Try to move all the common bash logic from the actions toml file
to separate scripts and call these scripts instead.
Note the repo itself is now checked out to get access to
these scripts.

Signed-off-by: Archana Shinde <archana.m.shinde@intel.com>
Signed-off-by: Eric Ernst <eric.ernst@intel.com>
2019-11-26 13:16:22 -08:00
Archana Shinde
383e70344f actions: Add job to upload tarball to release page
Once kata-deploy completes successfully, this job will upload
kata static tarball to the release page using GIT_UPLOAD_TOKEN
secret.

Signed-off-by: Jose Carlos Venegas Munoz<jose.carlos.venegas.munoz@intel.com>
Signed-off-by: Archana Shinde <archana.m.shinde@intel.com>
2019-11-26 11:43:28 -08:00
Eric Ernst
fd5549aa5f workflows: add release workflow
Many changes introduced by Archana Shinde.

This workflow will:
 1. get a list of artifacts from the packaging repo
 2. In parallel, build each of the applicable artifacts
 3. Consolidate the build artifacts from <2>
 4. Test the artifacts in a docker image on AKS
 5. Push the verified docker image to dockerhub

Signed-off-by: Eric Ernst <eric.ernst@intel.com>
Signed-off-by: Archana Shinde <archana.m.shinde@intel.com>
2019-11-25 14:00:15 -08:00
Yang Bo
b9158efe3a Merge pull request #93 from lifupan/fix_copy_file
Fix copy file
2019-11-25 11:40:32 +08:00
lifupan
aeeb6fce73 grpc: fix the issue of wrong containers base dir
The base dir should be "/run/kata-containers" instead
of "/run/agent".

Fixes: #92

Signed-off-by: lifupan <lifupan@gmail.com>
2019-11-25 10:35:17 +08:00
lifupan
5f29f3e293 grpc: fix the issue of return ENOENT for chmod on a file/dir
When call "C" func directly, it's needed to change the string to
CString. To avoid using the unsafe calling, replace it with the
rust safe function to set mode for a file/dir.

Signed-off-by: lifupan <lifupan@gmail.com>
2019-11-25 10:21:07 +08:00
Fupan Li
f2e22eec4f Merge pull request #89 from jiangliu/oci_v1
Minor improvement to agent/oci crate
2019-11-23 13:04:08 +08:00
Liu Jiang
a47a94218f agent: rename SerializeError as Error
Rename SerializeError as Error and export it as the Error codes for
the OCI crate.

Signed-off-by: Liu Jiang <gerry@linux.alibaba.com>
2019-11-22 18:08:56 +08:00
Liu Jiang
c34bdd06db agent: simplify implementation of oci/serialize
Simplify implementation of oci/serialize:
1) explicitly export pub members.
2) avoid unnecessary & and mut operators.
3) define Result to avoid duplicated code.

Signed-off-by: Liu Jiang <gerry@linux.alibaba.com>
2019-11-22 18:08:56 +08:00
Liu Jiang
b0edfc75ff agent: clean up clippy warnings about '`static'
warning: Constants have by default a `'static` lifetime
   --> src/lib.rs:254:26
    |
254 | pub const PIDNAMESPACE: &'static str = "pid";
    |                         -^^^^^^^---- help: consider removing `'static`: `&str`
    |
    = note: #[warn(clippy::const_static_lifetime)] on by default
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime

Fixes: #90

Signed-off-by: Liu Jiang <gerry@linux.alibaba.com>
2019-11-22 16:48:47 +08:00
Yang Bo
459e732ead Merge pull request #88 from lifupan/fix_deadlock
agent: fix the issue dead lock on AGENT_CONFIG
2019-11-19 18:58:03 +08:00
lifupan
6b611030db agent: fix the issue dead lock on AGENT_CONFIG
Once parsed cmdline and set the config on AGENT_CONFIG,
release the write lock as soon as possible. In case other
thread would get read lock on it.

Fixes:#87

Signed-off-by: lifupan <lifupan@gmail.com>
2019-11-19 18:32:11 +08:00
Yang Bo
60d713e84d Merge pull request #82 from ericho/namespace-uts
agent: Add unit tests for `namespace.rs`
2019-11-14 07:39:56 +08:00
Eric Ernst
7be308befe Merge pull request #81 from jodh-intel/add-version-option
Add version option
2019-11-13 08:13:03 -08:00
Erich Cordoba
8aa2c78dd2 agent: Add unit tests for namespace.rs
This patch adds a unit test for `namespace.rs`. The baseline for this
test comes from the Go agent tests in which the namespace is mounted in
a temporary folder.

In order to enable testing in the temporary folder the code was refactored
allow configuration for the specified namespace, thus the changes done
are described below:

- The `setup_persistent_ns` method was moved inside the `Namespace` type.
- A builder pattern was implemented for the `Namespace` type. This allows
  the caller to set the type of the desired namespace as well as the root
  folder which will help the testing.
- A new `NamespaceType` enum was introduced to represent the namespace type.
- The user of the `Namespace` type (sandbox.rs) was updated accordingly.

Fixes: #50

Signed-off-by: Erich Cordoba <erich.cordoba.malibran@intel.com>
2019-11-13 10:04:41 -06:00
Xu Wang
9112257c23 Merge pull request #86 from lifupan/fix_initrd_panic
agent: init agent as init before parsing cmd line
2019-11-13 01:03:48 +08:00
lifupan
d011b39e96 agent: init agent as init before parsing cmd line
When kata-agent run as init process in initrd, do the
init in which will do some base mount such as mount
/proc; thus the following config.parse_cmdline can access
/proc/cmdline to parse the parameters such as agent.log etc.

Fixes: #85

Signed-off-by: lifupan <lifupan@gmail.com>
2019-11-13 01:01:29 +08:00
James O. D. Hunt
3fe04a2ddc main: Add --version CLI option
Support `--version` which dumps the announce message and exits.

Fixes: #80.

Signed-off-by: James O. D. Hunt <james.o.hunt@intel.com>
2019-11-11 13:50:45 +00:00
Fupan Li
cb1849cd2c Merge pull request #84 from ericho/ci-and-rustfmt
agent: Fix CI and rustfmt issues
2019-11-11 11:01:51 +08:00
Fupan Li
60609cacd0 Merge pull request #74 from yyyeerbo/wip
netlink: Fix invalid route crashes agent
2019-11-11 10:59:59 +08:00
Erich Cordoba
8834e3a759 agent: Fix CI and rustfmt issues
The CI was failing due to two problems.

1. The `ci/static-checks.sh` was run from an incorrect place.
2. `rustfmt` was failing as some code wasn't correctly formatted.

This patch address all the changed requested by rustfmt and the
`static-checks.sh` script was updated

Fixes: #83

Signed-off-by: Erich Cordoba <erich.cordoba.malibran@intel.com>
2019-11-08 15:58:59 -06:00
Yang Bo
9dce527793 Merge pull request #77 from awprice/issue-62
config: add hotplug timeout option
2019-11-08 15:05:55 +08:00
Yang Bo
089f3b4651 Merge pull request #72 from ericho/master
agent: Move test macros to a separate module to be commonly used.
2019-11-08 15:02:56 +08:00
Alex Price
e06a230c30 config: add hotplug timeout option
This adds an option to the agent to control the hotplug timeout of block devices.
Retains the previous behaviour of defaulting to 3 seconds if not specified.
Can be increased when block device hot plugging is taking longer than expected.

fixes #62

Signed-off-by: Alex Price <aprice@atlassian.com>
2019-11-06 21:12:27 +11:00
Yang Bo
777cee5436 Merge pull request #71 from jodh-intel/allow-gnu-target-build
build: Allow building with gnu target
2019-11-06 10:27:25 +08:00
Erich Cordoba
b14f5a1f89 agent: Move test macros to a separate module to be commonly used.
The `skip_*` macros will be useful across the different tests so having
them in a separate module can help with code duplication. This change
creates a new module and exports the macros at crate level.

Signed-off-by: Erich Cordoba <erich.cordoba.malibran@intel.com>
2019-11-05 10:01:43 -06:00
Yang Bo
d33c2f84a8 netlink: Fix invalid route crashes agent
Invalid routes in update_routes request crash agent, fix it

Fixes: #73

Signed-off-by: Yang Bo <bo@hyper.sh>
2019-11-05 10:45:35 +08:00
James O. D. Hunt
f55667df38 build: Allow building with gnu target
Fixes to allow the rust agent to be built using a gnu target.
Specifically, remove assumptions about musl-specific types.

Fixes: #70.

Signed-off-by: James O. D. Hunt <james.o.hunt@intel.com>
2019-11-01 14:05:34 +00:00
Yang Bo
d045169476 Merge pull request #68 from yyyeerbo/wip
rust-agent: Set BUILDTYPE to debug.
2019-11-01 19:16:42 +08:00
Yang Bo
783cb13f8d Merge pull request #66 from ericho/master
agent: Remove `get_key_value` to enable building in stable rust.
2019-11-01 18:59:31 +08:00
Fupan Li
ae211e5bba Merge pull request #65 from jodh-intel/create-config-module
main: Split config code into separate module
2019-11-01 18:10:35 +08:00
Yang Bo
9df1d0e002 rust-agent: Set BUILDTYPE to debug.
Since build with --release produces corrupted binary in ci, we removed
--release. However, the make install target cannot find the binary,
set BUILDTYPE to debug

Fixes: #67

Signed-off-by: Yang Bo <bo@hyper.sh>
2019-11-01 17:17:38 +08:00
Erich Cordoba
5c96a920bd agent: Remove get_key_value to enable building in stable rust.
The get_key_value method is currently only avaiable in nightly rust.
As only this feature is required it worth to refactor and enable building
in the stable channel.

The method was removed by first getting the value from the CGROUPS hashmap,
then key is get by iterating over all the keys. The checks for an empty key and
key == "devices" were moved out of the hashmap block.

The README.md was updated as well to detail the instructions for stable rust.

Signed-off-by: Erich Cordoba <erich.cordoba.malibran@intel.com>
2019-10-31 12:25:34 -06:00
James O. D. Hunt
2787c545ac main: Split config code into separate module
Create a config module and add more tests. Also enable setting the log
level from the kernel command-line.

Fixes: #59, #63.

Signed-off-by: James O. D. Hunt <james.o.hunt@intel.com>
2019-10-31 15:07:49 +00:00
Eric Ernst
a19f07b017 Merge pull request #57 from yyyeerbo/wip
rust-agent: Land rust agent into kata-containers
2019-10-31 07:44:09 -07:00
Yang Bo
f8ced638d2 rust-agent: Land rust agent into kata-containers
Fixes: #56

Signed-off-by: Yang Bo <bo@hyper.sh>
2019-10-31 10:46:45 +08:00
86 changed files with 46497 additions and 393 deletions

View File

@@ -1,14 +0,0 @@
FROM ubuntu:latest
LABEL version="0.0.0"
LABEL maintainer="Kata folks"
LABEL com.github.actions.name="Prepare artifacts for Kata release page"
LABEL com.github.actions.description="Create and upload static binaries and Kata images to release page for a given release"
ENV GITHUB_ACTION_NAME="Prepare artifacts for Kata release"
ENV NEW_VERSION="1.8.2"
ENV BRANCH="master"
RUN git clone https://github.com/kata-containers/packaging.git && cd packaging
ENTRYPOINT ["/entrypoint.sh"]

View File

@@ -1,22 +0,0 @@
#!/bin/bash
set -o errexit
set -o pipefail
set -o nounset
die() {
msg="$*"
echo "ERROR: $msg" >&2
exit 1
}
# Entrypoint for the container image, we know that the AKS and Kata setup/testing
# scripts are located at root.
cd obs-packaging
bash -x ./gen_versions_txt.sh ${BRANCH}
cd ../release
bash -x ./publish-kata-image.sh -p ${NEW_VERSION}
bash -x ./kata-deploy-binaries.sh -p ${NEW_VERSION}
echo "maybe it worked"

View File

@@ -1,24 +0,0 @@
FROM microsoft/azure-cli:2.0.47
LABEL version="0.0.0"
LABEL maintainer="eric and sai"
LABEL com.github.actions.name="Test kata-deploy in an AKS cluster"
LABEL com.github.actions.description="Wow. Where do i start. Create an AKS cluster with containerd+runtimeclass, then deploys kata onto it and even might start a workload. nbd"
ARG AKS_ENGINE_VER="v0.36.4"
ENV GITHUB_ACTION_NAME="Test kata-deploy in an AKS cluster"
RUN curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl \
&& chmod +x ./kubectl \
&& mv ./kubectl /usr/local/bin/kubectl
RUN curl -LO https://github.com/Azure/aks-engine/releases/download/${AKS_ENGINE_VER}/aks-engine-${AKS_ENGINE_VER}-linux-amd64.tar.gz \
&& tar xvf aks-engine-${AKS_ENGINE_VER}-linux-amd64.tar.gz \
&& mv aks-engine-${AKS_ENGINE_VER}-linux-amd64/aks-engine /usr/local/bin/aks-engine \
&& rm aks-engine-${AKS_ENGINE_VER}-linux-amd64.tar.gz
COPY kubernetes-containerd.json /
COPY setup-aks.sh test-kata.sh entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]

View File

@@ -1,22 +0,0 @@
#!/bin/bash
set -o errexit
set -o pipefail
set -o nounset
die() {
msg="$*"
echo "ERROR: $msg" >&2
exit 1
}
# Since this is the entrypoint for the container image, we know that the AKS and Kata setup/testing
# scripts are located at root.
source /setup-aks.sh
source /test-kata.sh
trap destroy_aks EXIT
setup_aks
test_kata

View File

@@ -1,41 +0,0 @@
{
"apiVersion": "vlabs",
"properties": {
"orchestratorProfile": {
"orchestratorType": "Kubernetes",
"orchestratorVersion": "1.14.1",
"kubernetesConfig": {
"networkPlugin": "flannel",
"containerRuntime": "containerd",
"containerdVersion": "1.2.4"
}
},
"masterProfile": {
"count": 1,
"dnsPrefix": "",
"vmSize": "Standard_D2_v2"
},
"agentPoolProfiles": [
{
"name": "agentpool",
"count": 1,
"vmSize": "Standard_D4s_v3",
"availabilityProfile": "AvailabilitySet"
}
],
"linuxProfile": {
"adminUsername": "azureuser",
"ssh": {
"publicKeys": [
{
"keyData": ""
}
]
}
},
"servicePrincipalProfile": {
"clientId": "",
"secret": ""
}
}
}

View File

@@ -1,44 +0,0 @@
#!/bin/bash
set -o errexit
set -o pipefail
set -o nounset
export AZURE_HTTP_USER_AGENT="GITHUBACTIONS_${GITHUB_ACTION_NAME}_${GITHUB_REPOSITORY}"
LOCATION=${LOCATION:-westus2}
DNS_PREFIX=${DNS_PREFIX:-kata-deploy-${GITHUB_SHA:0:10}}
CLUSTER_CONFIG=${CLUSTER_CONFIG:-/kubernetes-containerd.json}
function die() {
msg="$*"
echo "ERROR: $msg" >&2
exit 1
}
function destroy_aks() {
set +x
az login --service-principal -u "$AZ_APPID" -p "$AZ_PASSWORD" --tenant "$AZ_TENANT_ID"
az group delete --name "$DNS_PREFIX" --yes --no-wait
az logout
}
function setup_aks() {
[[ -z "$AZ_APPID" ]] && die "no Azure service principal ID provided"
[[ -z "$AZ_PASSWORD" ]] && die "no Azure service principal secret provided"
[[ -z "$AZ_SUBSCRIPTION_ID" ]] && die "no Azure subscription ID provided"
[[ -z "$AZ_TENANT_ID" ]] && die "no Azure tenant ID provided"
# check cluster config existence
# TODO
# Give it a try
aks-engine deploy --subscription-id "$AZ_SUBSCRIPTION_ID" \
--client-id "$AZ_APPID" --client-secret "$AZ_PASSWORD" \
--location "$LOCATION" --dns-prefix "$DNS_PREFIX" \
--api-model "$CLUSTER_CONFIG" --force-overwrite
export KUBECONFIG="_output/$DNS_PREFIX/kubeconfig/kubeconfig.$LOCATION.json"
}

View File

@@ -1,112 +0,0 @@
#!/bin/bash
set -o errexit
set -o pipefail
set -o nounset
function waitForProcess() {
wait_time="$1"
sleep_time="$2"
cmd="$3"
while [ "$wait_time" -gt 0 ]; do
if eval "$cmd"; then
return 0
else
sleep "$sleep_time"
wait_time=$((wait_time-sleep_time))
fi
done
return 1
}
function run_test() {
YAMLPATH="https://raw.githubusercontent.com/egernst/kata-deploy/$GITHUB_SHA/kata-deploy"
echo "verify connectivity with a pod using Kata"
deployment=""
busybox_pod="test-nginx"
busybox_image="busybox"
cmd="kubectl get pods | grep $busybox_pod | grep Completed"
wait_time=120
sleep_time=3
for deployment in "nginx-deployment-qemu" "nginx-deployment-nemu"; do
# start the kata pod:
kubectl apply -f "$YAMLPATH/examples/${deployment}.yaml"
kubectl wait --timeout=5m --for=condition=Available deployment/${deployment}
kubectl wait --timeout=5m --for=condition=Available deployment/${deployment}
kubectl expose deployment/${deployment}
# test pod connectivity:
kubectl run $busybox_pod --restart=Never --image="$busybox_image" -- wget --timeout=5 "$deployment"
waitForProcess "$wait_time" "$sleep_time" "$cmd"
kubectl logs "$busybox_pod" | grep "index.html"
kubectl describe pod "$busybox_pod"
kubectl delete deployment "$deployment"
kubectl delete service "$deployment"
kubectl delete pod "$busybox_pod"
done
}
function test_kata() {
set -x
#kubectl all the things
kubectl get pods --all-namespaces
YAMLPATH="https://raw.githubusercontent.com/egernst/kata-deploy/$GITHUB_SHA/kata-deploy"
kubectl apply -f "$YAMLPATH/kata-rbac.yaml"
kubectl apply -f "$YAMLPATH/k8s-1.14/kata-nemu-runtimeClass.yaml"
kubectl apply -f "$YAMLPATH/k8s-1.14/kata-qemu-runtimeClass.yaml"
kubectl apply -f "$YAMLPATH/k8s-1.14/kata-fc-runtimeClass.yaml"
sleep 5
kubectl get runtimeclasses
wget "$YAMLPATH/kata-deploy.yaml"
wget "$YAMLPATH/kata-cleanup.yaml"
# update deployment daemonset to utilize the container under test:
sed -i "s#katadocker/kata-deploy#katadocker/kata-deploy-ci:${GITHUB_SHA}#g" kata-deploy.yaml
sed -i "s#katadocker/kata-deploy#katadocker/kata-deploy-ci:${GITHUB_SHA}#g" kata-cleanup.yaml
cat kata-deploy.yaml
sleep 100
# deploy kata:
kubectl apply -f kata-deploy.yaml
sleep 1
#wait for kata-deploy to be up
kubectl -n kube-system wait --timeout=5m --for=condition=Ready -l name=kata-deploy pod
#Do I see this?
kubectl get pods --all-namespaces --show-labels
kubectl get node --show-labels
run_test
# remove kata (yeah, we are about to destroy, but good to test this flow as well):
kubectl delete -f kata-deploy.yaml
kubectl -n kube-system wait --timeout=5m --for=delete -l name=kata-deploy pod
kubectl apply -f kata-cleanup.yaml
kubectl -n kube-system wait --timeout=5m --for=condition=Ready -l name=kubelet-kata-cleanup pod
kubectl get pods --all-namespaces --show-labels
kubectl get node --show-labels
kubectl delete -f kata-cleanup.yaml
rm kata-cleanup.yaml
rm kata-deploy.yaml
set +x
}

View File

@@ -1,4 +0,0 @@
VERSION=1.8.0-alpha1
git tag --delete $VERSION
git push origin :$VERSION
git tag -a $VERSION -m "test tag - $VERSION" && git push origin $VERSION

View File

@@ -1 +0,0 @@
adding a readme

18
.github/workflows/gather-artifacts.sh vendored Executable file
View File

@@ -0,0 +1,18 @@
#!/bin/bash
# Copyright (c) 2019 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
#
set -o errexit
set -o pipefail
pushd kata-artifacts >>/dev/null
for c in ./*.tar.gz
do
echo "untarring tarball $c"
tar -xvf $c
done
tar cvfJ ../kata-static.tar.xz ./opt
popd >>/dev/null

View File

@@ -0,0 +1,36 @@
#!/bin/bash
# Copyright (c) 2019 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
#
set -o errexit
set -o pipefail
main() {
artifact_stage=${1:-}
artifact=$(echo ${artifact_stage} | sed -n -e 's/^install_//p' | sed -r 's/_/-/g')
if [ -z "${artifact}" ]; then
"Scripts needs artifact name to build"
exit 1
fi
tag=$(echo $GITHUB_REF | cut -d/ -f3-)
export GOPATH=$HOME/go
go get github.com/kata-containers/packaging || true
pushd $GOPATH/src/github.com/kata-containers/packaging/release >>/dev/null
git checkout $tag
pushd ../obs-packaging
./gen_versions_txt.sh $tag
popd
source ./kata-deploy-binaries.sh
${artifact_stage} $tag
popd
mv $HOME/go/src/github.com/kata-containers/packaging/release/kata-static-${artifact}.tar.gz .
}
main $@

View File

@@ -1,42 +0,0 @@
# When a release page is published, start the release artifact process
on:
push:
tags:
- '*'
name: Build, Test, and Publish kata-deploy
jobs:
# create image and upload to release page (can we get branch information from release tag?
publish-artifacts:
runs-on: ubuntu-latest
steps:
- name : the ok ok
run: |
echo "hello worold"
sudo apt-get update
sudo apt-get install -y curl git
echo "still?"
# for test development:
git clone https://github.com/egernst/packaging-1
cd packaging-1
echo `pwd`
echo `ls`
cd release
echo `pwd`
echo `ls`
#./build-artifacts $GITHUB_TAG
echo "github tag?" $GITHUB_REF
echo "github sha"
echo $GITHUB_SHA
echo `git branch -a --contains $GITHUB_SHA`
echo "done...."
# docker run alpine sh -c date
# cd obs-packaging
#
#./gen_versions_txt.sh "stable-1.8"
# cd ../release
# ./publish-kata-image.sh "1.8.2"
# ./kata-deploy-binaries.sh "1.8.2"

View File

@@ -1,67 +0,0 @@
# When a release page is published, start the release artifact process
on: release
name: Build, Test, and Publish kata-deploy
jobs:
# create image and upload to release page (can we get branch information from release tag?
publish-artifacts:
runs-on: ubuntu-latest
steps:
- name: install-dependencies
- run: |
apt-get upate
apt-get install -y docker-ce git
git clone https://github.com/kata-containers/packaging
cd packaging/obs-packages
./gen_versions_file.txt
cd ../release
echo "maybe it worked"
tree
- name: publish-images
- run : wget all the things
- name: publish-images
- run : ./publish-images.sh
with:
args: tag? sha?
- name: create-static-binaries
- uses: TBD
with: tag?
# test the artifacts
kata-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: tag-filter
uses: actions/bin/filter@master
with:
args: tag
- name: docker-build
uses: actions/docker/cli@master
with:
args: build --build-arg KATA_VER=${GITHUB_REF##*/} -t katadocker/kata-deploy-ci:${{
github.sha }} ./kata-deploy
- name: docker-login
uses: actions/docker/login@master
env:
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
- name: docker-push-sha
uses: actions/docker/cli@master
with:
args: push katadocker/kata-deploy-ci:${{ github.sha }}
- name: aks-test
uses: ./kata-deploy/action
env:
AZ_APPID: ${{ secrets.AZ_APPID }}
AZ_PASSWORD: ${{ secrets.AZ_PASSWORD }}
AZ_SUBSCRIPTION_ID: ${{ secrets.AZ_SUBSCRIPTION_ID }}
AZ_TENANT_ID: ${{ secrets.AZ_TENANT_ID }}
- name: docker-tag-ref
uses: actions/docker/cli@master
with:
args: tag katadocker/kata-deploy-ci:${{ github.sha }} katadocker/kata-deploy:${GITHUB_REF##*/}
- name: docker-push-ref
uses: actions/docker/cli@master
with:
args: push katadocker/kata-deploy:${GITHUB_REF##*/}

349
.github/workflows/main.yaml vendored Normal file
View File

@@ -0,0 +1,349 @@
name: Publish release tarball
on:
push:
tags:
- '*'
jobs:
get-artifact-list:
runs-on: ubuntu-latest
steps:
- name: get the list
run: |
git clone https://github.com/kata-containers/packaging
pushd packaging
tag=$(echo $GITHUB_REF | cut -d/ -f3-)
git checkout $tag
popd
./packaging/artifact-list.sh > artifact-list.txt
- name: save-artifact-list
uses: actions/upload-artifact@v1
with:
name: artifact-list
path: artifact-list.txt
build-kernel:
runs-on: ubuntu-16.04
needs: get-artifact-list
env:
buildstr: "install_kernel"
steps:
- uses: actions/checkout@v1
- name: get-artifact-list
uses: actions/download-artifact@v1
with:
name: artifact-list
- run: |
sudo apt-get update && sudo apt install -y flex bison libelf-dev bc iptables
- name: build-kernel
run: |
if grep -q $buildstr ./artifact-list/artifact-list.txt; then
$GITHUB_WORKSPACE/.github/workflows/generate-artifact-tarball.sh $buildstr
echo ::set-env name=artifact-built::true
else
echo ::set-env name=artifact-built::false
fi
- name: store-artifacts
if: env.artifact-built == 'true'
uses: actions/upload-artifact@v1
with:
name: kata-artifacts
path: kata-static-kernel.tar.gz
build-experimental-kernel:
runs-on: ubuntu-16.04
needs: get-artifact-list
env:
buildstr: "install_experimental_kernel"
steps:
- uses: actions/checkout@v1
- name: get-artifact-list
uses: actions/download-artifact@v1
with:
name: artifact-list
- run: |
sudo apt-get update && sudo apt install -y flex bison libelf-dev bc iptables
- name: build-experimental-kernel
run: |
if grep -q $buildstr ./artifact-list/artifact-list.txt; then
$GITHUB_WORKSPACE/.github/workflows/generate-artifact-tarball.sh $buildstr
echo ::set-env name=artifact-built::true
else
echo ::set-env name=artifact-built::false
fi
- name: store-artifacts
if: env.artifact-built == 'true'
uses: actions/upload-artifact@v1
with:
name: kata-artifacts
path: kata-static-experimental-kernel.tar.gz
build-qemu:
runs-on: ubuntu-16.04
needs: get-artifact-list
env:
buildstr: "install_qemu"
steps:
- uses: actions/checkout@v1
- name: get-artifact-list
uses: actions/download-artifact@v1
with:
name: artifact-list
- name: build-qemu
run: |
if grep -q $buildstr ./artifact-list/artifact-list.txt; then
$GITHUB_WORKSPACE/.github/workflows/generate-artifact-tarball.sh $buildstr
echo ::set-env name=artifact-built::true
else
echo ::set-env name=artifact-built::false
fi
- name: store-artifacts
if: env.artifact-built == 'true'
uses: actions/upload-artifact@v1
with:
name: kata-artifacts
path: kata-static-qemu.tar.gz
build-nemu:
runs-on: ubuntu-16.04
needs: get-artifact-list
env:
buildstr: "install_nemu"
steps:
- uses: actions/checkout@v1
- name: get-artifact-list
uses: actions/download-artifact@v1
with:
name: artifact-list
- name: build-nemu
run: |
if grep -q $buildstr ./artifact-list/artifact-list.txt; then
$GITHUB_WORKSPACE/.github/workflows/generate-artifact-tarball.sh $buildstr
echo ::set-env name=artifact-built::true
else
echo ::set-env name=artifact-built::false
fi
- name: store-artifacts
if: env.artifact-built == 'true'
uses: actions/upload-artifact@v1
with:
name: kata-artifacts
path: kata-static-nemu.tar.gz
# Job for building the QEMU binaries with virtiofs support
build-qemu-virtiofsd:
runs-on: ubuntu-16.04
needs: get-artifact-list
env:
buildstr: "install_qemu_virtiofsd"
steps:
- uses: actions/checkout@v1
- name: get-artifact-list
uses: actions/download-artifact@v1
with:
name: artifact-list
- name: build-qemu-virtiofsd
run: |
if grep -q $buildstr ./artifact-list/artifact-list.txt; then
$GITHUB_WORKSPACE/.github/workflows/generate-artifact-tarball.sh $buildstr
echo ::set-env name=artifact-built::true
else
echo ::set-env name=artifact-built::false
fi
- name: store-artifacts
if: env.artifact-built == 'true'
uses: actions/upload-artifact@v1
with:
name: kata-artifacts
path: kata-static-qemu-virtiofsd.tar.gz
# Job for building the image
build-image:
runs-on: ubuntu-16.04
needs: get-artifact-list
env:
buildstr: "install_image"
steps:
- uses: actions/checkout@v1
- name: get-artifact-list
uses: actions/download-artifact@v1
with:
name: artifact-list
- name: build-image
run: |
if grep -q $buildstr ./artifact-list/artifact-list.txt; then
$GITHUB_WORKSPACE/.github/workflows/generate-artifact-tarball.sh $buildstr
echo ::set-env name=artifact-built::true
else
echo ::set-env name=artifact-built::false
fi
- name: store-artifacts
if: env.artifact-built == 'true'
uses: actions/upload-artifact@v1
with:
name: kata-artifacts
path: kata-static-image.tar.gz
# Job for building firecracker hypervisor
build-firecracker:
runs-on: ubuntu-16.04
needs: get-artifact-list
env:
buildstr: "install_firecracker"
steps:
- uses: actions/checkout@v1
- name: get-artifact-list
uses: actions/download-artifact@v1
with:
name: artifact-list
- name: build-firecracker
run: |
if grep -q $buildstr ./artifact-list/artifact-list.txt; then
$GITHUB_WORKSPACE/.github/workflows/generate-artifact-tarball.sh $buildstr
echo ::set-env name=artifact-built::true
else
echo ::set-env name=artifact-built::false
fi
- name: store-artifacts
if: env.artifact-built == 'true'
uses: actions/upload-artifact@v1
with:
name: kata-artifacts
path: kata-static-firecracker.tar.gz
# Job for building cloud-hypervisor
build-clh:
runs-on: ubuntu-16.04
needs: get-artifact-list
env:
buildstr: "install_clh"
steps:
- uses: actions/checkout@v1
- name: get-artifact-list
uses: actions/download-artifact@v1
with:
name: artifact-list
- name: build-clh
run: |
if grep -q $buildstr ./artifact-list/artifact-list.txt; then
$GITHUB_WORKSPACE/.github/workflows/generate-artifact-tarball.sh $buildstr
echo ::set-env name=artifact-built::true
else
echo ::set-env name=artifact-built::false
fi
- name: store-artifacts
if: env.artifact-built == 'true'
uses: actions/upload-artifact@v1
with:
name: kata-artifacts
path: kata-static-clh.tar.gz
# Job for building kata components
build-kata-components:
runs-on: ubuntu-16.04
needs: get-artifact-list
env:
buildstr: "install_kata_components"
steps:
- uses: actions/checkout@v1
- name: get-artifact-list
uses: actions/download-artifact@v1
with:
name: artifact-list
- name: build-kata-components
run: |
if grep -q $buildstr ./artifact-list/artifact-list.txt; then
$GITHUB_WORKSPACE/.github/workflows/generate-artifact-tarball.sh $buildstr
echo ::set-env name=artifact-built::true
else
echo ::set-env name=artifact-built::false
fi
- name: store-artifacts
if: env.artifact-built == 'true'
uses: actions/upload-artifact@v1
with:
name: kata-artifacts
path: kata-static-kata-components.tar.gz
gather-artifacts:
runs-on: ubuntu-16.04
needs: [build-experimental-kernel, build-kernel, build-qemu, build-qemu-virtiofsd, build-image, build-firecracker, build-kata-components, build-nemu, build-clh]
steps:
- uses: actions/checkout@v1
- name: get-artifacts
uses: actions/download-artifact@v1
with:
name: kata-artifacts
- name: colate-artifacts
run: |
$GITHUB_WORKSPACE/.github/workflows/gather-artifacts.sh
- name: store-artifacts
uses: actions/upload-artifact@v1
with:
name: release-candidate
path: kata-static.tar.xz
kata-deploy:
needs: gather-artifacts
runs-on: ubuntu-latest
steps:
- name: get-artifacts
uses: actions/download-artifact@v1
with:
name: release-candidate
- name: build-and-push-kata-deploy-ci
id: build-and-push-kata-deploy-ci
run: |
tag=$(echo $GITHUB_REF | cut -d/ -f3-)
git clone https://github.com/kata-containers/packaging
pushd packaging
git checkout $tag
pkg_sha=$(git rev-parse HEAD)
popd
mv release-candidate/kata-static.tar.xz ./packaging/kata-deploy/kata-static.tar.xz
docker build --build-arg KATA_ARTIFACTS=kata-static.tar.xz -t katadocker/kata-deploy-ci:$pkg_sha ./packaging/kata-deploy
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
docker push katadocker/kata-deploy-ci:$pkg_sha
echo "##[set-output name=PKG_SHA;]${pkg_sha}"
echo ::set-env name=TAG::$tag
- name: test-kata-deploy-ci-in-aks
uses: ./packaging/kata-deploy/action
with:
packaging-sha: ${{steps.build-and-push-kata-deploy-ci.outputs.PKG_SHA}}
env:
PKG_SHA: ${{steps.build-and-push-kata-deploy-ci.outputs.PKG_SHA}}
AZ_APPID: ${{ secrets.AZ_APPID }}
AZ_PASSWORD: ${{ secrets.AZ_PASSWORD }}
AZ_SUBSCRIPTION_ID: ${{ secrets.AZ_SUBSCRIPTION_ID }}
AZ_TENANT_ID: ${{ secrets.AZ_TENANT_ID }}
- name: push-tarball
run: |
# tag the container image we created and push to DockerHub
tag=$(echo $GITHUB_REF | cut -d/ -f3-)
docker tag katadocker/kata-deploy-ci:${{steps.build-and-push-kata-deploy-ci.outputs.PKG_SHA}} katadocker/kata-deploy:${tag}
docker push katadocker/kata-deploy:${tag}
upload-static-tarball:
needs: kata-deploy
runs-on: ubuntu-latest
steps:
- name: download-artifacts
uses: actions/download-artifact@v1
with:
name: release-candidate
- name: install hub
run: |
HUB_VER=$(curl -s "https://api.github.com/repos/github/hub/releases/latest" | jq -r .tag_name | sed 's/^v//')
wget -q -O- https://github.com/github/hub/releases/download/v$HUB_VER/hub-linux-amd64-$HUB_VER.tgz | \
tar xz --strip-components=2 --wildcards '*/bin/hub' && sudo mv hub /usr/local/bin/hub
- name: push static tarball to github
run: |
tag=$(echo $GITHUB_REF | cut -d/ -f3-)
tarball="kata-static-$tag-x86_64.tar.xz"
repo="https://github.com/kata-containers/runtime.git"
mv release-candidate/kata-static.tar.xz "release-candidate/${tarball}"
git clone "${repo}"
cd runtime
echo "uploading asset '${tarball}' to '${repo}' tag: ${tag}"
GITHUB_TOKEN=${{ secrets.GIT_UPLOAD_TOKEN }} hub release edit -m "" -a "../release-candidate/${tarball}" "${tag}"

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
/target
**/*.rs.bk
**/target
Cargo.lock
**/Cargo.lock

33
.travis.yml Normal file
View File

@@ -0,0 +1,33 @@
# Copyright (c) 2019 Ant Financial
#
# SPDX-License-Identifier: Apache-2.0
#
sudo: required
dist: bionic
os:
- linux
language: rust
rust:
- stable
env:
- target_branch=$TRAVIS_BRANCH RUST_AGENT=yes
before_install:
- "ci/setup.sh"
- "ci/install_go.sh"
- "ci/install_rust.sh"
- "ci/static-checks.sh"
# need to install rust from scratch?
# still need go to download github.com/kata-containers/tests
# which is already installed?
install:
- cd ${TRAVIS_BUILD_DIR}/src/agent && make
script:
- cd ${TRAVIS_BUILD_DIR}/src/agent && make check

7
Makefile Normal file
View File

@@ -0,0 +1,7 @@
# Copyright (c) 2020 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
#
test:
bash ci/go-test.sh

1
VERSION Normal file
View File

@@ -0,0 +1 @@
1.11.3

11
ci/go-test.sh Executable file
View File

@@ -0,0 +1,11 @@
#
# Copyright (c) 2020 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
set -e
cidir=$(dirname "$0")
source "${cidir}/lib.sh"
run_go_test

22
ci/install_go.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/bash
#
# Copyright (c) 2019 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
set -e
cidir=$(dirname "$0")
source "${cidir}/lib.sh"
clone_tests_repo
new_goroot=/usr/local/go
pushd "${tests_repo_dir}"
# Force overwrite the current version of golang
[ -z "${GOROOT}" ] || rm -rf "${GOROOT}"
.ci/install_go.sh -p -f -d "$(dirname ${new_goroot})"
[ -z "${GOROOT}" ] || sudo ln -sf "${new_goroot}" "${GOROOT}"
go version
popd

16
ci/install_rust.sh Executable file
View File

@@ -0,0 +1,16 @@
#!/bin/bash
# Copyright (c) 2019 Ant Financial
#
# SPDX-License-Identifier: Apache-2.0
#
set -e
cidir=$(dirname "$0")
source "${cidir}/lib.sh"
clone_tests_repo
pushd ${tests_repo_dir}
.ci/install_rust.sh
popd

35
ci/lib.sh Normal file
View File

@@ -0,0 +1,35 @@
#
# Copyright (c) 2018 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
export tests_repo="${tests_repo:-github.com/kata-containers/tests}"
export tests_repo_dir="$GOPATH/src/$tests_repo"
clone_tests_repo()
{
# KATA_CI_NO_NETWORK is (has to be) ignored if there is
# no existing clone.
if [ -d "$tests_repo_dir" -a -n "$KATA_CI_NO_NETWORK" ]
then
return
fi
go get -d -u "$tests_repo" || true
if [ -n "${TRAVIS_BRANCH:-}" ]; then
( cd "${tests_repo_dir}" && git checkout "${TRAVIS_BRANCH}" )
fi
}
run_static_checks()
{
clone_tests_repo
bash "$tests_repo_dir/.ci/static-checks.sh" "github.com/kata-containers/kata-containers"
}
run_go_test()
{
clone_tests_repo
bash "$tests_repo_dir/.ci/go-test.sh"
}

16
ci/run.sh Executable file
View File

@@ -0,0 +1,16 @@
#!/bin/bash
#
# Copyright (c) 2019 Ant Financial
#
# SPDX-License-Identifier: Apache-2.0
#
set -e
cidir=$(dirname "$0")
source "${cidir}/lib.sh"
clone_tests_repo
pushd ${tests_repo_dir}
.ci/run.sh
popd

16
ci/setup.sh Executable file
View File

@@ -0,0 +1,16 @@
#!/bin/bash
#
# Copyright (c) 2018 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
set -e
cidir=$(dirname "$0")
source "${cidir}/lib.sh"
clone_tests_repo
pushd "${tests_repo_dir}"
.ci/setup.sh
popd

12
ci/static-checks.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
#
# Copyright (c) 2017-2018 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
set -e
cidir=$(dirname "$0")
source "${cidir}/lib.sh"
run_static_checks

15
src/agent/.cargo/config Normal file
View File

@@ -0,0 +1,15 @@
## Copyright (c) 2020 ARM Limited
##
## SPDX-License-Identifier: Apache-2.0
##
[target.aarch64-unknown-linux-musl]
## Only setting linker with `aarch64-linux-musl-gcc`, the
## `rust-agent` could be totally statically linked.
linker = "aarch64-linux-musl-gcc"
## The __addtf3, __subtf3 and __multf3 symbols are used by aarch64-musl,
## but are not provided by rust compiler-builtins.
## For now, the only functional workaround accepted by rust communities
## is to get them from libgcc.
rustflags = [ "-C", "link-arg=-lgcc" ]

42
src/agent/Cargo.toml Normal file
View File

@@ -0,0 +1,42 @@
[package]
name = "kata-agent"
version = "0.1.0"
authors = ["Yang Bo <bo@hyper.sh>"]
edition = "2018"
[dependencies]
oci = { path = "oci" }
logging = { path = "logging" }
rustjail = { path = "rustjail" }
protocols = { path = "protocols" }
netlink = { path = "netlink" }
lazy_static = "1.3.0"
error-chain = "0.12.1"
grpcio = { git="https://github.com/alipay/grpc-rs", branch="rust_agent" }
protobuf = "2.6.1"
futures = "0.1.27"
libc = "0.2.58"
nix = "0.17.0"
prctl = "1.0.0"
serde_json = "1.0.39"
signal-hook = "0.1.9"
scan_fmt = "0.2.3"
scopeguard = "1.0.0"
regex = "1"
# slog:
# - Dynamic keys required to allow HashMap keys to be slog::Serialized.
# - The 'max_*' features allow changing the log level at runtime
# (by stopping the compiler from removing log calls).
slog = { version = "2.5.2", features = ["dynamic-keys", "max_level_trace", "release_max_level_info"] }
slog-scope = "4.1.2"
# for testing
tempfile = "3.1.0"
[workspace]
members = [
"logging",
"netlink",
"oci",
"protocols",
"rustjail",
]

202
src/agent/LICENSE Normal file
View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

124
src/agent/Makefile Normal file
View File

@@ -0,0 +1,124 @@
# Copyright (c) 2019 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
#
PROJECT_NAME = Kata Containers
PROJECT_URL = https://github.com/kata-containers
PROJECT_COMPONENT = kata-agent
TARGET = $(PROJECT_COMPONENT)
SOURCES := \
$(shell find . 2>&1 | grep -E '.*\.rs$$') \
Cargo.toml
VERSION_FILE := ./VERSION
VERSION := $(shell grep -v ^\# $(VERSION_FILE))
COMMIT_NO := $(shell git rev-parse HEAD 2>/dev/null || true)
COMMIT_NO_SHORT := $(shell git rev-parse --short HEAD 2>/dev/null || true)
COMMIT := $(if $(shell git status --porcelain --untracked-files=no 2>/dev/null || true),${COMMIT_NO}-dirty,${COMMIT_NO})
COMMIT_MSG = $(if $(COMMIT),$(COMMIT),unknown)
# Exported to allow cargo to see it
export VERSION_COMMIT := $(if $(COMMIT),$(VERSION)-$(COMMIT),$(VERSION))
BUILD_TYPE = debug
ARCH = $(shell uname -m)
LIBC = musl
TRIPLE = $(ARCH)-unknown-linux-$(LIBC)
TARGET_PATH = target/$(TRIPLE)/$(BUILD_TYPE)/$(TARGET)
DESTDIR :=
BINDIR := /usr/bin
# Define if agent will be installed as init
INIT := no
# Path to systemd unit directory if installed as not init.
UNIT_DIR := /usr/lib/systemd/system
GENERATED_FILES :=
ifeq ($(INIT),no)
# Unit file to start kata agent in systemd systems
UNIT_FILES = kata-agent.service
GENERATED_FILES := $(UNIT_FILES)
# Target to be reached in systemd services
UNIT_FILES += kata-containers.target
endif
# Display name of command and it's version (or a message if not available).
#
# Arguments:
#
# 1: Name of command
define get_command_version
$(shell printf "%s: %s\\n" $(1) "$(or $(shell $(1) --version 2>/dev/null), (not available))")
endef
define get_toolchain_version
$(shell printf "%s: %s\\n" "toolchain" "$(or $(shell rustup show active-toolchain 2>/dev/null), (unknown))")
endef
define INSTALL_FILE
install -D -m 644 $1 $(DESTDIR)$2/$1 || exit 1;
endef
default: $(TARGET) show-header
$(TARGET): $(TARGET_PATH)
$(TARGET_PATH): $(SOURCES) | show-summary
@cargo build --target $(TRIPLE)
show-header:
@printf "%s - version %s (commit %s)\n\n" "$(TARGET)" "$(VERSION)" "$(COMMIT_MSG)"
$(GENERATED_FILES): %: %.in
@sed \
-e 's|[@]bindir[@]|$(BINDIR)|g' \
-e 's|[@]kata-agent[@]|$(TARGET)|g' \
"$<" > "$@"
install: build-service
@install -D $(TARGET_PATH) $(DESTDIR)/$(BINDIR)/$(TARGET)
clean:
@cargo clean
check:
@cargo test --all --target $(TRIPLE)
run:
@cargo run --target $(TRIPLE)
build-service: $(GENERATED_FILES)
ifeq ($(INIT),no)
@echo "Installing systemd unit files..."
$(foreach f,$(UNIT_FILES),$(call INSTALL_FILE,$f,$(UNIT_DIR)))
endif
show-summary: show-header
@printf "project:\n"
@printf " name: $(PROJECT_NAME)\n"
@printf " url: $(PROJECT_URL)\n"
@printf " component: $(PROJECT_COMPONENT)\n"
@printf "target: $(TARGET)\n"
@printf "architecture:\n"
@printf " host: $(ARCH)\n"
@printf "rust:\n"
@printf " %s\n" "$(call get_command_version,cargo)"
@printf " %s\n" "$(call get_command_version,rustc)"
@printf " %s\n" "$(call get_command_version,rustup)"
@printf " %s\n" "$(call get_toolchain_version)"
@printf "\n"
help: show-summary
.PHONY: \
help \
show-header \
show-summary

67
src/agent/README.md Normal file
View File

@@ -0,0 +1,67 @@
# Kata Agent in Rust
This is a rust version of the [`kata-agent`](https://github.com/kata-containers/kata-agent).
In Denver PTG, [we discussed about re-writing agent in rust](https://etherpad.openstack.org/p/katacontainers-2019-ptg-denver-agenda):
> In general, we all think about re-write agent in rust to reduce the footprint of agent. Moreover, Eric mentioned the possibility to stop using gRPC, which may have some impact on footprint. We may begin to do some PoC to show how much we could save by re-writing agent in rust.
After that, we drafted the initial code here, and any contributions are welcome.
## Features
| Feature | Status |
| :--|:--:|
| **OCI Behaviors** |
| create/start containers | :white_check_mark: |
| signal/wait process | :white_check_mark: |
| exec/list process | :white_check_mark: |
| I/O stream | :white_check_mark: |
| Cgroups | :white_check_mark: |
| Capabilities, rlimit, readonly path, masked path, users | :white_check_mark: |
| container stats (`stats_container`) | :white_check_mark: |
| Hooks | :white_check_mark: |
| **Agent Features & APIs** |
| run agent as `init` (mount fs, udev, setup `lo`) | :white_check_mark: |
| block device as root device | :white_check_mark: |
| Health API | :white_check_mark: |
| network, interface/routes (`update_container`) | :white_check_mark: |
| File transfer API (`copy_file`) | :white_check_mark: |
| Device APIs (`reseed_random_device`, , `online_cpu_memory`, `mem_hotplug_probe`, `set_guet_data_time`) | :white_check_mark: |
| vsock support | :white_check_mark: |
| virtio-serial support | :heavy_multiplication_x: |
| OCI Spec validator | :white_check_mark: |
| **Infrastructures**|
| Debug Console | :white_check_mark: |
| Command line | :white_check_mark: |
| Tracing | :heavy_multiplication_x: |
## Getting Started
### Dependencies
The `rust-agent` depends on [`grpc-rs`](https://github.com/pingcap/grpc-rs) by PingCAP. However, the upstream `grpc-rs` and [gRPC](https://github.com/grpc/grpc) need some changes to be used here, which may take some time to be landed. Therefore, we created a temporary fork or `grpc-rs` here:
- https://github.com/alipay/grpc-rs/tree/rust_agent
### Build from Source
The rust-agent need to be built with rust nightly, and static linked with musl.
```bash
rustup target add x86_64-unknown-linux-musl
git submodule update --init --recursive
sudo ln -s /usr/bin/g++ /bin/musl-g++
cargo build --target x86_64-unknown-linux-musl --release
```
## Run Kata CI with rust-agent
* Firstly, install kata as noted by ["how to install Kata"](https://github.com/kata-containers/documentation/blob/master/install/README.md)
* Secondly, build your own kata initrd/image following the steps in ["how to build your own initrd/image"](https://github.com/kata-containers/documentation/blob/master/Developer-Guide.md#create-and-install-rootfs-and-initrd-image).
notes: Please use your rust agent instead of the go agent when building your initrd/image.
* Clone the kata ci test cases from: https://github.com/kata-containers/tests.git, and then run the cri test with:
```bash
$sudo -E PATH=$PATH -E GOPATH=$GOPATH integration/containerd/shimv2/shimv2-tests.sh
```
## Mini Benchmark
The memory of 'RssAnon' consumed by the go-agent and rust-agent as below:
go-agent: about 11M
rust-agent: about 1.1M

1
src/agent/VERSION Normal file
View File

@@ -0,0 +1 @@
0.0.1

View File

@@ -0,0 +1,22 @@
#
# Copyright (c) 2018-2019 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
#
[Unit]
Description=Kata Containers Agent
Documentation=https://github.com/kata-containers/kata-containers
Wants=kata-containers.target
[Service]
# Send agent output to tty to allow capture debug logs
# from a VM vsock port
StandardOutput=tty
Type=simple
ExecStart=@bindir@/@kata-agent@
LimitNOFILE=infinity
# ExecStop is required for static agent tracing; in all other scenarios
# the runtime handles shutting down the VM.
ExecStop=/bin/sync ; /usr/bin/systemctl --force poweroff
FailureAction=poweroff

View File

@@ -0,0 +1,15 @@
#
# Copyright (c) 2018-2019 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
#
[Unit]
Description=Kata Containers Agent Target
Requires=basic.target
Requires=tmp.mount
Wants=chronyd.service
Requires=kata-agent.service
Conflicts=rescue.service rescue.target
After=basic.target rescue.service rescue.target
AllowIsolate=yes

View File

@@ -0,0 +1,20 @@
[package]
name = "logging"
version = "0.1.0"
authors = ["Tim Zhang <tim@hyper.sh>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde_json = "1.0.39"
# slog:
# - Dynamic keys required to allow HashMap keys to be slog::Serialized.
# - The 'max_*' features allow changing the log level at runtime
# (by stopping the compiler from removing log calls).
slog = { version = "2.5.2", features = ["dynamic-keys", "max_level_trace", "release_max_level_info"] }
slog-json = "2.3.0"
slog-async = "2.3.0"
slog-scope = "4.1.2"
# for testing
tempfile = "3.1.0"

View File

@@ -0,0 +1,252 @@
// Copyright (c) 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
#[macro_use]
extern crate slog;
use slog::{BorrowedKV, Drain, Key, OwnedKV, OwnedKVList, Record, KV};
use std::collections::HashMap;
use std::io;
use std::io::Write;
use std::process;
use std::result;
use std::sync::Mutex;
// XXX: 'writer' param used to make testing possible.
pub fn create_logger<W>(name: &str, source: &str, level: slog::Level, writer: W) -> slog::Logger
where
W: Write + Send + Sync + 'static,
{
let json_drain = slog_json::Json::new(writer)
.add_default_keys()
.build()
.fuse();
// Ensure only a unique set of key/value fields is logged
let unique_drain = UniqueDrain::new(json_drain).fuse();
// Allow runtime filtering of records by log level
let filter_drain = RuntimeLevelFilter::new(unique_drain, level).fuse();
// Ensure the logger is thread-safe
let async_drain = slog_async::Async::new(filter_drain).build().fuse();
// Add some "standard" fields
slog::Logger::root(
async_drain.fuse(),
o!("version" => env!("CARGO_PKG_VERSION"),
"subsystem" => "root",
"pid" => process::id().to_string(),
"name" => name.to_string(),
"source" => source.to_string()),
)
}
// Used to convert an slog::OwnedKVList into a hash map.
struct HashSerializer {
fields: HashMap<String, String>,
}
impl HashSerializer {
fn new() -> HashSerializer {
HashSerializer {
fields: HashMap::new(),
}
}
fn add_field(&mut self, key: String, value: String) {
// Take care to only add the first instance of a key. This matters for loggers (but not
// Records) since a child loggers have parents and the loggers are serialised child first
// meaning the *newest* fields are serialised first.
if !self.fields.contains_key(&key) {
self.fields.insert(key, value);
}
}
fn remove_field(&mut self, key: &str) {
self.fields.remove(key);
}
}
impl KV for HashSerializer {
fn serialize(&self, _record: &Record, serializer: &mut dyn slog::Serializer) -> slog::Result {
for (key, value) in self.fields.iter() {
serializer.emit_str(Key::from(key.to_string()), value)?;
}
Ok(())
}
}
impl slog::Serializer for HashSerializer {
fn emit_arguments(&mut self, key: Key, value: &std::fmt::Arguments) -> slog::Result {
self.add_field(format!("{}", key), format!("{}", value));
Ok(())
}
}
struct UniqueDrain<D> {
drain: D,
}
impl<D> UniqueDrain<D> {
fn new(drain: D) -> Self {
UniqueDrain { drain }
}
}
impl<D> Drain for UniqueDrain<D>
where
D: Drain,
{
type Ok = ();
type Err = io::Error;
fn log(&self, record: &Record, values: &OwnedKVList) -> Result<Self::Ok, Self::Err> {
let mut logger_serializer = HashSerializer::new();
values.serialize(record, &mut logger_serializer)?;
let mut record_serializer = HashSerializer::new();
record.kv().serialize(record, &mut record_serializer)?;
for (key, _) in record_serializer.fields.iter() {
logger_serializer.remove_field(key);
}
let record_owned_kv = OwnedKV(record_serializer);
let record_static = record_static!(record.level(), "");
let new_record = Record::new(&record_static, record.msg(), BorrowedKV(&record_owned_kv));
let logger_owned_kv = OwnedKV(logger_serializer);
let result = self
.drain
.log(&new_record, &OwnedKVList::from(logger_owned_kv));
match result {
Ok(_t) => Ok(()),
Err(_e) => Err(std::io::Error::new(
std::io::ErrorKind::Other,
"failed to drain log".to_string(),
)),
}
}
}
// A RuntimeLevelFilter will discard all log records whose log level is less than the level
// specified in the struct.
struct RuntimeLevelFilter<D> {
drain: D,
level: Mutex<slog::Level>,
}
impl<D> RuntimeLevelFilter<D> {
fn new(drain: D, level: slog::Level) -> Self {
RuntimeLevelFilter {
drain,
level: Mutex::new(level),
}
}
}
impl<D> Drain for RuntimeLevelFilter<D>
where
D: Drain,
{
type Ok = Option<D::Ok>;
type Err = Option<D::Err>;
fn log(
&self,
record: &slog::Record,
values: &slog::OwnedKVList,
) -> result::Result<Self::Ok, Self::Err> {
let log_level = self.level.lock().unwrap();
if record.level().is_at_least(*log_level) {
self.drain.log(record, values)?;
}
Ok(None)
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::Value;
use std::io::prelude::*;
use tempfile::NamedTempFile;
#[test]
fn test_create_logger_write_to_tmpfile() {
// Create a writer for the logger drain to use
let writer = NamedTempFile::new().expect("failed to create tempfile");
// Used to check file contents before the temp file is unlinked
let mut writer_ref = writer.reopen().expect("failed to clone tempfile");
let level = slog::Level::Trace;
let name = "name";
let source = "source";
let record_subsystem = "record-subsystem";
let record_key = "record-key-1";
let record_value = "record-key-2";
let logger = create_logger(name, source, level, writer);
let msg = "foo, bar, baz";
// Call the logger (which calls the drain)
info!(logger, "{}", msg; "subsystem" => record_subsystem, record_key => record_value);
// Force temp file to be flushed
drop(logger);
let mut contents = String::new();
writer_ref
.read_to_string(&mut contents)
.expect("failed to read tempfile contents");
// Convert file to JSON
let fields: Value =
serde_json::from_str(&contents).expect("failed to convert logfile to json");
// Check the expected JSON fields
let field_ts = fields.get("ts").expect("failed to find timestamp field");
assert_ne!(field_ts, "");
let field_version = fields.get("version").expect("failed to find version field");
assert_eq!(field_version, env!("CARGO_PKG_VERSION"));
let field_pid = fields.get("pid").expect("failed to find pid field");
assert_ne!(field_pid, "");
let field_level = fields.get("level").expect("failed to find level field");
assert_eq!(field_level, "INFO");
let field_msg = fields.get("msg").expect("failed to find msg field");
assert_eq!(field_msg, msg);
let field_name = fields.get("name").expect("failed to find name field");
assert_eq!(field_name, name);
let field_source = fields.get("source").expect("failed to find source field");
assert_eq!(field_source, source);
let field_subsystem = fields
.get("subsystem")
.expect("failed to find subsystem field");
// The records field should take priority over the loggers field of the same name
assert_eq!(field_subsystem, record_subsystem);
let field_record_value = fields
.get(record_key)
.expect("failed to find record key field");
assert_eq!(field_record_value, record_value);
}
}

View File

@@ -0,0 +1,19 @@
[package]
name = "netlink"
version = "0.1.0"
authors = ["Yang Bo <yb203166@antfin.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
libc = "0.2.58"
nix = "0.17.0"
protobuf = "2.6.1"
rustjail = { path = "../rustjail" }
protocols = { path = "../protocols" }
slog = { version = "2.5.2", features = ["dynamic-keys", "max_level_trace", "release_max_level_info"] }
slog-json = "2.3.0"
slog-async = "2.3.0"
slog-scope = "4.1.2"
scan_fmt = "0.2.3"

2866
src/agent/netlink/src/lib.rs Normal file

File diff suppressed because it is too large Load Diff

11
src/agent/oci/Cargo.toml Normal file
View File

@@ -0,0 +1,11 @@
[package]
name = "oci"
version = "0.1.0"
authors = ["Yang Bo <bo@hyper.sh>"]
edition = "2018"
[dependencies]
serde = "1.0.91"
serde_derive = "1.0.91"
serde_json = "1.0.39"
libc = "0.2.58"

1616
src/agent/oci/src/lib.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,88 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
use serde::{Deserialize, Serialize};
use serde_json;
use std::error;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::fs::File;
use std::io;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
Io(io::Error),
Json(serde_json::Error),
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
match *self {
Error::Io(ref e) => e.fmt(f),
Error::Json(ref e) => e.fmt(f),
}
}
}
impl error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::Io(ref e) => e.description(),
Error::Json(ref e) => e.description(),
}
}
fn cause(&self) -> Option<&dyn error::Error> {
match *self {
Error::Io(ref e) => Some(e),
Error::Json(ref e) => Some(e),
}
}
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Error {
Error::Io(e)
}
}
impl From<serde_json::Error> for Error {
fn from(e: serde_json::Error) -> Error {
Error::Json(e)
}
}
pub fn to_writer<W, T>(o: &T, w: W) -> Result<()>
where
W: io::Write,
T: Serialize,
{
Ok(serde_json::to_writer(w, o)?)
}
pub fn serialize<T>(o: &T, path: &str) -> Result<()>
where
T: Serialize,
{
let f = File::create(path)?;
Ok(serde_json::to_writer(f, o)?)
}
pub fn to_string<T>(o: &T) -> Result<String>
where
T: Serialize,
{
Ok(serde_json::to_string(o)?)
}
pub fn deserialize<T>(path: &str) -> Result<T>
where
for<'a> T: Deserialize<'a>,
{
let f = File::open(path)?;
Ok(serde_json::from_reader(f)?)
}

View File

@@ -0,0 +1,10 @@
[package]
name = "protocols"
version = "0.1.0"
authors = ["Hui Zhu <teawater@hyper.sh>"]
edition = "2018"
[dependencies]
grpcio = { git="https://github.com/alipay/grpc-rs", branch="rust_agent" }
protobuf = "2.6.1"
futures = "0.1.27"

View File

@@ -0,0 +1,68 @@
#!/bin/bash
die() {
echo $1
exit
}
get_source_version() {
if [ ! -d $GOPATH/src/$1 ]; then
go get -d -v $1
fi
[ $? -eq 0 ] || die "Failed to get $1"
if [ "$2" != "" ] ; then
pushd "${GOPATH}/src/$1"
if [ $(git rev-parse HEAD) != $2 ] ; then
git checkout $2
[ $? -eq 0 ] || die "Failed to get $1 $2"
fi
popd
fi
}
get_rs() {
local cmd="protoc --rust_out=./src/ --grpc_out=./src/,plugins=grpc:./src/ --plugin=protoc-gen-grpc=`which grpc_rust_plugin` -I ./protos/ ./protos/$1"
echo $cmd
$cmd
[ $? -eq 0 ] || die "Failed to get rust from $1"
}
if [ "$(basename $(pwd))" != "protocols" ] || [ ! -d "./hack/" ]; then
die "Please go to directory of protocols before execute this shell"
fi
which protoc
[ $? -eq 0 ] || die "Please install protoc from github.com/protocolbuffers/protobuf"
which protoc-gen-rust
[ $? -eq 0 ] || die "Please install protobuf-codegen from github.com/pingcap/grpc-rs"
which grpc_rust_plugin
[ $? -eq 0 ] || die "Please install grpc_rust_plugin from github.com/pingcap/grpc-rs"
if [ $UPDATE_PROTOS ]; then
if [ ! $GOPATH ]; then
die 'Need $GOPATH to get the proto files'
fi
get_source_version "github.com/kata-containers/agent" ""
cp $GOPATH/src/github.com/kata-containers/agent/protocols/grpc/agent.proto ./protos/
cp $GOPATH/src/github.com/kata-containers/agent/protocols/grpc/oci.proto ./protos/
cp $GOPATH/src/github.com/kata-containers/agent/protocols/grpc/health.proto ./protos/
mkdir -p ./protos/github.com/kata-containers/agent/pkg/types/
cp $GOPATH/src/github.com/kata-containers/agent/pkg/types/types.proto ./protos/github.com/kata-containers/agent/pkg/types/
# The version is get from https://github.com/kata-containers/agent/blob/master/Gopkg.toml
get_source_version "github.com/gogo/protobuf" "4cbf7e384e768b4e01799441fdf2a706a5635ae7"
mkdir -p ./protos/github.com/gogo/protobuf/gogoproto/
cp $GOPATH/src/github.com/gogo/protobuf/gogoproto/gogo.proto ./protos/github.com/gogo/protobuf/gogoproto/
mkdir -p ./protos/google/protobuf/
cp $GOPATH/src/github.com/gogo/protobuf/protobuf/google/protobuf/empty.proto ./protos/google/protobuf/
fi
get_rs agent.proto
get_rs health.proto
get_rs github.com/kata-containers/agent/pkg/types/types.proto
get_rs google/protobuf/empty.proto
get_rs oci.proto
# Need change Box<Self> to ::std::boxed::Box<Self> because there is another struct Box
sed 's/fn into_any(self: Box<Self>) -> ::std::boxed::Box<::std::any::Any> {/fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<::std::any::Any> {/g' src/oci.rs > src/new_oci.rs
mv src/new_oci.rs src/oci.rs

View File

@@ -0,0 +1,487 @@
//
// Copyright 2017 HyperHQ Inc.
// Copyright 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
syntax = "proto3";
package grpc;
import "oci.proto";
import "github.com/kata-containers/agent/pkg/types/types.proto";
import "google/protobuf/empty.proto";
// unstable
service AgentService {
// execution
rpc CreateContainer(CreateContainerRequest) returns (google.protobuf.Empty);
rpc StartContainer(StartContainerRequest) returns (google.protobuf.Empty);
// RemoveContainer will tear down an existing container by forcibly terminating
// all processes running inside that container and releasing all internal
// resources associated with it.
// RemoveContainer will wait for all processes termination before returning.
// If any process can not be killed or if it can not be killed after
// the RemoveContainerRequest timeout, RemoveContainer will return an error.
rpc RemoveContainer(RemoveContainerRequest) returns (google.protobuf.Empty);
rpc ExecProcess(ExecProcessRequest) returns (google.protobuf.Empty);
rpc SignalProcess(SignalProcessRequest) returns (google.protobuf.Empty);
rpc WaitProcess(WaitProcessRequest) returns (WaitProcessResponse); // wait & reap like waitpid(2)
rpc ListProcesses(ListProcessesRequest) returns (ListProcessesResponse);
rpc UpdateContainer(UpdateContainerRequest) returns (google.protobuf.Empty);
rpc StatsContainer(StatsContainerRequest) returns (StatsContainerResponse);
rpc PauseContainer(PauseContainerRequest) returns (google.protobuf.Empty);
rpc ResumeContainer(ResumeContainerRequest) returns (google.protobuf.Empty);
// stdio
rpc WriteStdin(WriteStreamRequest) returns (WriteStreamResponse);
rpc ReadStdout(ReadStreamRequest) returns (ReadStreamResponse);
rpc ReadStderr(ReadStreamRequest) returns (ReadStreamResponse);
rpc CloseStdin(CloseStdinRequest) returns (google.protobuf.Empty);
rpc TtyWinResize(TtyWinResizeRequest) returns (google.protobuf.Empty);
// networking
rpc UpdateInterface(UpdateInterfaceRequest) returns (types.Interface);
rpc UpdateRoutes(UpdateRoutesRequest) returns (Routes);
rpc ListInterfaces(ListInterfacesRequest) returns(Interfaces);
rpc ListRoutes(ListRoutesRequest) returns (Routes);
// tracing
rpc StartTracing(StartTracingRequest) returns (google.protobuf.Empty);
rpc StopTracing(StopTracingRequest) returns (google.protobuf.Empty);
// misc (TODO: some rpcs can be replaced by hyperstart-exec)
rpc CreateSandbox(CreateSandboxRequest) returns (google.protobuf.Empty);
rpc DestroySandbox(DestroySandboxRequest) returns (google.protobuf.Empty);
rpc OnlineCPUMem(OnlineCPUMemRequest) returns (google.protobuf.Empty);
rpc ReseedRandomDev(ReseedRandomDevRequest) returns (google.protobuf.Empty);
rpc GetGuestDetails(GuestDetailsRequest) returns (GuestDetailsResponse);
rpc MemHotplugByProbe(MemHotplugByProbeRequest) returns (google.protobuf.Empty);
rpc SetGuestDateTime(SetGuestDateTimeRequest) returns (google.protobuf.Empty);
rpc CopyFile(CopyFileRequest) returns (google.protobuf.Empty);
}
message CreateContainerRequest {
string container_id = 1;
string exec_id = 2;
StringUser string_user = 3;
repeated Device devices = 4;
repeated Storage storages = 5;
Spec OCI = 6;
// This field is used to indicate if the container needs to join
// sandbox shared pid ns or create a new namespace. This field is
// meant to override the NEWPID config settings in the OCI spec.
// The agent would receive an OCI spec with PID namespace cleared
// out altogether and not just the pid ns path.
bool sandbox_pidns = 7;
}
message StartContainerRequest {
string container_id = 1;
}
message RemoveContainerRequest {
string container_id = 1;
// RemoveContainer will return an error if
// it could not kill some container processes
// after timeout seconds.
// Setting timeout to 0 means RemoveContainer will
// wait for ever.
uint32 timeout = 2;
}
message ExecProcessRequest {
string container_id = 1;
string exec_id = 2;
StringUser string_user = 3;
Process process = 4;
}
message SignalProcessRequest {
string container_id = 1;
// Special case for SignalProcess(): exec_id can be empty(""),
// which means to send the signal to all the processes including their descendants.
// Other APIs with exec_id should treat empty exec_id as an invalid request.
string exec_id = 2;
uint32 signal = 3;
}
message WaitProcessRequest {
string container_id = 1;
string exec_id = 2;
}
message WaitProcessResponse {
int32 status = 1;
}
// ListProcessesRequest contains the options used to list running processes inside the container
message ListProcessesRequest {
string container_id = 1;
string format = 2;
repeated string args = 3;
}
// ListProcessesResponse represents the list of running processes inside the container
message ListProcessesResponse {
bytes process_list = 1;
}
message UpdateContainerRequest {
string container_id = 1;
LinuxResources resources = 2;
}
message StatsContainerRequest {
string container_id = 1;
}
message PauseContainerRequest {
string container_id = 1;
}
message ResumeContainerRequest {
string container_id = 1;
}
message CpuUsage {
uint64 total_usage = 1;
repeated uint64 percpu_usage = 2;
uint64 usage_in_kernelmode = 3;
uint64 usage_in_usermode = 4;
}
message ThrottlingData {
uint64 periods = 1;
uint64 throttled_periods = 2;
uint64 throttled_time = 3;
}
message CpuStats {
CpuUsage cpu_usage = 1;
ThrottlingData throttling_data = 2;
}
message PidsStats {
uint64 current = 1;
uint64 limit = 2;
}
message MemoryData {
uint64 usage = 1;
uint64 max_usage = 2;
uint64 failcnt = 3;
uint64 limit = 4;
}
message MemoryStats {
uint64 cache = 1;
MemoryData usage = 2;
MemoryData swap_usage = 3;
MemoryData kernel_usage = 4;
bool use_hierarchy = 5;
map<string, uint64> stats = 6;
}
message BlkioStatsEntry {
uint64 major = 1;
uint64 minor = 2;
string op = 3;
uint64 value = 4;
}
message BlkioStats {
repeated BlkioStatsEntry io_service_bytes_recursive = 1; // number of bytes transferred to and from the block device
repeated BlkioStatsEntry io_serviced_recursive = 2;
repeated BlkioStatsEntry io_queued_recursive = 3;
repeated BlkioStatsEntry io_service_time_recursive = 4;
repeated BlkioStatsEntry io_wait_time_recursive = 5;
repeated BlkioStatsEntry io_merged_recursive = 6;
repeated BlkioStatsEntry io_time_recursive = 7;
repeated BlkioStatsEntry sectors_recursive = 8;
}
message HugetlbStats {
uint64 usage = 1;
uint64 max_usage = 2;
uint64 failcnt = 3;
}
message CgroupStats {
CpuStats cpu_stats = 1;
MemoryStats memory_stats = 2;
PidsStats pids_stats = 3;
BlkioStats blkio_stats = 4;
map<string, HugetlbStats> hugetlb_stats = 5; // the map is in the format "size of hugepage: stats of the hugepage"
}
message NetworkStats {
string name = 1;
uint64 rx_bytes = 2;
uint64 rx_packets = 3;
uint64 rx_errors = 4;
uint64 rx_dropped = 5;
uint64 tx_bytes = 6;
uint64 tx_packets = 7;
uint64 tx_errors = 8;
uint64 tx_dropped = 9;
}
message StatsContainerResponse {
CgroupStats cgroup_stats = 1;
repeated NetworkStats network_stats = 2;
}
message WriteStreamRequest {
string container_id = 1;
string exec_id = 2;
bytes data = 3;
}
message WriteStreamResponse {
uint32 len = 1;
}
message ReadStreamRequest {
string container_id = 1;
string exec_id = 2;
uint32 len = 3;
}
message ReadStreamResponse {
bytes data = 1;
}
message CloseStdinRequest {
string container_id = 1;
string exec_id = 2;
}
message TtyWinResizeRequest {
string container_id = 1;
string exec_id = 2;
uint32 row = 3;
uint32 column = 4;
}
message CreateSandboxRequest {
string hostname = 1;
repeated string dns = 2;
repeated Storage storages = 3;
// This field means that a pause process needs to be created by the
// agent. This pid namespace of the pause process will be treated as
// a shared pid namespace. All containers created will join this shared
// pid namespace.
bool sandbox_pidns = 4;
// SandboxId identifies which sandbox is using the agent. We allow only
// one sandbox per agent and implicitly require that CreateSandbox is
// called before other sandbox/network calls.
string sandbox_id = 5;
// This field, if non-empty, designates an absolute path to a directory
// that the agent will search for OCI hooks to run within the guest.
string guest_hook_path = 6;
}
message DestroySandboxRequest {
}
message Interfaces {
repeated types.Interface Interfaces = 1;
}
message Routes {
repeated types.Route Routes = 1;
}
message UpdateInterfaceRequest {
types.Interface interface = 1;
}
message UpdateRoutesRequest {
Routes routes = 1;
}
message ListInterfacesRequest {
}
message ListRoutesRequest {
}
message OnlineCPUMemRequest {
// Wait specifies if the caller waits for the agent to online all resources.
// If true the agent returns once all resources have been connected, otherwise all
// resources are connected asynchronously and the agent returns immediately.
bool wait = 1;
// NbCpus specifies the number of CPUs that were added and the agent has to online.
uint32 nb_cpus = 2;
// CpuOnly specifies whether only online CPU or not.
bool cpu_only = 3;
}
message ReseedRandomDevRequest {
// Data specifies the random data used to reseed the guest crng.
bytes data = 2;
}
// AgentDetails provides information to the client about the running agent.
message AgentDetails {
// Semantic version of agent (see https://semver.org).
string version = 1;
// Set if the agent is running as PID 1.
bool init_daemon = 2;
// List of available device handlers.
repeated string device_handlers = 3;
// List of available storage handlers.
repeated string storage_handlers = 4;
// Set only if the agent is built with seccomp support and the guest
// environment supports seccomp.
bool supports_seccomp = 5;
}
message GuestDetailsRequest {
// MemBlockSize asks server to return the system memory block size that can be used
// for memory hotplug alignment. Typically the server returns what's in
// /sys/devices/system/memory/block_size_bytes.
bool mem_block_size = 1;
// MemoryHotplugProbe asks server to return whether guest kernel supports memory hotplug
// via probeinterface. Typically the server will check if the path
// /sys/devices/system/memory/probe exists.
bool mem_hotplug_probe = 2;
}
message GuestDetailsResponse {
// MemBlockSizeBytes returns the system memory block size in bytes.
uint64 mem_block_size_bytes = 1;
AgentDetails agent_details = 2;
bool support_mem_hotplug_probe = 3;
}
message MemHotplugByProbeRequest {
// server needs to send the value of memHotplugProbeAddr into file /sys/devices/system/memory/probe,
// in order to notify the guest kernel about hot-add memory event
repeated uint64 memHotplugProbeAddr = 1;
}
message SetGuestDateTimeRequest {
// Sec the second since the Epoch.
int64 Sec = 1;
// Usec the microseconds portion of time since the Epoch.
int64 Usec = 2;
}
// Storage represents both the rootfs of the container, and any volume that
// could have been defined through the Mount list of the OCI specification.
message Storage {
// Driver is used to define the way the storage is passed through the
// virtual machine. It can be "9p", "blk", or something else, but for
// all cases, this will define if some extra steps are required before
// this storage gets mounted into the container.
string driver = 1;
// DriverOptions allows the caller to define a list of options such
// as block sizes, numbers of luns, ... which are very specific to
// every device and cannot be generalized through extra fields.
repeated string driver_options = 2;
// Source can be anything representing the source of the storage. This
// will be handled by the proper handler based on the Driver used.
// For instance, it can be a very simple path if the caller knows the
// name of device inside the VM, or it can be some sort of identifier
// to let the agent find the device inside the VM.
string source = 3;
// Fstype represents the filesystem that needs to be used to mount the
// storage inside the VM. For instance, it could be "xfs" for block
// device, "9p" for shared filesystem, or "tmpfs" for shared /dev/shm.
string fstype = 4;
// Options describes the additional options that might be needed to
// mount properly the storage filesytem.
repeated string options = 5;
// MountPoint refers to the path where the storage should be mounted
// inside the VM.
string mount_point = 6;
}
// Device represents only the devices that could have been defined through the
// Linux Device list of the OCI specification.
message Device {
// Id can be used to identify the device inside the VM. Some devices
// might not need it to be identified on the VM, and will rely on the
// provided VmPath instead.
string id = 1;
// Type defines the type of device described. This can be "blk",
// "scsi", "vfio", ...
// Particularly, this should be used to trigger the use of the
// appropriate device handler.
string type = 2;
// VmPath can be used by the caller to provide directly the path of
// the device as it will appear inside the VM. For some devices, the
// device id or the list of options passed might not be enough to find
// the device. In those cases, the caller should predict and provide
// this vm_path.
string vm_path = 3;
// ContainerPath defines the path where the device should be found inside
// the container. This path should match the path of the device from
// the device list listed inside the OCI spec. This is used in order
// to identify the right device in the spec and update it with the
// right options such as major/minor numbers as they appear inside
// the VM for instance. Note that an empty ctr_path should be used
// to make sure the device handler inside the agent is called, but
// no spec update needs to be performed. This has to happen for the
// case of rootfs, when a device has to be waited for after it has
// been hotplugged. An equivalent Storage entry should be defined if
// any mount needs to be performed afterwards.
string container_path = 4;
// Options allows the caller to define a list of options such as block
// sizes, numbers of luns, ... which are very specific to every device
// and cannot be generalized through extra fields.
repeated string options = 5;
}
message StringUser {
string uid = 1;
string gid = 2;
repeated string additionalGids = 3;
}
message CopyFileRequest {
// Path is the destination file in the guest. It must be absolute,
// canonical and below /run.
string path = 1;
// FileSize is the expected file size, for security reasons write operations
// are made in a temporary file, once it has the expected size, it's moved
// to the destination path.
int64 file_size = 2;
// FileMode is the file mode.
uint32 file_mode = 3;
// DirMode is the mode for the parent directories of destination path.
uint32 dir_mode = 4;
// Uid is the numeric user id.
int32 uid = 5;
// Gid is the numeric group id.
int32 gid = 6;
// Offset for the next write operation.
int64 offset = 7;
// Data to write in the destination file.
bytes data = 8;
}
message StartTracingRequest {
}
message StopTracingRequest {
}

View File

@@ -0,0 +1,144 @@
// 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 = "proto2";
package gogoproto;
import "google/protobuf/descriptor.proto";
option java_package = "com.google.protobuf";
option java_outer_classname = "GoGoProtos";
option go_package = "github.com/gogo/protobuf/gogoproto";
extend google.protobuf.EnumOptions {
optional bool goproto_enum_prefix = 62001;
optional bool goproto_enum_stringer = 62021;
optional bool enum_stringer = 62022;
optional string enum_customname = 62023;
optional bool enumdecl = 62024;
}
extend google.protobuf.EnumValueOptions {
optional string enumvalue_customname = 66001;
}
extend google.protobuf.FileOptions {
optional bool goproto_getters_all = 63001;
optional bool goproto_enum_prefix_all = 63002;
optional bool goproto_stringer_all = 63003;
optional bool verbose_equal_all = 63004;
optional bool face_all = 63005;
optional bool gostring_all = 63006;
optional bool populate_all = 63007;
optional bool stringer_all = 63008;
optional bool onlyone_all = 63009;
optional bool equal_all = 63013;
optional bool description_all = 63014;
optional bool testgen_all = 63015;
optional bool benchgen_all = 63016;
optional bool marshaler_all = 63017;
optional bool unmarshaler_all = 63018;
optional bool stable_marshaler_all = 63019;
optional bool sizer_all = 63020;
optional bool goproto_enum_stringer_all = 63021;
optional bool enum_stringer_all = 63022;
optional bool unsafe_marshaler_all = 63023;
optional bool unsafe_unmarshaler_all = 63024;
optional bool goproto_extensions_map_all = 63025;
optional bool goproto_unrecognized_all = 63026;
optional bool gogoproto_import = 63027;
optional bool protosizer_all = 63028;
optional bool compare_all = 63029;
optional bool typedecl_all = 63030;
optional bool enumdecl_all = 63031;
optional bool goproto_registration = 63032;
optional bool messagename_all = 63033;
optional bool goproto_sizecache_all = 63034;
optional bool goproto_unkeyed_all = 63035;
}
extend google.protobuf.MessageOptions {
optional bool goproto_getters = 64001;
optional bool goproto_stringer = 64003;
optional bool verbose_equal = 64004;
optional bool face = 64005;
optional bool gostring = 64006;
optional bool populate = 64007;
optional bool stringer = 67008;
optional bool onlyone = 64009;
optional bool equal = 64013;
optional bool description = 64014;
optional bool testgen = 64015;
optional bool benchgen = 64016;
optional bool marshaler = 64017;
optional bool unmarshaler = 64018;
optional bool stable_marshaler = 64019;
optional bool sizer = 64020;
optional bool unsafe_marshaler = 64023;
optional bool unsafe_unmarshaler = 64024;
optional bool goproto_extensions_map = 64025;
optional bool goproto_unrecognized = 64026;
optional bool protosizer = 64028;
optional bool compare = 64029;
optional bool typedecl = 64030;
optional bool messagename = 64033;
optional bool goproto_sizecache = 64034;
optional bool goproto_unkeyed = 64035;
}
extend google.protobuf.FieldOptions {
optional bool nullable = 65001;
optional bool embed = 65002;
optional string customtype = 65003;
optional string customname = 65004;
optional string jsontag = 65005;
optional string moretags = 65006;
optional string casttype = 65007;
optional string castkey = 65008;
optional string castvalue = 65009;
optional bool stdtime = 65010;
optional bool stdduration = 65011;
optional bool wktpointer = 65012;
}

View File

@@ -0,0 +1,49 @@
//
// Copyright 2018 Intel Corporation.
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
syntax = "proto3";
package types;
enum IPFamily {
v4 = 0;
v6 = 1;
}
message IPAddress {
IPFamily family = 1;
string address = 2;
string mask = 3;
}
message Interface {
string device = 1;
string name = 2;
repeated IPAddress IPAddresses = 3;
uint64 mtu = 4;
string hwAddr = 5;
// pciAddr is the PCI address in the format "bridgeAddr/deviceAddr".
// Here, bridgeAddr is the address at which the bridge is attached on the root bus,
// while deviceAddr is the address at which the network device is attached on the bridge.
string pciAddr = 6;
// Type defines the type of interface described by this structure.
// The expected values are the one that are defined by the netlink
// library, regarding each type of link. Here is a non exhaustive
// list: "veth", "macvtap", "vlan", "macvlan", "tap", ...
string type = 7;
uint32 raw_flags = 8;
}
message Route {
string dest = 1;
string gateway = 2;
string device = 3;
string source = 4;
uint32 scope = 5;
}

View File

@@ -0,0 +1,52 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// 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.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// 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 google.protobuf;
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option go_package = "types";
option java_package = "com.google.protobuf";
option java_outer_classname = "EmptyProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option cc_enable_arenas = true;
// A generic empty message that you can re-use to avoid defining duplicated
// empty messages in your APIs. A typical example is to use it as the request
// or the response type of an API method. For instance:
//
// service Foo {
// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);
// }
//
// The JSON representation for `Empty` is empty JSON object `{}`.
message Empty {}

View File

@@ -0,0 +1,40 @@
//
// Copyright 2017 HyperHQ Inc.
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
syntax = "proto3";
package grpc;
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
option (gogoproto.equal_all) = true;
option (gogoproto.populate_all) = true;
option (gogoproto.testgen_all) = true;
option (gogoproto.benchgen_all) = true;
message CheckRequest {
string service = 1;
}
message HealthCheckResponse {
enum ServingStatus {
UNKNOWN = 0;
SERVING = 1;
NOT_SERVING = 2;
}
ServingStatus status = 1;
}
message VersionCheckResponse {
string grpc_version = 1;
string agent_version = 2;
}
service Health {
rpc Check(CheckRequest) returns (HealthCheckResponse);
rpc Version(CheckRequest) returns (VersionCheckResponse);
}

View File

@@ -0,0 +1,463 @@
//
// Copyright (c) 2017 Intel Corporation
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
syntax = "proto3";
package grpc;
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
import "google/protobuf/wrappers.proto";
option (gogoproto.equal_all) = true;
option (gogoproto.populate_all) = true;
option (gogoproto.testgen_all) = true;
option (gogoproto.benchgen_all) = true;
message Spec {
// Version of the Open Container Initiative Runtime Specification with which the bundle complies.
string Version = 1;
// Process configures the container process.
Process Process = 2;
// Root configures the container's root filesystem.
Root Root = 3;
// Hostname configures the container's hostname.
string Hostname = 4;
// Mounts configures additional mounts (on top of Root).
repeated Mount Mounts = 5 [(gogoproto.nullable) = false];
// Hooks configures callbacks for container lifecycle events.
Hooks Hooks = 6;
// Annotations contains arbitrary metadata for the container.
map<string, string> Annotations = 7;
// Linux is platform-specific configuration for Linux based containers.
Linux Linux = 8;
// Solaris is platform-specific configuration for Solaris based containers.
Solaris Solaris = 9;
// Windows is platform-specific configuration for Windows based containers.
Windows Windows = 10;
}
message Process {
// Terminal creates an interactive terminal for the container.
bool Terminal = 1;
// ConsoleSize specifies the size of the console.
Box ConsoleSize = 2;
// User specifies user information for the process.
User User = 3 [(gogoproto.nullable) = false];
// Args specifies the binary and arguments for the application to execute.
repeated string Args = 4;
// Env populates the process environment for the process.
repeated string Env = 5;
// Cwd is the current working directory for the process and must be
// relative to the container's root.
string Cwd = 6;
// Capabilities are Linux capabilities that are kept for the process.
LinuxCapabilities Capabilities = 7;
// Rlimits specifies rlimit options to apply to the process.
repeated POSIXRlimit Rlimits = 8 [(gogoproto.nullable) = false];
// NoNewPrivileges controls whether additional privileges could be gained by processes in the container.
bool NoNewPrivileges = 9;
// ApparmorProfile specifies the apparmor profile for the container.
string ApparmorProfile = 10;
// Specify an oom_score_adj for the container.
int64 OOMScoreAdj = 11;
// SelinuxLabel specifies the selinux context that the container process is run as.
string SelinuxLabel = 12;
}
message Box {
// Height is the vertical dimension of a box.
uint32 Height = 1;
// Width is the horizontal dimension of a box.
uint32 Width = 2;
}
message User {
// UID is the user id.
uint32 UID = 1;
// GID is the group id.
uint32 GID = 2;
// AdditionalGids are additional group ids set for the container's process.
repeated uint32 AdditionalGids = 3;
// Username is the user name.
string Username = 4;
}
message LinuxCapabilities {
// Bounding is the set of capabilities checked by the kernel.
repeated string Bounding = 1;
// Effective is the set of capabilities checked by the kernel.
repeated string Effective = 2;
// Inheritable is the capabilities preserved across execve.
repeated string Inheritable = 3;
// Permitted is the limiting superset for effective capabilities.
repeated string Permitted = 4;
// Ambient is the ambient set of capabilities that are kept.
repeated string Ambient = 5;
}
message POSIXRlimit {
// Type of the rlimit to set
string Type = 1;
// Hard is the hard limit for the specified type
uint64 Hard = 2;
// Soft is the soft limit for the specified type
uint64 Soft = 3;
}
message Mount {
// destination is the path inside the container expect when it starts with "tmp:/"
string destination = 1;
// source is the path inside the container expect when it starts with "vm:/dev/" or "tmp:/"
// the path which starts with "vm:/dev/" refers the guest vm's "/dev",
// especially, "vm:/dev/hostfs/" refers to the shared filesystem.
// "tmp:/" is a temporary directory which is used for temporary mounts.
string source = 2;
string type = 3;
repeated string options = 4;
}
message Root {
// Path is the absolute path to the container's root filesystem.
string Path = 1;
// Readonly makes the root filesystem for the container readonly before the process is executed.
bool Readonly = 2;
}
message Hooks {
// Prestart is a list of hooks to be run before the container process is executed.
repeated Hook Prestart = 1 [(gogoproto.nullable) = false];
// Poststart is a list of hooks to be run after the container process is started.
repeated Hook Poststart = 2 [(gogoproto.nullable) = false];
// Poststop is a list of hooks to be run after the container process exits.
repeated Hook Poststop = 3 [(gogoproto.nullable) = false];
}
message Hook {
string Path = 1;
repeated string Args = 2;
repeated string Env = 3;
int64 Timeout = 4;
}
message Linux {
// UIDMapping specifies user mappings for supporting user namespaces.
repeated LinuxIDMapping UIDMappings = 1 [(gogoproto.nullable) = false];
// GIDMapping specifies group mappings for supporting user namespaces.
repeated LinuxIDMapping GIDMappings = 2 [(gogoproto.nullable) = false];
// Sysctl are a set of key value pairs that are set for the container on start
map<string, string> Sysctl = 3;
// Resources contain cgroup information for handling resource constraints
// for the container
LinuxResources Resources = 4;
// CgroupsPath specifies the path to cgroups that are created and/or joined by the container.
// The path is expected to be relative to the cgroups mountpoint.
// If resources are specified, the cgroups at CgroupsPath will be updated based on resources.
string CgroupsPath = 5;
// Namespaces contains the namespaces that are created and/or joined by the container
repeated LinuxNamespace Namespaces = 6 [(gogoproto.nullable) = false];
// Devices are a list of device nodes that are created for the container
repeated LinuxDevice Devices = 7 [(gogoproto.nullable) = false];
// Seccomp specifies the seccomp security settings for the container.
LinuxSeccomp Seccomp = 8;
// RootfsPropagation is the rootfs mount propagation mode for the container.
string RootfsPropagation = 9;
// MaskedPaths masks over the provided paths inside the container.
repeated string MaskedPaths = 10;
// ReadonlyPaths sets the provided paths as RO inside the container.
repeated string ReadonlyPaths = 11;
// MountLabel specifies the selinux context for the mounts in the container.
string MountLabel = 12;
// IntelRdt contains Intel Resource Director Technology (RDT) information
// for handling resource constraints (e.g., L3 cache) for the container
LinuxIntelRdt IntelRdt = 13;
}
message Windows {
// Dummy string, never used.
string dummy = 1;
}
message Solaris {
// Dummy string, never used.
string dummy = 1;
}
message LinuxIDMapping {
// HostID is the starting UID/GID on the host to be mapped to 'ContainerID'
uint32 HostID = 1;
// ContainerID is the starting UID/GID in the container
uint32 ContainerID = 2;
// Size is the number of IDs to be mapped
uint32 Size = 3;
}
message LinuxNamespace {
// Type is the type of namespace
string Type = 1;
// Path is a path to an existing namespace persisted on disk that can be joined
// and is of the same type
string Path = 2;
}
message LinuxDevice {
// Path to the device.
string Path = 1;
// Device type, block, char, etc.
string Type = 2;
// Major is the device's major number.
int64 Major = 3;
// Minor is the device's minor number.
int64 Minor = 4;
// FileMode permission bits for the device.
uint32 FileMode = 5;
// UID of the device.
uint32 UID = 6;
// Gid of the device.
uint32 GID = 7;
}
message LinuxResources {
// Devices configures the device whitelist.
repeated LinuxDeviceCgroup Devices = 1 [(gogoproto.nullable) = false];
// Memory restriction configuration
LinuxMemory Memory = 2;
// CPU resource restriction configuration
LinuxCPU CPU = 3;
// Task resource restriction configuration.
LinuxPids Pids = 4;
// BlockIO restriction configuration
LinuxBlockIO BlockIO = 5;
// Hugetlb limit (in bytes)
repeated LinuxHugepageLimit HugepageLimits = 6 [(gogoproto.nullable) = false];
// Network restriction configuration
LinuxNetwork Network = 7;
}
message LinuxMemory {
// Memory limit (in bytes).
int64 Limit = 1;
// Memory reservation or soft_limit (in bytes).
int64 Reservation = 2;
// Total memory limit (memory + swap).
int64 Swap = 3;
// Kernel memory limit (in bytes).
int64 Kernel = 4;
// Kernel memory limit for tcp (in bytes)
int64 KernelTCP = 5;
// How aggressive the kernel will swap memory pages.
uint64 Swappiness = 6;
// DisableOOMKiller disables the OOM killer for out of memory conditions
bool DisableOOMKiller = 7;
}
message LinuxCPU {
// CPU shares (relative weight (ratio) vs. other cgroups with cpu shares).
uint64 Shares = 1;
// CPU hardcap limit (in usecs). Allowed cpu time in a given period.
int64 Quota = 2;
// CPU period to be used for hardcapping (in usecs).
uint64 Period = 3;
// How much time realtime scheduling may use (in usecs).
int64 RealtimeRuntime = 4;
// CPU period to be used for realtime scheduling (in usecs).
uint64 RealtimePeriod = 5;
// CPUs to use within the cpuset. Default is to use any CPU available.
string Cpus = 6;
// List of memory nodes in the cpuset. Default is to use any available memory node.
string Mems = 7;
}
message LinuxWeightDevice {
// Major is the device's major number.
int64 Major = 1;
// Minor is the device's minor number.
int64 Minor = 2;
// Weight is the bandwidth rate for the device.
uint32 Weight = 3;
// LeafWeight is the bandwidth rate for the device while competing with the cgroup's child cgroups, CFQ scheduler only
uint32 LeafWeight = 4;
}
message LinuxThrottleDevice {
// Major is the device's major number.
int64 Major = 1;
// Minor is the device's minor number.
int64 Minor = 2;
// Rate is the IO rate limit per cgroup per device
uint64 Rate = 3;
}
message LinuxBlockIO {
// Specifies per cgroup weight
uint32 Weight = 1;
// Specifies tasks' weight in the given cgroup while competing with the cgroup's child cgroups, CFQ scheduler only
uint32 LeafWeight = 2;
// Weight per cgroup per device, can override BlkioWeight
repeated LinuxWeightDevice WeightDevice = 3 [(gogoproto.nullable) = false];
// IO read rate limit per cgroup per device, bytes per second
repeated LinuxThrottleDevice ThrottleReadBpsDevice = 4 [(gogoproto.nullable) = false];
// IO write rate limit per cgroup per device, bytes per second
repeated LinuxThrottleDevice ThrottleWriteBpsDevice = 5 [(gogoproto.nullable) = false];
// IO read rate limit per cgroup per device, IO per second
repeated LinuxThrottleDevice ThrottleReadIOPSDevice = 6 [(gogoproto.nullable) = false];
// IO write rate limit per cgroup per device, IO per second
repeated LinuxThrottleDevice ThrottleWriteIOPSDevice = 7 [(gogoproto.nullable) = false];
}
message LinuxPids {
// Maximum number of PIDs. Default is "no limit".
int64 Limit = 1;
}
message LinuxDeviceCgroup {
// Allow or deny
bool Allow = 1;
// Device type, block, char, etc.
string Type = 2;
// Major is the device's major number.
int64 Major = 3;
// Minor is the device's minor number.
int64 Minor = 4;
// Cgroup access permissions format, rwm.
string Access = 5;
}
message LinuxNetwork {
// Set class identifier for container's network packets
uint32 ClassID = 1;
// Set priority of network traffic for container
repeated LinuxInterfacePriority Priorities = 2 [(gogoproto.nullable) = false];
}
message LinuxHugepageLimit {
// Pagesize is the hugepage size
string Pagesize = 1;
// Limit is the limit of "hugepagesize" hugetlb usage
uint64 Limit = 2;
}
message LinuxInterfacePriority {
// Name is the name of the network interface
string Name = 1;
// Priority for the interface
uint32 Priority = 2;
}
message LinuxSeccomp {
string DefaultAction = 1;
repeated string Architectures = 2;
repeated LinuxSyscall Syscalls = 3 [(gogoproto.nullable) = false];
}
message LinuxSeccompArg {
uint64 Index = 1;
uint64 Value = 2;
uint64 ValueTwo = 3;
string Op = 4;
}
message LinuxSyscall {
repeated string Names = 1;
string Action = 2;
repeated LinuxSeccompArg Args = 3 [(gogoproto.nullable) = false];
}
message LinuxIntelRdt {
// The schema for L3 cache id and capacity bitmask (CBM)
// Format: "L3:<cache_id0>=<cbm0>;<cache_id1>=<cbm1>;..."
string L3CacheSchema = 1;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,883 @@
// This file is generated. Do not edit
// @generated
// https://github.com/Manishearth/rust-clippy/issues/702
#![allow(unknown_lints)]
#![allow(clippy)]
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(box_pointers)]
#![allow(dead_code)]
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(trivial_casts)]
#![allow(unsafe_code)]
#![allow(unused_imports)]
#![allow(unused_results)]
const METHOD_AGENT_SERVICE_CREATE_CONTAINER: ::grpcio::Method<super::agent::CreateContainerRequest, super::empty::Empty> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/CreateContainer",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_START_CONTAINER: ::grpcio::Method<super::agent::StartContainerRequest, super::empty::Empty> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/StartContainer",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_REMOVE_CONTAINER: ::grpcio::Method<super::agent::RemoveContainerRequest, super::empty::Empty> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/RemoveContainer",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_EXEC_PROCESS: ::grpcio::Method<super::agent::ExecProcessRequest, super::empty::Empty> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/ExecProcess",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_SIGNAL_PROCESS: ::grpcio::Method<super::agent::SignalProcessRequest, super::empty::Empty> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/SignalProcess",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_WAIT_PROCESS: ::grpcio::Method<super::agent::WaitProcessRequest, super::agent::WaitProcessResponse> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/WaitProcess",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_LIST_PROCESSES: ::grpcio::Method<super::agent::ListProcessesRequest, super::agent::ListProcessesResponse> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/ListProcesses",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_UPDATE_CONTAINER: ::grpcio::Method<super::agent::UpdateContainerRequest, super::empty::Empty> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/UpdateContainer",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_STATS_CONTAINER: ::grpcio::Method<super::agent::StatsContainerRequest, super::agent::StatsContainerResponse> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/StatsContainer",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_PAUSE_CONTAINER: ::grpcio::Method<super::agent::PauseContainerRequest, super::empty::Empty> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/PauseContainer",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_RESUME_CONTAINER: ::grpcio::Method<super::agent::ResumeContainerRequest, super::empty::Empty> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/ResumeContainer",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_WRITE_STDIN: ::grpcio::Method<super::agent::WriteStreamRequest, super::agent::WriteStreamResponse> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/WriteStdin",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_READ_STDOUT: ::grpcio::Method<super::agent::ReadStreamRequest, super::agent::ReadStreamResponse> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/ReadStdout",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_READ_STDERR: ::grpcio::Method<super::agent::ReadStreamRequest, super::agent::ReadStreamResponse> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/ReadStderr",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_CLOSE_STDIN: ::grpcio::Method<super::agent::CloseStdinRequest, super::empty::Empty> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/CloseStdin",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_TTY_WIN_RESIZE: ::grpcio::Method<super::agent::TtyWinResizeRequest, super::empty::Empty> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/TtyWinResize",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_UPDATE_INTERFACE: ::grpcio::Method<super::agent::UpdateInterfaceRequest, super::types::Interface> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/UpdateInterface",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_UPDATE_ROUTES: ::grpcio::Method<super::agent::UpdateRoutesRequest, super::agent::Routes> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/UpdateRoutes",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_LIST_INTERFACES: ::grpcio::Method<super::agent::ListInterfacesRequest, super::agent::Interfaces> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/ListInterfaces",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_LIST_ROUTES: ::grpcio::Method<super::agent::ListRoutesRequest, super::agent::Routes> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/ListRoutes",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_START_TRACING: ::grpcio::Method<super::agent::StartTracingRequest, super::empty::Empty> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/StartTracing",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_STOP_TRACING: ::grpcio::Method<super::agent::StopTracingRequest, super::empty::Empty> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/StopTracing",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_CREATE_SANDBOX: ::grpcio::Method<super::agent::CreateSandboxRequest, super::empty::Empty> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/CreateSandbox",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_DESTROY_SANDBOX: ::grpcio::Method<super::agent::DestroySandboxRequest, super::empty::Empty> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/DestroySandbox",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_ONLINE_CPU_MEM: ::grpcio::Method<super::agent::OnlineCPUMemRequest, super::empty::Empty> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/OnlineCPUMem",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_RESEED_RANDOM_DEV: ::grpcio::Method<super::agent::ReseedRandomDevRequest, super::empty::Empty> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/ReseedRandomDev",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_GET_GUEST_DETAILS: ::grpcio::Method<super::agent::GuestDetailsRequest, super::agent::GuestDetailsResponse> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/GetGuestDetails",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_MEM_HOTPLUG_BY_PROBE: ::grpcio::Method<super::agent::MemHotplugByProbeRequest, super::empty::Empty> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/MemHotplugByProbe",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_SET_GUEST_DATE_TIME: ::grpcio::Method<super::agent::SetGuestDateTimeRequest, super::empty::Empty> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/SetGuestDateTime",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_AGENT_SERVICE_COPY_FILE: ::grpcio::Method<super::agent::CopyFileRequest, super::empty::Empty> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.AgentService/CopyFile",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
#[derive(Clone)]
pub struct AgentServiceClient {
client: ::grpcio::Client,
}
impl AgentServiceClient {
pub fn new(channel: ::grpcio::Channel) -> Self {
AgentServiceClient {
client: ::grpcio::Client::new(channel),
}
}
pub fn create_container_opt(&self, req: &super::agent::CreateContainerRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::empty::Empty> {
self.client.unary_call(&METHOD_AGENT_SERVICE_CREATE_CONTAINER, req, opt)
}
pub fn create_container(&self, req: &super::agent::CreateContainerRequest) -> ::grpcio::Result<super::empty::Empty> {
self.create_container_opt(req, ::grpcio::CallOption::default())
}
pub fn create_container_async_opt(&self, req: &super::agent::CreateContainerRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_CREATE_CONTAINER, req, opt)
}
pub fn create_container_async(&self, req: &super::agent::CreateContainerRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.create_container_async_opt(req, ::grpcio::CallOption::default())
}
pub fn start_container_opt(&self, req: &super::agent::StartContainerRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::empty::Empty> {
self.client.unary_call(&METHOD_AGENT_SERVICE_START_CONTAINER, req, opt)
}
pub fn start_container(&self, req: &super::agent::StartContainerRequest) -> ::grpcio::Result<super::empty::Empty> {
self.start_container_opt(req, ::grpcio::CallOption::default())
}
pub fn start_container_async_opt(&self, req: &super::agent::StartContainerRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_START_CONTAINER, req, opt)
}
pub fn start_container_async(&self, req: &super::agent::StartContainerRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.start_container_async_opt(req, ::grpcio::CallOption::default())
}
pub fn remove_container_opt(&self, req: &super::agent::RemoveContainerRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::empty::Empty> {
self.client.unary_call(&METHOD_AGENT_SERVICE_REMOVE_CONTAINER, req, opt)
}
pub fn remove_container(&self, req: &super::agent::RemoveContainerRequest) -> ::grpcio::Result<super::empty::Empty> {
self.remove_container_opt(req, ::grpcio::CallOption::default())
}
pub fn remove_container_async_opt(&self, req: &super::agent::RemoveContainerRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_REMOVE_CONTAINER, req, opt)
}
pub fn remove_container_async(&self, req: &super::agent::RemoveContainerRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.remove_container_async_opt(req, ::grpcio::CallOption::default())
}
pub fn exec_process_opt(&self, req: &super::agent::ExecProcessRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::empty::Empty> {
self.client.unary_call(&METHOD_AGENT_SERVICE_EXEC_PROCESS, req, opt)
}
pub fn exec_process(&self, req: &super::agent::ExecProcessRequest) -> ::grpcio::Result<super::empty::Empty> {
self.exec_process_opt(req, ::grpcio::CallOption::default())
}
pub fn exec_process_async_opt(&self, req: &super::agent::ExecProcessRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_EXEC_PROCESS, req, opt)
}
pub fn exec_process_async(&self, req: &super::agent::ExecProcessRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.exec_process_async_opt(req, ::grpcio::CallOption::default())
}
pub fn signal_process_opt(&self, req: &super::agent::SignalProcessRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::empty::Empty> {
self.client.unary_call(&METHOD_AGENT_SERVICE_SIGNAL_PROCESS, req, opt)
}
pub fn signal_process(&self, req: &super::agent::SignalProcessRequest) -> ::grpcio::Result<super::empty::Empty> {
self.signal_process_opt(req, ::grpcio::CallOption::default())
}
pub fn signal_process_async_opt(&self, req: &super::agent::SignalProcessRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_SIGNAL_PROCESS, req, opt)
}
pub fn signal_process_async(&self, req: &super::agent::SignalProcessRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.signal_process_async_opt(req, ::grpcio::CallOption::default())
}
pub fn wait_process_opt(&self, req: &super::agent::WaitProcessRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::agent::WaitProcessResponse> {
self.client.unary_call(&METHOD_AGENT_SERVICE_WAIT_PROCESS, req, opt)
}
pub fn wait_process(&self, req: &super::agent::WaitProcessRequest) -> ::grpcio::Result<super::agent::WaitProcessResponse> {
self.wait_process_opt(req, ::grpcio::CallOption::default())
}
pub fn wait_process_async_opt(&self, req: &super::agent::WaitProcessRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::agent::WaitProcessResponse>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_WAIT_PROCESS, req, opt)
}
pub fn wait_process_async(&self, req: &super::agent::WaitProcessRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::agent::WaitProcessResponse>> {
self.wait_process_async_opt(req, ::grpcio::CallOption::default())
}
pub fn list_processes_opt(&self, req: &super::agent::ListProcessesRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::agent::ListProcessesResponse> {
self.client.unary_call(&METHOD_AGENT_SERVICE_LIST_PROCESSES, req, opt)
}
pub fn list_processes(&self, req: &super::agent::ListProcessesRequest) -> ::grpcio::Result<super::agent::ListProcessesResponse> {
self.list_processes_opt(req, ::grpcio::CallOption::default())
}
pub fn list_processes_async_opt(&self, req: &super::agent::ListProcessesRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::agent::ListProcessesResponse>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_LIST_PROCESSES, req, opt)
}
pub fn list_processes_async(&self, req: &super::agent::ListProcessesRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::agent::ListProcessesResponse>> {
self.list_processes_async_opt(req, ::grpcio::CallOption::default())
}
pub fn update_container_opt(&self, req: &super::agent::UpdateContainerRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::empty::Empty> {
self.client.unary_call(&METHOD_AGENT_SERVICE_UPDATE_CONTAINER, req, opt)
}
pub fn update_container(&self, req: &super::agent::UpdateContainerRequest) -> ::grpcio::Result<super::empty::Empty> {
self.update_container_opt(req, ::grpcio::CallOption::default())
}
pub fn update_container_async_opt(&self, req: &super::agent::UpdateContainerRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_UPDATE_CONTAINER, req, opt)
}
pub fn update_container_async(&self, req: &super::agent::UpdateContainerRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.update_container_async_opt(req, ::grpcio::CallOption::default())
}
pub fn stats_container_opt(&self, req: &super::agent::StatsContainerRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::agent::StatsContainerResponse> {
self.client.unary_call(&METHOD_AGENT_SERVICE_STATS_CONTAINER, req, opt)
}
pub fn stats_container(&self, req: &super::agent::StatsContainerRequest) -> ::grpcio::Result<super::agent::StatsContainerResponse> {
self.stats_container_opt(req, ::grpcio::CallOption::default())
}
pub fn stats_container_async_opt(&self, req: &super::agent::StatsContainerRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::agent::StatsContainerResponse>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_STATS_CONTAINER, req, opt)
}
pub fn stats_container_async(&self, req: &super::agent::StatsContainerRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::agent::StatsContainerResponse>> {
self.stats_container_async_opt(req, ::grpcio::CallOption::default())
}
pub fn pause_container_opt(&self, req: &super::agent::PauseContainerRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::empty::Empty> {
self.client.unary_call(&METHOD_AGENT_SERVICE_PAUSE_CONTAINER, req, opt)
}
pub fn pause_container(&self, req: &super::agent::PauseContainerRequest) -> ::grpcio::Result<super::empty::Empty> {
self.pause_container_opt(req, ::grpcio::CallOption::default())
}
pub fn pause_container_async_opt(&self, req: &super::agent::PauseContainerRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_PAUSE_CONTAINER, req, opt)
}
pub fn pause_container_async(&self, req: &super::agent::PauseContainerRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.pause_container_async_opt(req, ::grpcio::CallOption::default())
}
pub fn resume_container_opt(&self, req: &super::agent::ResumeContainerRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::empty::Empty> {
self.client.unary_call(&METHOD_AGENT_SERVICE_RESUME_CONTAINER, req, opt)
}
pub fn resume_container(&self, req: &super::agent::ResumeContainerRequest) -> ::grpcio::Result<super::empty::Empty> {
self.resume_container_opt(req, ::grpcio::CallOption::default())
}
pub fn resume_container_async_opt(&self, req: &super::agent::ResumeContainerRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_RESUME_CONTAINER, req, opt)
}
pub fn resume_container_async(&self, req: &super::agent::ResumeContainerRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.resume_container_async_opt(req, ::grpcio::CallOption::default())
}
pub fn write_stdin_opt(&self, req: &super::agent::WriteStreamRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::agent::WriteStreamResponse> {
self.client.unary_call(&METHOD_AGENT_SERVICE_WRITE_STDIN, req, opt)
}
pub fn write_stdin(&self, req: &super::agent::WriteStreamRequest) -> ::grpcio::Result<super::agent::WriteStreamResponse> {
self.write_stdin_opt(req, ::grpcio::CallOption::default())
}
pub fn write_stdin_async_opt(&self, req: &super::agent::WriteStreamRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::agent::WriteStreamResponse>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_WRITE_STDIN, req, opt)
}
pub fn write_stdin_async(&self, req: &super::agent::WriteStreamRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::agent::WriteStreamResponse>> {
self.write_stdin_async_opt(req, ::grpcio::CallOption::default())
}
pub fn read_stdout_opt(&self, req: &super::agent::ReadStreamRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::agent::ReadStreamResponse> {
self.client.unary_call(&METHOD_AGENT_SERVICE_READ_STDOUT, req, opt)
}
pub fn read_stdout(&self, req: &super::agent::ReadStreamRequest) -> ::grpcio::Result<super::agent::ReadStreamResponse> {
self.read_stdout_opt(req, ::grpcio::CallOption::default())
}
pub fn read_stdout_async_opt(&self, req: &super::agent::ReadStreamRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::agent::ReadStreamResponse>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_READ_STDOUT, req, opt)
}
pub fn read_stdout_async(&self, req: &super::agent::ReadStreamRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::agent::ReadStreamResponse>> {
self.read_stdout_async_opt(req, ::grpcio::CallOption::default())
}
pub fn read_stderr_opt(&self, req: &super::agent::ReadStreamRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::agent::ReadStreamResponse> {
self.client.unary_call(&METHOD_AGENT_SERVICE_READ_STDERR, req, opt)
}
pub fn read_stderr(&self, req: &super::agent::ReadStreamRequest) -> ::grpcio::Result<super::agent::ReadStreamResponse> {
self.read_stderr_opt(req, ::grpcio::CallOption::default())
}
pub fn read_stderr_async_opt(&self, req: &super::agent::ReadStreamRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::agent::ReadStreamResponse>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_READ_STDERR, req, opt)
}
pub fn read_stderr_async(&self, req: &super::agent::ReadStreamRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::agent::ReadStreamResponse>> {
self.read_stderr_async_opt(req, ::grpcio::CallOption::default())
}
pub fn close_stdin_opt(&self, req: &super::agent::CloseStdinRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::empty::Empty> {
self.client.unary_call(&METHOD_AGENT_SERVICE_CLOSE_STDIN, req, opt)
}
pub fn close_stdin(&self, req: &super::agent::CloseStdinRequest) -> ::grpcio::Result<super::empty::Empty> {
self.close_stdin_opt(req, ::grpcio::CallOption::default())
}
pub fn close_stdin_async_opt(&self, req: &super::agent::CloseStdinRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_CLOSE_STDIN, req, opt)
}
pub fn close_stdin_async(&self, req: &super::agent::CloseStdinRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.close_stdin_async_opt(req, ::grpcio::CallOption::default())
}
pub fn tty_win_resize_opt(&self, req: &super::agent::TtyWinResizeRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::empty::Empty> {
self.client.unary_call(&METHOD_AGENT_SERVICE_TTY_WIN_RESIZE, req, opt)
}
pub fn tty_win_resize(&self, req: &super::agent::TtyWinResizeRequest) -> ::grpcio::Result<super::empty::Empty> {
self.tty_win_resize_opt(req, ::grpcio::CallOption::default())
}
pub fn tty_win_resize_async_opt(&self, req: &super::agent::TtyWinResizeRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_TTY_WIN_RESIZE, req, opt)
}
pub fn tty_win_resize_async(&self, req: &super::agent::TtyWinResizeRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.tty_win_resize_async_opt(req, ::grpcio::CallOption::default())
}
pub fn update_interface_opt(&self, req: &super::agent::UpdateInterfaceRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::types::Interface> {
self.client.unary_call(&METHOD_AGENT_SERVICE_UPDATE_INTERFACE, req, opt)
}
pub fn update_interface(&self, req: &super::agent::UpdateInterfaceRequest) -> ::grpcio::Result<super::types::Interface> {
self.update_interface_opt(req, ::grpcio::CallOption::default())
}
pub fn update_interface_async_opt(&self, req: &super::agent::UpdateInterfaceRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::types::Interface>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_UPDATE_INTERFACE, req, opt)
}
pub fn update_interface_async(&self, req: &super::agent::UpdateInterfaceRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::types::Interface>> {
self.update_interface_async_opt(req, ::grpcio::CallOption::default())
}
pub fn update_routes_opt(&self, req: &super::agent::UpdateRoutesRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::agent::Routes> {
self.client.unary_call(&METHOD_AGENT_SERVICE_UPDATE_ROUTES, req, opt)
}
pub fn update_routes(&self, req: &super::agent::UpdateRoutesRequest) -> ::grpcio::Result<super::agent::Routes> {
self.update_routes_opt(req, ::grpcio::CallOption::default())
}
pub fn update_routes_async_opt(&self, req: &super::agent::UpdateRoutesRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::agent::Routes>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_UPDATE_ROUTES, req, opt)
}
pub fn update_routes_async(&self, req: &super::agent::UpdateRoutesRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::agent::Routes>> {
self.update_routes_async_opt(req, ::grpcio::CallOption::default())
}
pub fn list_interfaces_opt(&self, req: &super::agent::ListInterfacesRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::agent::Interfaces> {
self.client.unary_call(&METHOD_AGENT_SERVICE_LIST_INTERFACES, req, opt)
}
pub fn list_interfaces(&self, req: &super::agent::ListInterfacesRequest) -> ::grpcio::Result<super::agent::Interfaces> {
self.list_interfaces_opt(req, ::grpcio::CallOption::default())
}
pub fn list_interfaces_async_opt(&self, req: &super::agent::ListInterfacesRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::agent::Interfaces>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_LIST_INTERFACES, req, opt)
}
pub fn list_interfaces_async(&self, req: &super::agent::ListInterfacesRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::agent::Interfaces>> {
self.list_interfaces_async_opt(req, ::grpcio::CallOption::default())
}
pub fn list_routes_opt(&self, req: &super::agent::ListRoutesRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::agent::Routes> {
self.client.unary_call(&METHOD_AGENT_SERVICE_LIST_ROUTES, req, opt)
}
pub fn list_routes(&self, req: &super::agent::ListRoutesRequest) -> ::grpcio::Result<super::agent::Routes> {
self.list_routes_opt(req, ::grpcio::CallOption::default())
}
pub fn list_routes_async_opt(&self, req: &super::agent::ListRoutesRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::agent::Routes>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_LIST_ROUTES, req, opt)
}
pub fn list_routes_async(&self, req: &super::agent::ListRoutesRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::agent::Routes>> {
self.list_routes_async_opt(req, ::grpcio::CallOption::default())
}
pub fn start_tracing_opt(&self, req: &super::agent::StartTracingRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::empty::Empty> {
self.client.unary_call(&METHOD_AGENT_SERVICE_START_TRACING, req, opt)
}
pub fn start_tracing(&self, req: &super::agent::StartTracingRequest) -> ::grpcio::Result<super::empty::Empty> {
self.start_tracing_opt(req, ::grpcio::CallOption::default())
}
pub fn start_tracing_async_opt(&self, req: &super::agent::StartTracingRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_START_TRACING, req, opt)
}
pub fn start_tracing_async(&self, req: &super::agent::StartTracingRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.start_tracing_async_opt(req, ::grpcio::CallOption::default())
}
pub fn stop_tracing_opt(&self, req: &super::agent::StopTracingRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::empty::Empty> {
self.client.unary_call(&METHOD_AGENT_SERVICE_STOP_TRACING, req, opt)
}
pub fn stop_tracing(&self, req: &super::agent::StopTracingRequest) -> ::grpcio::Result<super::empty::Empty> {
self.stop_tracing_opt(req, ::grpcio::CallOption::default())
}
pub fn stop_tracing_async_opt(&self, req: &super::agent::StopTracingRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_STOP_TRACING, req, opt)
}
pub fn stop_tracing_async(&self, req: &super::agent::StopTracingRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.stop_tracing_async_opt(req, ::grpcio::CallOption::default())
}
pub fn create_sandbox_opt(&self, req: &super::agent::CreateSandboxRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::empty::Empty> {
self.client.unary_call(&METHOD_AGENT_SERVICE_CREATE_SANDBOX, req, opt)
}
pub fn create_sandbox(&self, req: &super::agent::CreateSandboxRequest) -> ::grpcio::Result<super::empty::Empty> {
self.create_sandbox_opt(req, ::grpcio::CallOption::default())
}
pub fn create_sandbox_async_opt(&self, req: &super::agent::CreateSandboxRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_CREATE_SANDBOX, req, opt)
}
pub fn create_sandbox_async(&self, req: &super::agent::CreateSandboxRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.create_sandbox_async_opt(req, ::grpcio::CallOption::default())
}
pub fn destroy_sandbox_opt(&self, req: &super::agent::DestroySandboxRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::empty::Empty> {
self.client.unary_call(&METHOD_AGENT_SERVICE_DESTROY_SANDBOX, req, opt)
}
pub fn destroy_sandbox(&self, req: &super::agent::DestroySandboxRequest) -> ::grpcio::Result<super::empty::Empty> {
self.destroy_sandbox_opt(req, ::grpcio::CallOption::default())
}
pub fn destroy_sandbox_async_opt(&self, req: &super::agent::DestroySandboxRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_DESTROY_SANDBOX, req, opt)
}
pub fn destroy_sandbox_async(&self, req: &super::agent::DestroySandboxRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.destroy_sandbox_async_opt(req, ::grpcio::CallOption::default())
}
pub fn online_cpu_mem_opt(&self, req: &super::agent::OnlineCPUMemRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::empty::Empty> {
self.client.unary_call(&METHOD_AGENT_SERVICE_ONLINE_CPU_MEM, req, opt)
}
pub fn online_cpu_mem(&self, req: &super::agent::OnlineCPUMemRequest) -> ::grpcio::Result<super::empty::Empty> {
self.online_cpu_mem_opt(req, ::grpcio::CallOption::default())
}
pub fn online_cpu_mem_async_opt(&self, req: &super::agent::OnlineCPUMemRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_ONLINE_CPU_MEM, req, opt)
}
pub fn online_cpu_mem_async(&self, req: &super::agent::OnlineCPUMemRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.online_cpu_mem_async_opt(req, ::grpcio::CallOption::default())
}
pub fn reseed_random_dev_opt(&self, req: &super::agent::ReseedRandomDevRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::empty::Empty> {
self.client.unary_call(&METHOD_AGENT_SERVICE_RESEED_RANDOM_DEV, req, opt)
}
pub fn reseed_random_dev(&self, req: &super::agent::ReseedRandomDevRequest) -> ::grpcio::Result<super::empty::Empty> {
self.reseed_random_dev_opt(req, ::grpcio::CallOption::default())
}
pub fn reseed_random_dev_async_opt(&self, req: &super::agent::ReseedRandomDevRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_RESEED_RANDOM_DEV, req, opt)
}
pub fn reseed_random_dev_async(&self, req: &super::agent::ReseedRandomDevRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.reseed_random_dev_async_opt(req, ::grpcio::CallOption::default())
}
pub fn get_guest_details_opt(&self, req: &super::agent::GuestDetailsRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::agent::GuestDetailsResponse> {
self.client.unary_call(&METHOD_AGENT_SERVICE_GET_GUEST_DETAILS, req, opt)
}
pub fn get_guest_details(&self, req: &super::agent::GuestDetailsRequest) -> ::grpcio::Result<super::agent::GuestDetailsResponse> {
self.get_guest_details_opt(req, ::grpcio::CallOption::default())
}
pub fn get_guest_details_async_opt(&self, req: &super::agent::GuestDetailsRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::agent::GuestDetailsResponse>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_GET_GUEST_DETAILS, req, opt)
}
pub fn get_guest_details_async(&self, req: &super::agent::GuestDetailsRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::agent::GuestDetailsResponse>> {
self.get_guest_details_async_opt(req, ::grpcio::CallOption::default())
}
pub fn mem_hotplug_by_probe_opt(&self, req: &super::agent::MemHotplugByProbeRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::empty::Empty> {
self.client.unary_call(&METHOD_AGENT_SERVICE_MEM_HOTPLUG_BY_PROBE, req, opt)
}
pub fn mem_hotplug_by_probe(&self, req: &super::agent::MemHotplugByProbeRequest) -> ::grpcio::Result<super::empty::Empty> {
self.mem_hotplug_by_probe_opt(req, ::grpcio::CallOption::default())
}
pub fn mem_hotplug_by_probe_async_opt(&self, req: &super::agent::MemHotplugByProbeRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_MEM_HOTPLUG_BY_PROBE, req, opt)
}
pub fn mem_hotplug_by_probe_async(&self, req: &super::agent::MemHotplugByProbeRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.mem_hotplug_by_probe_async_opt(req, ::grpcio::CallOption::default())
}
pub fn set_guest_date_time_opt(&self, req: &super::agent::SetGuestDateTimeRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::empty::Empty> {
self.client.unary_call(&METHOD_AGENT_SERVICE_SET_GUEST_DATE_TIME, req, opt)
}
pub fn set_guest_date_time(&self, req: &super::agent::SetGuestDateTimeRequest) -> ::grpcio::Result<super::empty::Empty> {
self.set_guest_date_time_opt(req, ::grpcio::CallOption::default())
}
pub fn set_guest_date_time_async_opt(&self, req: &super::agent::SetGuestDateTimeRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_SET_GUEST_DATE_TIME, req, opt)
}
pub fn set_guest_date_time_async(&self, req: &super::agent::SetGuestDateTimeRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.set_guest_date_time_async_opt(req, ::grpcio::CallOption::default())
}
pub fn copy_file_opt(&self, req: &super::agent::CopyFileRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::empty::Empty> {
self.client.unary_call(&METHOD_AGENT_SERVICE_COPY_FILE, req, opt)
}
pub fn copy_file(&self, req: &super::agent::CopyFileRequest) -> ::grpcio::Result<super::empty::Empty> {
self.copy_file_opt(req, ::grpcio::CallOption::default())
}
pub fn copy_file_async_opt(&self, req: &super::agent::CopyFileRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.client.unary_call_async(&METHOD_AGENT_SERVICE_COPY_FILE, req, opt)
}
pub fn copy_file_async(&self, req: &super::agent::CopyFileRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::empty::Empty>> {
self.copy_file_async_opt(req, ::grpcio::CallOption::default())
}
pub fn spawn<F>(&self, f: F) where F: ::futures::Future<Item = (), Error = ()> + Send + 'static {
self.client.spawn(f)
}
}
pub trait AgentService {
fn create_container(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::CreateContainerRequest, sink: ::grpcio::UnarySink<super::empty::Empty>);
fn start_container(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::StartContainerRequest, sink: ::grpcio::UnarySink<super::empty::Empty>);
fn remove_container(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::RemoveContainerRequest, sink: ::grpcio::UnarySink<super::empty::Empty>);
fn exec_process(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::ExecProcessRequest, sink: ::grpcio::UnarySink<super::empty::Empty>);
fn signal_process(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::SignalProcessRequest, sink: ::grpcio::UnarySink<super::empty::Empty>);
fn wait_process(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::WaitProcessRequest, sink: ::grpcio::UnarySink<super::agent::WaitProcessResponse>);
fn list_processes(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::ListProcessesRequest, sink: ::grpcio::UnarySink<super::agent::ListProcessesResponse>);
fn update_container(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::UpdateContainerRequest, sink: ::grpcio::UnarySink<super::empty::Empty>);
fn stats_container(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::StatsContainerRequest, sink: ::grpcio::UnarySink<super::agent::StatsContainerResponse>);
fn pause_container(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::PauseContainerRequest, sink: ::grpcio::UnarySink<super::empty::Empty>);
fn resume_container(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::ResumeContainerRequest, sink: ::grpcio::UnarySink<super::empty::Empty>);
fn write_stdin(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::WriteStreamRequest, sink: ::grpcio::UnarySink<super::agent::WriteStreamResponse>);
fn read_stdout(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::ReadStreamRequest, sink: ::grpcio::UnarySink<super::agent::ReadStreamResponse>);
fn read_stderr(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::ReadStreamRequest, sink: ::grpcio::UnarySink<super::agent::ReadStreamResponse>);
fn close_stdin(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::CloseStdinRequest, sink: ::grpcio::UnarySink<super::empty::Empty>);
fn tty_win_resize(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::TtyWinResizeRequest, sink: ::grpcio::UnarySink<super::empty::Empty>);
fn update_interface(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::UpdateInterfaceRequest, sink: ::grpcio::UnarySink<super::types::Interface>);
fn update_routes(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::UpdateRoutesRequest, sink: ::grpcio::UnarySink<super::agent::Routes>);
fn list_interfaces(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::ListInterfacesRequest, sink: ::grpcio::UnarySink<super::agent::Interfaces>);
fn list_routes(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::ListRoutesRequest, sink: ::grpcio::UnarySink<super::agent::Routes>);
fn start_tracing(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::StartTracingRequest, sink: ::grpcio::UnarySink<super::empty::Empty>);
fn stop_tracing(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::StopTracingRequest, sink: ::grpcio::UnarySink<super::empty::Empty>);
fn create_sandbox(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::CreateSandboxRequest, sink: ::grpcio::UnarySink<super::empty::Empty>);
fn destroy_sandbox(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::DestroySandboxRequest, sink: ::grpcio::UnarySink<super::empty::Empty>);
fn online_cpu_mem(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::OnlineCPUMemRequest, sink: ::grpcio::UnarySink<super::empty::Empty>);
fn reseed_random_dev(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::ReseedRandomDevRequest, sink: ::grpcio::UnarySink<super::empty::Empty>);
fn get_guest_details(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::GuestDetailsRequest, sink: ::grpcio::UnarySink<super::agent::GuestDetailsResponse>);
fn mem_hotplug_by_probe(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::MemHotplugByProbeRequest, sink: ::grpcio::UnarySink<super::empty::Empty>);
fn set_guest_date_time(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::SetGuestDateTimeRequest, sink: ::grpcio::UnarySink<super::empty::Empty>);
fn copy_file(&mut self, ctx: ::grpcio::RpcContext, req: super::agent::CopyFileRequest, sink: ::grpcio::UnarySink<super::empty::Empty>);
}
pub fn create_agent_service<S: AgentService + Send + Clone + 'static>(s: S) -> ::grpcio::Service {
let mut builder = ::grpcio::ServiceBuilder::new();
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_CREATE_CONTAINER, move |ctx, req, resp| {
instance.create_container(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_START_CONTAINER, move |ctx, req, resp| {
instance.start_container(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_REMOVE_CONTAINER, move |ctx, req, resp| {
instance.remove_container(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_EXEC_PROCESS, move |ctx, req, resp| {
instance.exec_process(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_SIGNAL_PROCESS, move |ctx, req, resp| {
instance.signal_process(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_WAIT_PROCESS, move |ctx, req, resp| {
instance.wait_process(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_LIST_PROCESSES, move |ctx, req, resp| {
instance.list_processes(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_UPDATE_CONTAINER, move |ctx, req, resp| {
instance.update_container(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_STATS_CONTAINER, move |ctx, req, resp| {
instance.stats_container(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_PAUSE_CONTAINER, move |ctx, req, resp| {
instance.pause_container(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_RESUME_CONTAINER, move |ctx, req, resp| {
instance.resume_container(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_WRITE_STDIN, move |ctx, req, resp| {
instance.write_stdin(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_READ_STDOUT, move |ctx, req, resp| {
instance.read_stdout(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_READ_STDERR, move |ctx, req, resp| {
instance.read_stderr(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_CLOSE_STDIN, move |ctx, req, resp| {
instance.close_stdin(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_TTY_WIN_RESIZE, move |ctx, req, resp| {
instance.tty_win_resize(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_UPDATE_INTERFACE, move |ctx, req, resp| {
instance.update_interface(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_UPDATE_ROUTES, move |ctx, req, resp| {
instance.update_routes(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_LIST_INTERFACES, move |ctx, req, resp| {
instance.list_interfaces(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_LIST_ROUTES, move |ctx, req, resp| {
instance.list_routes(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_START_TRACING, move |ctx, req, resp| {
instance.start_tracing(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_STOP_TRACING, move |ctx, req, resp| {
instance.stop_tracing(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_CREATE_SANDBOX, move |ctx, req, resp| {
instance.create_sandbox(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_DESTROY_SANDBOX, move |ctx, req, resp| {
instance.destroy_sandbox(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_ONLINE_CPU_MEM, move |ctx, req, resp| {
instance.online_cpu_mem(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_RESEED_RANDOM_DEV, move |ctx, req, resp| {
instance.reseed_random_dev(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_GET_GUEST_DETAILS, move |ctx, req, resp| {
instance.get_guest_details(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_MEM_HOTPLUG_BY_PROBE, move |ctx, req, resp| {
instance.mem_hotplug_by_probe(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_SET_GUEST_DATE_TIME, move |ctx, req, resp| {
instance.set_guest_date_time(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_AGENT_SERVICE_COPY_FILE, move |ctx, req, resp| {
instance.copy_file(ctx, req, resp)
});
builder.build()
}

View File

@@ -0,0 +1,221 @@
// This file is generated by rust-protobuf 2.6.2. Do not edit
// @generated
// https://github.com/Manishearth/rust-clippy/issues/702
#![allow(unknown_lints)]
#![allow(clippy)]
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(box_pointers)]
#![allow(dead_code)]
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(trivial_casts)]
#![allow(unsafe_code)]
#![allow(unused_imports)]
#![allow(unused_results)]
use protobuf::Message as Message_imported_for_functions;
use protobuf::ProtobufEnum as ProtobufEnum_imported_for_functions;
#[derive(PartialEq,Clone,Default)]
pub struct Empty {
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a Empty {
fn default() -> &'a Empty {
<Empty as ::protobuf::Message>::default_instance()
}
}
impl Empty {
pub fn new() -> Empty {
::std::default::Default::default()
}
}
impl ::protobuf::Message for Empty {
fn is_initialized(&self) -> bool {
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream) -> ::protobuf::ProtobufResult<()> {
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &::std::any::Any {
self as &::std::any::Any
}
fn as_any_mut(&mut self) -> &mut ::std::any::Any {
self as &mut ::std::any::Any
}
fn into_any(self: Box<Self>) -> ::std::boxed::Box<::std::any::Any> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> Empty {
Empty::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy {
lock: ::protobuf::lazy::ONCE_INIT,
ptr: 0 as *const ::protobuf::reflect::MessageDescriptor,
};
unsafe {
descriptor.get(|| {
let fields = ::std::vec::Vec::new();
::protobuf::reflect::MessageDescriptor::new::<Empty>(
"Empty",
fields,
file_descriptor_proto()
)
})
}
}
fn default_instance() -> &'static Empty {
static mut instance: ::protobuf::lazy::Lazy<Empty> = ::protobuf::lazy::Lazy {
lock: ::protobuf::lazy::ONCE_INIT,
ptr: 0 as *const Empty,
};
unsafe {
instance.get(Empty::new)
}
}
}
impl ::protobuf::Clear for Empty {
fn clear(&mut self) {
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for Empty {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for Empty {
fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef {
::protobuf::reflect::ProtobufValueRef::Message(self)
}
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\x1bgoogle/protobuf/empty.proto\x12\x0fgoogle.protobuf\"\x07\n\x05Empt\
yBT\n\x13com.google.protobufB\nEmptyProtoP\x01Z\x05types\xf8\x01\x01\xa2\
\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesJ\xfe\x10\n\x06\x12\
\x04\x1e\03\x10\n\xcc\x0c\n\x01\x0c\x12\x03\x1e\0\x122\xc1\x0c\x20Protoc\
ol\x20Buffers\x20-\x20Google's\x20data\x20interchange\x20format\n\x20Cop\
yright\x202008\x20Google\x20Inc.\x20\x20All\x20rights\x20reserved.\n\x20\
https://developers.google.com/protocol-buffers/\n\n\x20Redistribution\
\x20and\x20use\x20in\x20source\x20and\x20binary\x20forms,\x20with\x20or\
\x20without\n\x20modification,\x20are\x20permitted\x20provided\x20that\
\x20the\x20following\x20conditions\x20are\n\x20met:\n\n\x20\x20\x20\x20\
\x20*\x20Redistributions\x20of\x20source\x20code\x20must\x20retain\x20th\
e\x20above\x20copyright\n\x20notice,\x20this\x20list\x20of\x20conditions\
\x20and\x20the\x20following\x20disclaimer.\n\x20\x20\x20\x20\x20*\x20Red\
istributions\x20in\x20binary\x20form\x20must\x20reproduce\x20the\x20abov\
e\n\x20copyright\x20notice,\x20this\x20list\x20of\x20conditions\x20and\
\x20the\x20following\x20disclaimer\n\x20in\x20the\x20documentation\x20an\
d/or\x20other\x20materials\x20provided\x20with\x20the\n\x20distribution.\
\n\x20\x20\x20\x20\x20*\x20Neither\x20the\x20name\x20of\x20Google\x20Inc\
.\x20nor\x20the\x20names\x20of\x20its\n\x20contributors\x20may\x20be\x20\
used\x20to\x20endorse\x20or\x20promote\x20products\x20derived\x20from\n\
\x20this\x20software\x20without\x20specific\x20prior\x20written\x20permi\
ssion.\n\n\x20THIS\x20SOFTWARE\x20IS\x20PROVIDED\x20BY\x20THE\x20COPYRIG\
HT\x20HOLDERS\x20AND\x20CONTRIBUTORS\n\x20\"AS\x20IS\"\x20AND\x20ANY\x20\
EXPRESS\x20OR\x20IMPLIED\x20WARRANTIES,\x20INCLUDING,\x20BUT\x20NOT\n\
\x20LIMITED\x20TO,\x20THE\x20IMPLIED\x20WARRANTIES\x20OF\x20MERCHANTABIL\
ITY\x20AND\x20FITNESS\x20FOR\n\x20A\x20PARTICULAR\x20PURPOSE\x20ARE\x20D\
ISCLAIMED.\x20IN\x20NO\x20EVENT\x20SHALL\x20THE\x20COPYRIGHT\n\x20OWNER\
\x20OR\x20CONTRIBUTORS\x20BE\x20LIABLE\x20FOR\x20ANY\x20DIRECT,\x20INDIR\
ECT,\x20INCIDENTAL,\n\x20SPECIAL,\x20EXEMPLARY,\x20OR\x20CONSEQUENTIAL\
\x20DAMAGES\x20(INCLUDING,\x20BUT\x20NOT\n\x20LIMITED\x20TO,\x20PROCUREM\
ENT\x20OF\x20SUBSTITUTE\x20GOODS\x20OR\x20SERVICES;\x20LOSS\x20OF\x20USE\
,\n\x20DATA,\x20OR\x20PROFITS;\x20OR\x20BUSINESS\x20INTERRUPTION)\x20HOW\
EVER\x20CAUSED\x20AND\x20ON\x20ANY\n\x20THEORY\x20OF\x20LIABILITY,\x20WH\
ETHER\x20IN\x20CONTRACT,\x20STRICT\x20LIABILITY,\x20OR\x20TORT\n\x20(INC\
LUDING\x20NEGLIGENCE\x20OR\x20OTHERWISE)\x20ARISING\x20IN\x20ANY\x20WAY\
\x20OUT\x20OF\x20THE\x20USE\n\x20OF\x20THIS\x20SOFTWARE,\x20EVEN\x20IF\
\x20ADVISED\x20OF\x20THE\x20POSSIBILITY\x20OF\x20SUCH\x20DAMAGE.\n\n\x08\
\n\x01\x02\x12\x03\x20\0\x18\n\x08\n\x01\x08\x12\x03\"\0;\n\t\n\x02\x08%\
\x12\x03\"\0;\n\x08\n\x01\x08\x12\x03#\0\x1c\n\t\n\x02\x08\x0b\x12\x03#\
\0\x1c\n\x08\n\x01\x08\x12\x03$\0,\n\t\n\x02\x08\x01\x12\x03$\0,\n\x08\n\
\x01\x08\x12\x03%\0+\n\t\n\x02\x08\x08\x12\x03%\0+\n\x08\n\x01\x08\x12\
\x03&\0\"\n\t\n\x02\x08\n\x12\x03&\0\"\n\x08\n\x01\x08\x12\x03'\0!\n\t\n\
\x02\x08$\x12\x03'\0!\n\x08\n\x01\x08\x12\x03(\0\x1f\n\t\n\x02\x08\x1f\
\x12\x03(\0\x1f\n\xfb\x02\n\x02\x04\0\x12\x033\0\x10\x1a\xef\x02\x20A\
\x20generic\x20empty\x20message\x20that\x20you\x20can\x20re-use\x20to\
\x20avoid\x20defining\x20duplicated\n\x20empty\x20messages\x20in\x20your\
\x20APIs.\x20A\x20typical\x20example\x20is\x20to\x20use\x20it\x20as\x20t\
he\x20request\n\x20or\x20the\x20response\x20type\x20of\x20an\x20API\x20m\
ethod.\x20For\x20instance:\n\n\x20\x20\x20\x20\x20service\x20Foo\x20{\n\
\x20\x20\x20\x20\x20\x20\x20rpc\x20Bar(google.protobuf.Empty)\x20returns\
\x20(google.protobuf.Empty);\n\x20\x20\x20\x20\x20}\n\n\x20The\x20JSON\
\x20representation\x20for\x20`Empty`\x20is\x20empty\x20JSON\x20object\
\x20`{}`.\n\n\n\n\x03\x04\0\x01\x12\x033\x08\rb\x06proto3\
";
static mut file_descriptor_proto_lazy: ::protobuf::lazy::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::lazy::Lazy {
lock: ::protobuf::lazy::ONCE_INIT,
ptr: 0 as *const ::protobuf::descriptor::FileDescriptorProto,
};
fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
::protobuf::parse_from_bytes(file_descriptor_proto_data).unwrap()
}
pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
unsafe {
file_descriptor_proto_lazy.get(|| {
parse_descriptor_proto()
})
}
}

View File

@@ -0,0 +1,688 @@
// This file is generated by rust-protobuf 2.6.2. Do not edit
// @generated
// https://github.com/Manishearth/rust-clippy/issues/702
#![allow(unknown_lints)]
#![allow(clippy)]
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(box_pointers)]
#![allow(dead_code)]
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(trivial_casts)]
#![allow(unsafe_code)]
#![allow(unused_imports)]
#![allow(unused_results)]
use protobuf::Message as Message_imported_for_functions;
use protobuf::ProtobufEnum as ProtobufEnum_imported_for_functions;
#[derive(PartialEq,Clone,Default)]
pub struct CheckRequest {
// message fields
pub service: ::std::string::String,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a CheckRequest {
fn default() -> &'a CheckRequest {
<CheckRequest as ::protobuf::Message>::default_instance()
}
}
impl CheckRequest {
pub fn new() -> CheckRequest {
::std::default::Default::default()
}
// string service = 1;
pub fn get_service(&self) -> &str {
&self.service
}
pub fn clear_service(&mut self) {
self.service.clear();
}
// Param is passed by value, moved
pub fn set_service(&mut self, v: ::std::string::String) {
self.service = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_service(&mut self) -> &mut ::std::string::String {
&mut self.service
}
// Take field
pub fn take_service(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.service, ::std::string::String::new())
}
}
impl ::protobuf::Message for CheckRequest {
fn is_initialized(&self) -> bool {
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.service)?;
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
if !self.service.is_empty() {
my_size += ::protobuf::rt::string_size(1, &self.service);
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream) -> ::protobuf::ProtobufResult<()> {
if !self.service.is_empty() {
os.write_string(1, &self.service)?;
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &::std::any::Any {
self as &::std::any::Any
}
fn as_any_mut(&mut self) -> &mut ::std::any::Any {
self as &mut ::std::any::Any
}
fn into_any(self: Box<Self>) -> ::std::boxed::Box<::std::any::Any> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> CheckRequest {
CheckRequest::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy {
lock: ::protobuf::lazy::ONCE_INIT,
ptr: 0 as *const ::protobuf::reflect::MessageDescriptor,
};
unsafe {
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"service",
|m: &CheckRequest| { &m.service },
|m: &mut CheckRequest| { &mut m.service },
));
::protobuf::reflect::MessageDescriptor::new::<CheckRequest>(
"CheckRequest",
fields,
file_descriptor_proto()
)
})
}
}
fn default_instance() -> &'static CheckRequest {
static mut instance: ::protobuf::lazy::Lazy<CheckRequest> = ::protobuf::lazy::Lazy {
lock: ::protobuf::lazy::ONCE_INIT,
ptr: 0 as *const CheckRequest,
};
unsafe {
instance.get(CheckRequest::new)
}
}
}
impl ::protobuf::Clear for CheckRequest {
fn clear(&mut self) {
self.service.clear();
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for CheckRequest {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for CheckRequest {
fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef {
::protobuf::reflect::ProtobufValueRef::Message(self)
}
}
#[derive(PartialEq,Clone,Default)]
pub struct HealthCheckResponse {
// message fields
pub status: HealthCheckResponse_ServingStatus,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a HealthCheckResponse {
fn default() -> &'a HealthCheckResponse {
<HealthCheckResponse as ::protobuf::Message>::default_instance()
}
}
impl HealthCheckResponse {
pub fn new() -> HealthCheckResponse {
::std::default::Default::default()
}
// .grpc.HealthCheckResponse.ServingStatus status = 1;
pub fn get_status(&self) -> HealthCheckResponse_ServingStatus {
self.status
}
pub fn clear_status(&mut self) {
self.status = HealthCheckResponse_ServingStatus::UNKNOWN;
}
// Param is passed by value, moved
pub fn set_status(&mut self, v: HealthCheckResponse_ServingStatus) {
self.status = v;
}
}
impl ::protobuf::Message for HealthCheckResponse {
fn is_initialized(&self) -> bool {
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.status, 1, &mut self.unknown_fields)?
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
if self.status != HealthCheckResponse_ServingStatus::UNKNOWN {
my_size += ::protobuf::rt::enum_size(1, self.status);
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream) -> ::protobuf::ProtobufResult<()> {
if self.status != HealthCheckResponse_ServingStatus::UNKNOWN {
os.write_enum(1, self.status.value())?;
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &::std::any::Any {
self as &::std::any::Any
}
fn as_any_mut(&mut self) -> &mut ::std::any::Any {
self as &mut ::std::any::Any
}
fn into_any(self: Box<Self>) -> ::std::boxed::Box<::std::any::Any> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> HealthCheckResponse {
HealthCheckResponse::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy {
lock: ::protobuf::lazy::ONCE_INIT,
ptr: 0 as *const ::protobuf::reflect::MessageDescriptor,
};
unsafe {
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<HealthCheckResponse_ServingStatus>>(
"status",
|m: &HealthCheckResponse| { &m.status },
|m: &mut HealthCheckResponse| { &mut m.status },
));
::protobuf::reflect::MessageDescriptor::new::<HealthCheckResponse>(
"HealthCheckResponse",
fields,
file_descriptor_proto()
)
})
}
}
fn default_instance() -> &'static HealthCheckResponse {
static mut instance: ::protobuf::lazy::Lazy<HealthCheckResponse> = ::protobuf::lazy::Lazy {
lock: ::protobuf::lazy::ONCE_INIT,
ptr: 0 as *const HealthCheckResponse,
};
unsafe {
instance.get(HealthCheckResponse::new)
}
}
}
impl ::protobuf::Clear for HealthCheckResponse {
fn clear(&mut self) {
self.status = HealthCheckResponse_ServingStatus::UNKNOWN;
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for HealthCheckResponse {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for HealthCheckResponse {
fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef {
::protobuf::reflect::ProtobufValueRef::Message(self)
}
}
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub enum HealthCheckResponse_ServingStatus {
UNKNOWN = 0,
SERVING = 1,
NOT_SERVING = 2,
}
impl ::protobuf::ProtobufEnum for HealthCheckResponse_ServingStatus {
fn value(&self) -> i32 {
*self as i32
}
fn from_i32(value: i32) -> ::std::option::Option<HealthCheckResponse_ServingStatus> {
match value {
0 => ::std::option::Option::Some(HealthCheckResponse_ServingStatus::UNKNOWN),
1 => ::std::option::Option::Some(HealthCheckResponse_ServingStatus::SERVING),
2 => ::std::option::Option::Some(HealthCheckResponse_ServingStatus::NOT_SERVING),
_ => ::std::option::Option::None
}
}
fn values() -> &'static [Self] {
static values: &'static [HealthCheckResponse_ServingStatus] = &[
HealthCheckResponse_ServingStatus::UNKNOWN,
HealthCheckResponse_ServingStatus::SERVING,
HealthCheckResponse_ServingStatus::NOT_SERVING,
];
values
}
fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::EnumDescriptor> = ::protobuf::lazy::Lazy {
lock: ::protobuf::lazy::ONCE_INIT,
ptr: 0 as *const ::protobuf::reflect::EnumDescriptor,
};
unsafe {
descriptor.get(|| {
::protobuf::reflect::EnumDescriptor::new("HealthCheckResponse_ServingStatus", file_descriptor_proto())
})
}
}
}
impl ::std::marker::Copy for HealthCheckResponse_ServingStatus {
}
impl ::std::default::Default for HealthCheckResponse_ServingStatus {
fn default() -> Self {
HealthCheckResponse_ServingStatus::UNKNOWN
}
}
impl ::protobuf::reflect::ProtobufValue for HealthCheckResponse_ServingStatus {
fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef {
::protobuf::reflect::ProtobufValueRef::Enum(self.descriptor())
}
}
#[derive(PartialEq,Clone,Default)]
pub struct VersionCheckResponse {
// message fields
pub grpc_version: ::std::string::String,
pub agent_version: ::std::string::String,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a VersionCheckResponse {
fn default() -> &'a VersionCheckResponse {
<VersionCheckResponse as ::protobuf::Message>::default_instance()
}
}
impl VersionCheckResponse {
pub fn new() -> VersionCheckResponse {
::std::default::Default::default()
}
// string grpc_version = 1;
pub fn get_grpc_version(&self) -> &str {
&self.grpc_version
}
pub fn clear_grpc_version(&mut self) {
self.grpc_version.clear();
}
// Param is passed by value, moved
pub fn set_grpc_version(&mut self, v: ::std::string::String) {
self.grpc_version = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_grpc_version(&mut self) -> &mut ::std::string::String {
&mut self.grpc_version
}
// Take field
pub fn take_grpc_version(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.grpc_version, ::std::string::String::new())
}
// string agent_version = 2;
pub fn get_agent_version(&self) -> &str {
&self.agent_version
}
pub fn clear_agent_version(&mut self) {
self.agent_version.clear();
}
// Param is passed by value, moved
pub fn set_agent_version(&mut self, v: ::std::string::String) {
self.agent_version = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_agent_version(&mut self) -> &mut ::std::string::String {
&mut self.agent_version
}
// Take field
pub fn take_agent_version(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.agent_version, ::std::string::String::new())
}
}
impl ::protobuf::Message for VersionCheckResponse {
fn is_initialized(&self) -> bool {
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.grpc_version)?;
},
2 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.agent_version)?;
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
if !self.grpc_version.is_empty() {
my_size += ::protobuf::rt::string_size(1, &self.grpc_version);
}
if !self.agent_version.is_empty() {
my_size += ::protobuf::rt::string_size(2, &self.agent_version);
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream) -> ::protobuf::ProtobufResult<()> {
if !self.grpc_version.is_empty() {
os.write_string(1, &self.grpc_version)?;
}
if !self.agent_version.is_empty() {
os.write_string(2, &self.agent_version)?;
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &::std::any::Any {
self as &::std::any::Any
}
fn as_any_mut(&mut self) -> &mut ::std::any::Any {
self as &mut ::std::any::Any
}
fn into_any(self: Box<Self>) -> ::std::boxed::Box<::std::any::Any> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> VersionCheckResponse {
VersionCheckResponse::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy {
lock: ::protobuf::lazy::ONCE_INIT,
ptr: 0 as *const ::protobuf::reflect::MessageDescriptor,
};
unsafe {
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"grpc_version",
|m: &VersionCheckResponse| { &m.grpc_version },
|m: &mut VersionCheckResponse| { &mut m.grpc_version },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"agent_version",
|m: &VersionCheckResponse| { &m.agent_version },
|m: &mut VersionCheckResponse| { &mut m.agent_version },
));
::protobuf::reflect::MessageDescriptor::new::<VersionCheckResponse>(
"VersionCheckResponse",
fields,
file_descriptor_proto()
)
})
}
}
fn default_instance() -> &'static VersionCheckResponse {
static mut instance: ::protobuf::lazy::Lazy<VersionCheckResponse> = ::protobuf::lazy::Lazy {
lock: ::protobuf::lazy::ONCE_INIT,
ptr: 0 as *const VersionCheckResponse,
};
unsafe {
instance.get(VersionCheckResponse::new)
}
}
}
impl ::protobuf::Clear for VersionCheckResponse {
fn clear(&mut self) {
self.grpc_version.clear();
self.agent_version.clear();
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for VersionCheckResponse {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for VersionCheckResponse {
fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef {
::protobuf::reflect::ProtobufValueRef::Message(self)
}
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\x0chealth.proto\x12\x04grpc\x1a-github.com/gogo/protobuf/gogoproto/go\
go.proto\"(\n\x0cCheckRequest\x12\x18\n\x07service\x18\x01\x20\x01(\tR\
\x07service\"\x92\x01\n\x13HealthCheckResponse\x12?\n\x06status\x18\x01\
\x20\x01(\x0e2'.grpc.HealthCheckResponse.ServingStatusR\x06status\":\n\r\
ServingStatus\x12\x0b\n\x07UNKNOWN\x10\0\x12\x0b\n\x07SERVING\x10\x01\
\x12\x0f\n\x0bNOT_SERVING\x10\x02\"^\n\x14VersionCheckResponse\x12!\n\
\x0cgrpc_version\x18\x01\x20\x01(\tR\x0bgrpcVersion\x12#\n\ragent_versio\
n\x18\x02\x20\x01(\tR\x0cagentVersion2{\n\x06Health\x126\n\x05Check\x12\
\x12.grpc.CheckRequest\x1a\x19.grpc.HealthCheckResponse\x129\n\x07Versio\
n\x12\x12.grpc.CheckRequest\x1a\x1a.grpc.VersionCheckResponseB\x10\xb8\
\xe2\x1e\x01\xf8\xe1\x1e\x01\xa8\xe2\x1e\x01\xc0\xe2\x1e\x01J\xd9\x06\n\
\x06\x12\x04\x06\0&\x01\nO\n\x01\x0c\x12\x03\x06\0\x122E\n\x20Copyright\
\x202017\x20HyperHQ\x20Inc.\n\n\x20SPDX-License-Identifier:\x20Apache-2.\
0\n\n\n\x08\n\x01\x02\x12\x03\x08\0\r\n\t\n\x02\x03\0\x12\x03\n\07\n\x08\
\n\x01\x08\x12\x03\x0c\0$\n\x0b\n\x04\x08\xa5\xec\x03\x12\x03\x0c\0$\n\
\x08\n\x01\x08\x12\x03\r\0'\n\x0b\n\x04\x08\x9f\xec\x03\x12\x03\r\0'\n\
\x08\n\x01\x08\x12\x03\x0e\0&\n\x0b\n\x04\x08\xa7\xec\x03\x12\x03\x0e\0&\
\n\x08\n\x01\x08\x12\x03\x0f\0'\n\x0b\n\x04\x08\xa8\xec\x03\x12\x03\x0f\
\0'\n\n\n\x02\x04\0\x12\x04\x11\0\x13\x01\n\n\n\x03\x04\0\x01\x12\x03\
\x11\x08\x14\n\x0b\n\x04\x04\0\x02\0\x12\x03\x12\x08\x1b\n\r\n\x05\x04\0\
\x02\0\x04\x12\x04\x12\x08\x11\x16\n\x0c\n\x05\x04\0\x02\0\x05\x12\x03\
\x12\x08\x0e\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x12\x0f\x16\n\x0c\n\x05\
\x04\0\x02\0\x03\x12\x03\x12\x19\x1a\n\n\n\x02\x04\x01\x12\x04\x15\0\x1c\
\x01\n\n\n\x03\x04\x01\x01\x12\x03\x15\x08\x1b\n\x0c\n\x04\x04\x01\x04\0\
\x12\x04\x16\x08\x1a\t\n\x0c\n\x05\x04\x01\x04\0\x01\x12\x03\x16\r\x1a\n\
\r\n\x06\x04\x01\x04\0\x02\0\x12\x03\x17\x10\x1c\n\x0e\n\x07\x04\x01\x04\
\0\x02\0\x01\x12\x03\x17\x10\x17\n\x0e\n\x07\x04\x01\x04\0\x02\0\x02\x12\
\x03\x17\x1a\x1b\n\r\n\x06\x04\x01\x04\0\x02\x01\x12\x03\x18\x10\x1c\n\
\x0e\n\x07\x04\x01\x04\0\x02\x01\x01\x12\x03\x18\x10\x17\n\x0e\n\x07\x04\
\x01\x04\0\x02\x01\x02\x12\x03\x18\x1a\x1b\n\r\n\x06\x04\x01\x04\0\x02\
\x02\x12\x03\x19\x10\x20\n\x0e\n\x07\x04\x01\x04\0\x02\x02\x01\x12\x03\
\x19\x10\x1b\n\x0e\n\x07\x04\x01\x04\0\x02\x02\x02\x12\x03\x19\x1e\x1f\n\
\x0b\n\x04\x04\x01\x02\0\x12\x03\x1b\x08!\n\r\n\x05\x04\x01\x02\0\x04\
\x12\x04\x1b\x08\x1a\t\n\x0c\n\x05\x04\x01\x02\0\x06\x12\x03\x1b\x08\x15\
\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x1b\x16\x1c\n\x0c\n\x05\x04\x01\
\x02\0\x03\x12\x03\x1b\x1f\x20\n\n\n\x02\x04\x02\x12\x04\x1e\0!\x01\n\n\
\n\x03\x04\x02\x01\x12\x03\x1e\x08\x1c\n\x0b\n\x04\x04\x02\x02\0\x12\x03\
\x1f\x08\x20\n\r\n\x05\x04\x02\x02\0\x04\x12\x04\x1f\x08\x1e\x1e\n\x0c\n\
\x05\x04\x02\x02\0\x05\x12\x03\x1f\x08\x0e\n\x0c\n\x05\x04\x02\x02\0\x01\
\x12\x03\x1f\x0f\x1b\n\x0c\n\x05\x04\x02\x02\0\x03\x12\x03\x1f\x1e\x1f\n\
\x0b\n\x04\x04\x02\x02\x01\x12\x03\x20\x08!\n\r\n\x05\x04\x02\x02\x01\
\x04\x12\x04\x20\x08\x1f\x20\n\x0c\n\x05\x04\x02\x02\x01\x05\x12\x03\x20\
\x08\x0e\n\x0c\n\x05\x04\x02\x02\x01\x01\x12\x03\x20\x0f\x1c\n\x0c\n\x05\
\x04\x02\x02\x01\x03\x12\x03\x20\x1f\x20\n\n\n\x02\x06\0\x12\x04#\0&\x01\
\n\n\n\x03\x06\0\x01\x12\x03#\x08\x0e\n\x0b\n\x04\x06\0\x02\0\x12\x03$\
\x08>\n\x0c\n\x05\x06\0\x02\0\x01\x12\x03$\x0c\x11\n\x0c\n\x05\x06\0\x02\
\0\x02\x12\x03$\x12\x1e\n\x0c\n\x05\x06\0\x02\0\x03\x12\x03$)<\n\x0b\n\
\x04\x06\0\x02\x01\x12\x03%\x08A\n\x0c\n\x05\x06\0\x02\x01\x01\x12\x03%\
\x0c\x13\n\x0c\n\x05\x06\0\x02\x01\x02\x12\x03%\x14\x20\n\x0c\n\x05\x06\
\0\x02\x01\x03\x12\x03%+?b\x06proto3\
";
static mut file_descriptor_proto_lazy: ::protobuf::lazy::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::lazy::Lazy {
lock: ::protobuf::lazy::ONCE_INIT,
ptr: 0 as *const ::protobuf::descriptor::FileDescriptorProto,
};
fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
::protobuf::parse_from_bytes(file_descriptor_proto_data).unwrap()
}
pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
unsafe {
file_descriptor_proto_lazy.get(|| {
parse_descriptor_proto()
})
}
}

View File

@@ -0,0 +1,99 @@
// This file is generated. Do not edit
// @generated
// https://github.com/Manishearth/rust-clippy/issues/702
#![allow(unknown_lints)]
#![allow(clippy)]
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(box_pointers)]
#![allow(dead_code)]
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(trivial_casts)]
#![allow(unsafe_code)]
#![allow(unused_imports)]
#![allow(unused_results)]
const METHOD_HEALTH_CHECK: ::grpcio::Method<super::health::CheckRequest, super::health::HealthCheckResponse> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.Health/Check",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
const METHOD_HEALTH_VERSION: ::grpcio::Method<super::health::CheckRequest, super::health::VersionCheckResponse> = ::grpcio::Method {
ty: ::grpcio::MethodType::Unary,
name: "/grpc.Health/Version",
req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de },
};
#[derive(Clone)]
pub struct HealthClient {
client: ::grpcio::Client,
}
impl HealthClient {
pub fn new(channel: ::grpcio::Channel) -> Self {
HealthClient {
client: ::grpcio::Client::new(channel),
}
}
pub fn check_opt(&self, req: &super::health::CheckRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::health::HealthCheckResponse> {
self.client.unary_call(&METHOD_HEALTH_CHECK, req, opt)
}
pub fn check(&self, req: &super::health::CheckRequest) -> ::grpcio::Result<super::health::HealthCheckResponse> {
self.check_opt(req, ::grpcio::CallOption::default())
}
pub fn check_async_opt(&self, req: &super::health::CheckRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::health::HealthCheckResponse>> {
self.client.unary_call_async(&METHOD_HEALTH_CHECK, req, opt)
}
pub fn check_async(&self, req: &super::health::CheckRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::health::HealthCheckResponse>> {
self.check_async_opt(req, ::grpcio::CallOption::default())
}
pub fn version_opt(&self, req: &super::health::CheckRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<super::health::VersionCheckResponse> {
self.client.unary_call(&METHOD_HEALTH_VERSION, req, opt)
}
pub fn version(&self, req: &super::health::CheckRequest) -> ::grpcio::Result<super::health::VersionCheckResponse> {
self.version_opt(req, ::grpcio::CallOption::default())
}
pub fn version_async_opt(&self, req: &super::health::CheckRequest, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::health::VersionCheckResponse>> {
self.client.unary_call_async(&METHOD_HEALTH_VERSION, req, opt)
}
pub fn version_async(&self, req: &super::health::CheckRequest) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver<super::health::VersionCheckResponse>> {
self.version_async_opt(req, ::grpcio::CallOption::default())
}
pub fn spawn<F>(&self, f: F) where F: ::futures::Future<Item = (), Error = ()> + Send + 'static {
self.client.spawn(f)
}
}
pub trait Health {
fn check(&mut self, ctx: ::grpcio::RpcContext, req: super::health::CheckRequest, sink: ::grpcio::UnarySink<super::health::HealthCheckResponse>);
fn version(&mut self, ctx: ::grpcio::RpcContext, req: super::health::CheckRequest, sink: ::grpcio::UnarySink<super::health::VersionCheckResponse>);
}
pub fn create_health<S: Health + Send + Clone + 'static>(s: S) -> ::grpcio::Service {
let mut builder = ::grpcio::ServiceBuilder::new();
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_HEALTH_CHECK, move |ctx, req, resp| {
instance.check(ctx, req, resp)
});
let mut instance = s.clone();
builder = builder.add_unary_handler(&METHOD_HEALTH_VERSION, move |ctx, req, resp| {
instance.version(ctx, req, resp)
});
builder.build()
}

View File

@@ -0,0 +1,17 @@
#![allow(bare_trait_objects)]
pub mod agent;
pub mod agent_grpc;
pub mod health;
pub mod health_grpc;
pub mod oci;
pub mod types;
pub mod empty;
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}

10473
src/agent/protocols/src/oci.rs Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
[package]
name = "rustjail"
version = "0.1.0"
authors = ["Yang Bo <bo@hyper.sh>"]
edition = "2018"
[dependencies]
error-chain = "0.12.1"
serde = "1.0.91"
serde_json = "1.0.39"
serde_derive = "1.0.91"
oci = { path = "../oci" }
protocols = { path ="../protocols" }
caps = "0.3.0"
nix = "0.17.0"
scopeguard = "1.0.0"
prctl = "1.0.0"
lazy_static = "1.3.0"
libc = "0.2.58"
protobuf = "2.6.1"
slog = "2.5.2"
slog-scope = "4.1.2"
scan_fmt = "0.2"
regex = "1.1"
path-absolutize = { git = "git://github.com/magiclen/path-absolutize.git", tag= "v1.2.0" }

View File

@@ -0,0 +1,135 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
// looks like we can use caps to manipulate capabilities
// conveniently, use caps to do it directly.. maybe
use lazy_static;
use crate::errors::*;
use crate::log_child;
use crate::sync::write_count;
use caps::{self, CapSet, Capability, CapsHashSet};
use oci::LinuxCapabilities;
use std::collections::HashMap;
use std::os::unix::io::RawFd;
lazy_static! {
pub static ref CAPSMAP: HashMap<String, Capability> = {
let mut m = HashMap::new();
m.insert("CAP_CHOWN".to_string(), Capability::CAP_CHOWN);
m.insert("CAP_DAC_OVERRIDE".to_string(), Capability::CAP_DAC_OVERRIDE);
m.insert(
"CAP_DAC_READ_SEARCH".to_string(),
Capability::CAP_DAC_READ_SEARCH,
);
m.insert("CAP_FOWNER".to_string(), Capability::CAP_FOWNER);
m.insert("CAP_FSETID".to_string(), Capability::CAP_FSETID);
m.insert("CAP_KILL".to_string(), Capability::CAP_KILL);
m.insert("CAP_SETGID".to_string(), Capability::CAP_SETGID);
m.insert("CAP_SETUID".to_string(), Capability::CAP_SETUID);
m.insert("CAP_SETPCAP".to_string(), Capability::CAP_SETPCAP);
m.insert(
"CAP_LINUX_IMMUTABLE".to_string(),
Capability::CAP_LINUX_IMMUTABLE,
);
m.insert(
"CAP_NET_BIND_SERVICE".to_string(),
Capability::CAP_NET_BIND_SERVICE,
);
m.insert(
"CAP_NET_BROADCAST".to_string(),
Capability::CAP_NET_BROADCAST,
);
m.insert("CAP_NET_ADMIN".to_string(), Capability::CAP_NET_ADMIN);
m.insert("CAP_NET_RAW".to_string(), Capability::CAP_NET_RAW);
m.insert("CAP_IPC_LOCK".to_string(), Capability::CAP_IPC_LOCK);
m.insert("CAP_IPC_OWNER".to_string(), Capability::CAP_IPC_OWNER);
m.insert("CAP_SYS_MODULE".to_string(), Capability::CAP_SYS_MODULE);
m.insert("CAP_SYS_RAWIO".to_string(), Capability::CAP_SYS_RAWIO);
m.insert("CAP_SYS_CHROOT".to_string(), Capability::CAP_SYS_CHROOT);
m.insert("CAP_SYS_PTRACE".to_string(), Capability::CAP_SYS_PTRACE);
m.insert("CAP_SYS_PACCT".to_string(), Capability::CAP_SYS_PACCT);
m.insert("CAP_SYS_ADMIN".to_string(), Capability::CAP_SYS_ADMIN);
m.insert("CAP_SYS_BOOT".to_string(), Capability::CAP_SYS_BOOT);
m.insert("CAP_SYS_NICE".to_string(), Capability::CAP_SYS_NICE);
m.insert("CAP_SYS_RESOURCE".to_string(), Capability::CAP_SYS_RESOURCE);
m.insert("CAP_SYS_TIME".to_string(), Capability::CAP_SYS_TIME);
m.insert(
"CAP_SYS_TTY_CONFIG".to_string(),
Capability::CAP_SYS_TTY_CONFIG,
);
m.insert("CAP_MKNOD".to_string(), Capability::CAP_MKNOD);
m.insert("CAP_LEASE".to_string(), Capability::CAP_LEASE);
m.insert("CAP_AUDIT_WRITE".to_string(), Capability::CAP_AUDIT_WRITE);
m.insert("CAP_AUDIT_CONTROL".to_string(), Capability::CAP_AUDIT_WRITE);
m.insert("CAP_SETFCAP".to_string(), Capability::CAP_SETFCAP);
m.insert("CAP_MAC_OVERRIDE".to_string(), Capability::CAP_MAC_OVERRIDE);
m.insert("CAP_SYSLOG".to_string(), Capability::CAP_SYSLOG);
m.insert("CAP_WAKE_ALARM".to_string(), Capability::CAP_WAKE_ALARM);
m.insert(
"CAP_BLOCK_SUSPEND".to_string(),
Capability::CAP_BLOCK_SUSPEND,
);
m.insert("CAP_AUDIT_READ".to_string(), Capability::CAP_AUDIT_READ);
m
};
}
fn to_capshashset(cfd_log: RawFd, caps: &[String]) -> CapsHashSet {
let mut r = CapsHashSet::new();
for cap in caps.iter() {
let c = CAPSMAP.get(cap);
if c.is_none() {
log_child!(cfd_log, "{} is not a cap", cap);
continue;
}
r.insert(*c.unwrap());
}
r
}
pub fn reset_effective() -> Result<()> {
caps::set(None, CapSet::Effective, caps::all())?;
Ok(())
}
pub fn drop_priviledges(cfd_log: RawFd, caps: &LinuxCapabilities) -> Result<()> {
let all = caps::all();
for c in all.difference(&to_capshashset(cfd_log, caps.bounding.as_ref())) {
caps::drop(None, CapSet::Bounding, *c)?;
}
caps::set(
None,
CapSet::Effective,
to_capshashset(cfd_log, caps.effective.as_ref()),
)?;
caps::set(
None,
CapSet::Permitted,
to_capshashset(cfd_log, caps.permitted.as_ref()),
)?;
caps::set(
None,
CapSet::Inheritable,
to_capshashset(cfd_log, caps.inheritable.as_ref()),
)?;
if let Err(_) = caps::set(
None,
CapSet::Ambient,
to_capshashset(cfd_log, caps.ambient.as_ref()),
) {
log_child!(cfd_log, "failed to set ambient capability");
}
Ok(())
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,49 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
use crate::errors::*;
// use crate::configs::{FreezerState, Config};
use oci::LinuxResources;
use protocols::agent::CgroupStats;
use std::collections::HashMap;
pub mod fs;
pub mod systemd;
pub type FreezerState = &'static str;
pub trait Manager {
fn apply(&self, _pid: i32) -> Result<()> {
Err(ErrorKind::ErrorCode("not supported!".to_string()).into())
}
fn get_pids(&self) -> Result<Vec<i32>> {
Err(ErrorKind::ErrorCode("not supported!".to_string()).into())
}
fn get_all_pids(&self) -> Result<Vec<i32>> {
Err(ErrorKind::ErrorCode("not supported!".to_string()).into())
}
fn get_stats(&self) -> Result<CgroupStats> {
Err(ErrorKind::ErrorCode("not supported!".to_string()).into())
}
fn freeze(&self, _state: FreezerState) -> Result<()> {
Err(ErrorKind::ErrorCode("not supported!".to_string()).into())
}
fn destroy(&mut self) -> Result<()> {
Err(ErrorKind::ErrorCode("not supported!".to_string()).into())
}
fn get_paths(&self) -> Result<HashMap<String, String>> {
Err(ErrorKind::ErrorCode("not supported!".to_string()).into())
}
fn set(&self, _container: &LinuxResources, _update: bool) -> Result<()> {
Err(ErrorKind::ErrorCode("not supported!".to_string()).into())
}
}

View File

@@ -0,0 +1,10 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
use crate::cgroups::Manager as CgroupManager;
pub struct Manager {}
impl CgroupManager for Manager {}

View File

@@ -0,0 +1,56 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
use libc::*;
use serde;
#[macro_use]
use serde_derive;
use serde_json;
#[derive(Serialize, Deserialize, Debug)]
pub struct Device {
#[serde(default)]
r#type: char,
#[serde(default)]
path: String,
#[serde(default)]
major: i64,
#[serde(default)]
minor: i64,
#[serde(default)]
permissions: String,
#[serde(default)]
file_mode: mode_t,
#[serde(default)]
uid: i32,
#[serde(default)]
gid: i32,
#[serde(default)]
allow: bool,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct BlockIODevice {
#[serde(default)]
major: i64,
#[serde(default)]
minor: i64,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct WeightDevice {
block: BlockIODevice,
#[serde(default)]
weight: u16,
#[serde(default, rename = "leafWeight")]
leaf_weight: u16,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ThrottleDevice {
block: BlockIODevice,
#[serde(default)]
rate: u64,
}

View File

@@ -0,0 +1,494 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
use serde;
#[macro_use]
use serde_derive;
use serde_json;
use protocols::oci::State as OCIState;
use crate::errors::*;
use std::collections::HashMap;
use std::fmt;
use std::path::PathBuf;
use std::time::Duration;
use nix::unistd;
use self::device::{Device, ThrottleDevice, WeightDevice};
use self::namespaces::Namespaces;
use crate::specconv::CreateOpts;
pub mod device;
pub mod namespaces;
pub mod validator;
#[derive(Serialize, Deserialize, Debug)]
pub struct Rlimit {
#[serde(default)]
r#type: i32,
#[serde(default)]
hard: i32,
#[serde(default)]
soft: i32,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct IDMap {
#[serde(default)]
container_id: i32,
#[serde(default)]
host_id: i32,
#[serde(default)]
size: i32,
}
type Action = i32;
#[derive(Serialize, Deserialize, Debug)]
pub struct Seccomp {
#[serde(default)]
default_action: Action,
#[serde(default)]
architectures: Vec<String>,
#[serde(default)]
syscalls: Vec<Syscall>,
}
type Operator = i32;
#[derive(Serialize, Deserialize, Debug)]
pub struct Arg {
#[serde(default)]
index: u32,
#[serde(default)]
value: u64,
#[serde(default)]
value_two: u64,
#[serde(default)]
op: Operator,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Syscall {
#[serde(default, skip_serializing_if = "String::is_empty")]
name: String,
#[serde(default)]
action: Action,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
args: Vec<Arg>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Config<'a> {
#[serde(default)]
no_pivot_root: bool,
#[serde(default)]
parent_death_signal: i32,
#[serde(default)]
rootfs: String,
#[serde(default)]
readonlyfs: bool,
#[serde(default, rename = "rootPropagation")]
root_propagation: i32,
#[serde(default)]
mounts: Vec<Mount>,
#[serde(default)]
devices: Vec<Device>,
#[serde(default)]
mount_label: String,
#[serde(default)]
hostname: String,
#[serde(default)]
namespaces: Namespaces,
#[serde(default)]
capabilities: Option<Capabilities>,
#[serde(default)]
networks: Vec<Network>,
#[serde(default)]
routes: Vec<Route>,
#[serde(default)]
cgroups: Option<Cgroup<'a>>,
#[serde(default, skip_serializing_if = "String::is_empty")]
apparmor_profile: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
process_label: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
rlimits: Vec<Rlimit>,
#[serde(default)]
oom_score_adj: Option<i32>,
#[serde(default)]
uid_mappings: Vec<IDMap>,
#[serde(default)]
gid_mappings: Vec<IDMap>,
#[serde(default)]
mask_paths: Vec<String>,
#[serde(default)]
readonly_paths: Vec<String>,
#[serde(default)]
sysctl: HashMap<String, String>,
#[serde(default)]
seccomp: Option<Seccomp>,
#[serde(default)]
no_new_privileges: bool,
hooks: Option<Hooks>,
#[serde(default)]
version: String,
#[serde(default)]
labels: Vec<String>,
#[serde(default)]
no_new_keyring: bool,
#[serde(default)]
intel_rdt: Option<IntelRdt>,
#[serde(default)]
rootless_euid: bool,
#[serde(default)]
rootless_cgroups: bool,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Hooks {
prestart: Vec<Box<Hook>>,
poststart: Vec<Box<Hook>>,
poststop: Vec<Box<Hook>>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Capabilities {
bounding: Vec<String>,
effective: Vec<String>,
inheritable: Vec<String>,
permitted: Vec<String>,
ambient: Vec<String>,
}
pub trait Hook {
fn run(&self, state: &OCIState) -> Result<()>;
}
pub struct FuncHook {
// run: fn(&OCIState) -> Result<()>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Command {
#[serde(default)]
path: String,
#[serde(default)]
args: Vec<String>,
#[serde(default)]
env: Vec<String>,
#[serde(default)]
dir: String,
#[serde(default)]
timeout: Duration,
}
pub struct CommandHook {
command: Command,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Mount {
#[serde(default)]
source: String,
#[serde(default)]
destination: String,
#[serde(default)]
device: String,
#[serde(default)]
flags: i32,
#[serde(default)]
propagation_flags: Vec<i32>,
#[serde(default)]
data: String,
#[serde(default)]
relabel: String,
#[serde(default)]
extensions: i32,
#[serde(default)]
premount_cmds: Vec<Command>,
#[serde(default)]
postmount_cmds: Vec<Command>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct HugepageLimit {
#[serde(default)]
page_size: String,
#[serde(default)]
limit: u64,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct IntelRdt {
#[serde(default, skip_serializing_if = "String::is_empty")]
l3_cache_schema: String,
#[serde(
default,
rename = "memBwSchema",
skip_serializing_if = "String::is_empty"
)]
mem_bw_schema: String,
}
pub type FreezerState = String;
#[derive(Serialize, Deserialize, Debug)]
pub struct Cgroup<'a> {
#[serde(default, skip_serializing_if = "String::is_empty")]
name: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
parent: String,
#[serde(default)]
path: String,
#[serde(default)]
scope_prefix: String,
paths: HashMap<String, String>,
resource: &'a Resources<'a>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Resources<'a> {
#[serde(default)]
allow_all_devices: bool,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
allowed_devices: Vec<&'a Device>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
denied_devices: Vec<&'a Device>,
#[serde(default)]
devices: Vec<&'a Device>,
#[serde(default)]
memory: i64,
#[serde(default)]
memory_reservation: i64,
#[serde(default)]
memory_swap: i64,
#[serde(default)]
kernel_memory: i64,
#[serde(default)]
kernel_memory_tcp: i64,
#[serde(default)]
cpu_shares: u64,
#[serde(default)]
cpu_quota: i64,
#[serde(default)]
cpu_period: u64,
#[serde(default)]
cpu_rt_quota: i64,
#[serde(default)]
cpu_rt_period: u64,
#[serde(default)]
cpuset_cpus: String,
#[serde(default)]
cpuset_mems: String,
#[serde(default)]
pids_limit: i64,
#[serde(default)]
blkio_weight: u64,
#[serde(default)]
blkio_leaf_weight: u64,
#[serde(default)]
blkio_weight_device: Vec<&'a WeightDevice>,
#[serde(default)]
blkio_throttle_read_bps_device: Vec<&'a ThrottleDevice>,
#[serde(default)]
blkio_throttle_write_bps_device: Vec<&'a ThrottleDevice>,
#[serde(default)]
blkio_throttle_read_iops_device: Vec<&'a ThrottleDevice>,
#[serde(default)]
blkio_throttle_write_iops_device: Vec<&'a ThrottleDevice>,
#[serde(default)]
freezer: FreezerState,
#[serde(default)]
hugetlb_limit: Vec<&'a HugepageLimit>,
#[serde(default)]
oom_kill_disable: bool,
#[serde(default)]
memory_swapiness: u64,
#[serde(default)]
net_prio_ifpriomap: Vec<&'a IfPrioMap>,
#[serde(default)]
net_cls_classid_u: u32,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Network {
#[serde(default)]
r#type: String,
#[serde(default)]
name: String,
#[serde(default)]
bridge: String,
#[serde(default)]
mac_address: String,
#[serde(default)]
address: String,
#[serde(default)]
gateway: String,
#[serde(default)]
ipv6_address: String,
#[serde(default)]
ipv6_gateway: String,
#[serde(default)]
mtu: i32,
#[serde(default)]
txqueuelen: i32,
#[serde(default)]
host_interface_name: String,
#[serde(default)]
hairpin_mode: bool,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Route {
#[serde(default)]
destination: String,
#[serde(default)]
source: String,
#[serde(default)]
gateway: String,
#[serde(default)]
interface_name: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct IfPrioMap {
#[serde(default)]
interface: String,
#[serde(default)]
priority: i32,
}
impl IfPrioMap {
fn cgroup_string(&self) -> String {
format!("{} {}", self.interface, self.priority)
}
}
/*
impl Config {
fn new(opts: &CreateOpts) -> Result<Self> {
if opts.spec.is_none() {
return Err(ErrorKind::ErrorCode("invalid createopts!".into()));
}
let root = unistd::getcwd().chain_err(|| "cannot getwd")?;
let root = root.as_path().canonicalize().chain_err(||
"cannot resolve root into absolute path")?;
let mut root = root.into();
let cwd = root.clone();
let spec = opts.spec.as_ref().unwrap();
if spec.root.is_none() {
return Err(ErrorKind::ErrorCode("no root".into()));
}
let rootfs = PathBuf::from(&spec.root.as_ref().unwrap().path);
if rootfs.is_relative() {
root = format!("{}/{}", root, rootfs.into());
}
// handle annotations
let mut label = spec.annotations
.iter()
.map(|(key, value)| format!("{}={}", key, value)).collect();
label.push(format!("bundle={}", cwd));
let mut config = Config {
rootfs: root,
no_pivot_root: opts.no_pivot_root,
readonlyfs: spec.root.as_ref().unwrap().readonly,
hostname: spec.hostname.clone(),
labels: label,
no_new_keyring: opts.no_new_keyring,
rootless_euid: opts.rootless_euid,
rootless_cgroups: opts.rootless_cgroups,
};
config.mounts = Vec::new();
for m in &spec.mounts {
config.mounts.push(Mount::new(&cwd, &m)?);
}
config.devices = create_devices(&spec)?;
config.cgroups = Cgroups::new(&opts)?;
if spec.linux.as_ref().is_none() {
return Err(ErrorKind::ErrorCode("no linux configuration".into()));
}
let linux = spec.linux.as_ref().unwrap();
let propagation = MOUNTPROPAGATIONMAPPING.get(linux.rootfs_propagation);
if propagation.is_none() {
Err(ErrorKind::ErrorCode("rootfs propagation not support".into()));
}
config.root_propagation = propagation.unwrap();
if config.no_pivot_root && (config.root_propagation & MSFlags::MSPRIVATE != 0) {
return Err(ErrorKind::ErrorCode("[r]private is not safe without pivot root".into()));
}
// handle namespaces
let m: HashMap<String, String> = HashMap::new();
for ns in &linux.namespaces {
if NAMESPACEMAPPING.get(&ns.r#type.as_str()).is_none() {
return Err(ErrorKind::ErrorCode("namespace don't exist".into()));
}
if m.get(&ns.r#type).is_some() {
return Err(ErrorKind::ErrorCode(format!("duplicate ns {}", ns.r#type)));
}
m.insert(ns.r#type, ns.path);
}
if m.contains_key(oci::NETWORKNAMESPACE) {
let path = m.get(oci::NETWORKNAMESPACE).unwrap();
if path == "" {
config.networks = vec![Network {
r#type: "loopback",
}];
}
}
if m.contains_key(oci::USERNAMESPACE) {
setup_user_namespace(&spec, &mut config)?;
}
config.namespaces = m.iter().map(|(key, value)| Namespace {
r#type: key,
path: value,
}).collect();
config.mask_paths = linux.mask_paths;
config.readonly_path = linux.readonly_path;
config.mount_label = linux.mount_label;
config.sysctl = linux.sysctl;
config.seccomp = None;
config.intelrdt = None;
if spec.process.is_some() {
let process = spec.process.as_ref().unwrap();
config.oom_score_adj = process.oom_score_adj;
config.process_label = process.selinux_label.clone();
if process.capabilities.as_ref().is_some() {
let cap = process.capabilities.as_ref().unwrap();
config.capabilities = Some(Capabilities {
..cap
})
}
}
config.hooks = None;
config.version = spec.version;
Ok(config)
}
}
impl Mount {
fn new(cwd: &str, m: &oci::Mount) -> Result<Self> {
}
}
*/

View File

@@ -0,0 +1,46 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
use serde;
#[macro_use]
use serde_derive;
use serde_json;
use std::collections::HashMap;
#[macro_use]
use lazy_static;
pub type NamespaceType = String;
pub type Namespaces = Vec<Namespace>;
#[derive(Serialize, Deserialize, Debug)]
pub struct Namespace {
#[serde(default)]
r#type: NamespaceType,
#[serde(default)]
path: String,
}
pub const NEWNET: &'static str = "NEWNET";
pub const NEWPID: &'static str = "NEWPID";
pub const NEWNS: &'static str = "NEWNS";
pub const NEWUTS: &'static str = "NEWUTS";
pub const NEWUSER: &'static str = "NEWUSER";
pub const NEWCGROUP: &'static str = "NEWCGROUP";
pub const NEWIPC: &'static str = "NEWIPC";
lazy_static! {
static ref TYPETONAME: HashMap<&'static str, &'static str> = {
let mut m = HashMap::new();
m.insert("pid", "pid");
m.insert("network", "net");
m.insert("mount", "mnt");
m.insert("user", "user");
m.insert("uts", "uts");
m.insert("ipc", "ipc");
m.insert("cgroup", "cgroup");
m
};
}

View File

@@ -0,0 +1,23 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
use crate::configs::Config;
use std::io::Result;
pub trait Validator {
fn validate(&self, config: &Config) -> Result<()> {
Ok(())
}
}
pub struct ConfigValidator {}
impl Validator for ConfigValidator {}
impl ConfigValidator {
fn new() -> Self {
ConfigValidator {}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,34 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
// define errors here
error_chain! {
types {
Error, ErrorKind, ResultExt, Result;
}
// foreign error conv to chain error
foreign_links {
Io(std::io::Error);
Nix(nix::Error);
Ffi(std::ffi::NulError);
Caps(caps::errors::Error);
Serde(serde_json::Error);
FromUTF8(std::string::FromUtf8Error);
Parse(std::num::ParseIntError);
Scanfmt(scan_fmt::parse::ScanError);
Ip(std::net::AddrParseError);
Regex(regex::Error);
EnvVar(std::env::VarError);
UTF8(std::str::Utf8Error);
}
// define new errors
errors {
ErrorCode(t: String) {
description("Error Code")
display("Error Code: '{}'", t)
}
}
}

View File

@@ -0,0 +1,585 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
// #![allow(unused_attributes)]
// #![allow(unused_imports)]
// #![allow(unused_variables)]
// #![allow(unused_mut)]
#![allow(dead_code)]
// #![allow(deprecated)]
// #![allow(unused_must_use)]
#![allow(non_upper_case_globals)]
// #![allow(unused_comparisons)]
#[macro_use]
extern crate error_chain;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
extern crate caps;
extern crate protocols;
#[macro_use]
extern crate scopeguard;
extern crate prctl;
#[macro_use]
extern crate lazy_static;
extern crate libc;
extern crate protobuf;
#[macro_use]
extern crate slog;
#[macro_use]
extern crate scan_fmt;
extern crate oci;
extern crate path_absolutize;
extern crate regex;
// Convenience macro to obtain the scope logger
macro_rules! sl {
() => {
slog_scope::logger().new(o!("subsystem" => "rustjail"))
};
}
pub mod capabilities;
pub mod cgroups;
pub mod container;
pub mod errors;
pub mod mount;
pub mod process;
pub mod specconv;
pub mod sync;
pub mod validator;
// pub mod factory;
//pub mod configs;
// pub mod devices;
// pub mod init;
// pub mod rootfs;
// pub mod capabilities;
// pub mod console;
// pub mod stats;
// pub mod user;
//pub mod intelrdt;
// construtc ociSpec from grpcSpec, which is needed for hook
// execution. since hooks read config.json
use oci::{
Box as ociBox, Hooks as ociHooks, Linux as ociLinux, LinuxCapabilities as ociLinuxCapabilities,
Mount as ociMount, POSIXRlimit as ociPOSIXRlimit, Process as ociProcess, Root as ociRoot,
Spec as ociSpec, User as ociUser,
};
use protocols::oci::{
Hooks as grpcHooks, Linux as grpcLinux, Mount as grpcMount, Process as grpcProcess,
Root as grpcRoot, Spec as grpcSpec,
};
use std::collections::HashMap;
use std::mem::MaybeUninit;
pub fn process_grpc_to_oci(p: &grpcProcess) -> ociProcess {
let console_size = if p.ConsoleSize.is_some() {
let c = p.ConsoleSize.as_ref().unwrap();
Some(ociBox {
height: c.Height,
width: c.Width,
})
} else {
None
};
let user = if p.User.is_some() {
let u = p.User.as_ref().unwrap();
ociUser {
uid: u.UID,
gid: u.GID,
additional_gids: u.AdditionalGids.clone(),
username: u.Username.clone(),
}
} else {
unsafe { MaybeUninit::zeroed().assume_init() }
};
let capabilities = if p.Capabilities.is_some() {
let cap = p.Capabilities.as_ref().unwrap();
Some(ociLinuxCapabilities {
bounding: cap.Bounding.clone().into_vec(),
effective: cap.Effective.clone().into_vec(),
inheritable: cap.Inheritable.clone().into_vec(),
permitted: cap.Permitted.clone().into_vec(),
ambient: cap.Ambient.clone().into_vec(),
})
} else {
None
};
let rlimits = {
let mut r = Vec::new();
for lm in p.Rlimits.iter() {
r.push(ociPOSIXRlimit {
r#type: lm.Type.clone(),
hard: lm.Hard,
soft: lm.Soft,
});
}
r
};
ociProcess {
terminal: p.Terminal,
console_size,
user,
args: p.Args.clone().into_vec(),
env: p.Env.clone().into_vec(),
cwd: p.Cwd.clone(),
capabilities,
rlimits,
no_new_privileges: p.NoNewPrivileges,
apparmor_profile: p.ApparmorProfile.clone(),
oom_score_adj: Some(p.OOMScoreAdj as i32),
selinux_label: p.SelinuxLabel.clone(),
}
}
fn process_oci_to_grpc(_p: ociProcess) -> grpcProcess {
// dont implement it for now
unsafe { MaybeUninit::zeroed().assume_init() }
}
fn root_grpc_to_oci(root: &grpcRoot) -> ociRoot {
ociRoot {
path: root.Path.clone(),
readonly: root.Readonly,
}
}
fn root_oci_to_grpc(_root: &ociRoot) -> grpcRoot {
unsafe { MaybeUninit::zeroed().assume_init() }
}
fn mount_grpc_to_oci(m: &grpcMount) -> ociMount {
ociMount {
destination: m.destination.clone(),
r#type: m.field_type.clone(),
source: m.source.clone(),
options: m.options.clone().into_vec(),
}
}
fn mount_oci_to_grpc(_m: &ociMount) -> grpcMount {
unsafe { MaybeUninit::zeroed().assume_init() }
}
use oci::Hook as ociHook;
use protocols::oci::Hook as grpcHook;
fn hook_grpc_to_oci(h: &[grpcHook]) -> Vec<ociHook> {
let mut r = Vec::new();
for e in h.iter() {
r.push(ociHook {
path: e.Path.clone(),
args: e.Args.clone().into_vec(),
env: e.Env.clone().into_vec(),
timeout: Some(e.Timeout as i32),
});
}
r
}
fn hooks_grpc_to_oci(h: &grpcHooks) -> ociHooks {
let prestart = hook_grpc_to_oci(h.Prestart.as_ref());
let poststart = hook_grpc_to_oci(h.Poststart.as_ref());
let poststop = hook_grpc_to_oci(h.Poststop.as_ref());
ociHooks {
prestart,
poststart,
poststop,
}
}
fn hooks_oci_to_grpc(_h: &ociHooks) -> grpcHooks {
unsafe { MaybeUninit::zeroed().assume_init() }
}
use oci::{
LinuxDevice as ociLinuxDevice, LinuxIDMapping as ociLinuxIDMapping,
LinuxIntelRdt as ociLinuxIntelRdt, LinuxNamespace as ociLinuxNamespace,
LinuxResources as ociLinuxResources, LinuxSeccomp as ociLinuxSeccomp,
};
use protocols::oci::{
LinuxIDMapping as grpcLinuxIDMapping, LinuxResources as grpcLinuxResources,
LinuxSeccomp as grpcLinuxSeccomp,
};
fn idmap_grpc_to_oci(im: &grpcLinuxIDMapping) -> ociLinuxIDMapping {
ociLinuxIDMapping {
container_id: im.ContainerID,
host_id: im.HostID,
size: im.Size,
}
}
fn idmaps_grpc_to_oci(ims: &[grpcLinuxIDMapping]) -> Vec<ociLinuxIDMapping> {
let mut r = Vec::new();
for im in ims.iter() {
r.push(idmap_grpc_to_oci(im));
}
r
}
use oci::{
LinuxBlockIO as ociLinuxBlockIO, LinuxBlockIODevice as ociLinuxBlockIODevice,
LinuxCPU as ociLinuxCPU, LinuxDeviceCgroup as ociLinuxDeviceCgroup,
LinuxHugepageLimit as ociLinuxHugepageLimit,
LinuxInterfacePriority as ociLinuxInterfacePriority, LinuxMemory as ociLinuxMemory,
LinuxNetwork as ociLinuxNetwork, LinuxPids as ociLinuxPids,
LinuxThrottleDevice as ociLinuxThrottleDevice, LinuxWeightDevice as ociLinuxWeightDevice,
};
use protocols::oci::{
LinuxBlockIO as grpcLinuxBlockIO, LinuxThrottleDevice as grpcLinuxThrottleDevice,
LinuxWeightDevice as grpcLinuxWeightDevice,
};
fn throttle_devices_grpc_to_oci(tds: &[grpcLinuxThrottleDevice]) -> Vec<ociLinuxThrottleDevice> {
let mut r = Vec::new();
for td in tds.iter() {
r.push(ociLinuxThrottleDevice {
blk: ociLinuxBlockIODevice {
major: td.Major,
minor: td.Minor,
},
rate: td.Rate,
});
}
r
}
fn weight_devices_grpc_to_oci(wds: &[grpcLinuxWeightDevice]) -> Vec<ociLinuxWeightDevice> {
let mut r = Vec::new();
for wd in wds.iter() {
r.push(ociLinuxWeightDevice {
blk: ociLinuxBlockIODevice {
major: wd.Major,
minor: wd.Minor,
},
weight: Some(wd.Weight as u16),
leaf_weight: Some(wd.LeafWeight as u16),
});
}
r
}
fn blockio_grpc_to_oci(blk: &grpcLinuxBlockIO) -> ociLinuxBlockIO {
let weight_device = weight_devices_grpc_to_oci(blk.WeightDevice.as_ref());
let throttle_read_bps_device = throttle_devices_grpc_to_oci(blk.ThrottleReadBpsDevice.as_ref());
let throttle_write_bps_device =
throttle_devices_grpc_to_oci(blk.ThrottleWriteBpsDevice.as_ref());
let throttle_read_iops_device =
throttle_devices_grpc_to_oci(blk.ThrottleReadIOPSDevice.as_ref());
let throttle_write_iops_device =
throttle_devices_grpc_to_oci(blk.ThrottleWriteIOPSDevice.as_ref());
ociLinuxBlockIO {
weight: Some(blk.Weight as u16),
leaf_weight: Some(blk.LeafWeight as u16),
weight_device,
throttle_read_bps_device,
throttle_write_bps_device,
throttle_read_iops_device,
throttle_write_iops_device,
}
}
pub fn resources_grpc_to_oci(res: &grpcLinuxResources) -> ociLinuxResources {
let devices = {
let mut d = Vec::new();
for dev in res.Devices.iter() {
let major = if dev.Major == -1 {
None
} else {
Some(dev.Major)
};
let minor = if dev.Minor == -1 {
None
} else {
Some(dev.Minor)
};
d.push(ociLinuxDeviceCgroup {
allow: dev.Allow,
r#type: dev.Type.clone(),
major,
minor,
access: dev.Access.clone(),
});
}
d
};
let memory = if res.Memory.is_some() {
let mem = res.Memory.as_ref().unwrap();
Some(ociLinuxMemory {
limit: Some(mem.Limit),
reservation: Some(mem.Reservation),
swap: Some(mem.Swap),
kernel: Some(mem.Kernel),
kernel_tcp: Some(mem.KernelTCP),
swapiness: Some(mem.Swappiness as i64),
disable_oom_killer: Some(mem.DisableOOMKiller),
})
} else {
None
};
let cpu = if res.CPU.is_some() {
let c = res.CPU.as_ref().unwrap();
Some(ociLinuxCPU {
shares: Some(c.Shares),
quota: Some(c.Quota),
period: Some(c.Period),
realtime_runtime: Some(c.RealtimeRuntime),
realtime_period: Some(c.RealtimePeriod),
cpus: c.Cpus.clone(),
mems: c.Mems.clone(),
})
} else {
None
};
let pids = if res.Pids.is_some() {
let p = res.Pids.as_ref().unwrap();
Some(ociLinuxPids { limit: p.Limit })
} else {
None
};
let block_io = if res.BlockIO.is_some() {
let blk = res.BlockIO.as_ref().unwrap();
// copy LinuxBlockIO
Some(blockio_grpc_to_oci(blk))
} else {
None
};
let hugepage_limits = {
let mut r = Vec::new();
for hl in res.HugepageLimits.iter() {
r.push(ociLinuxHugepageLimit {
page_size: hl.Pagesize.clone(),
limit: hl.Limit,
});
}
r
};
let network = if res.Network.is_some() {
let net = res.Network.as_ref().unwrap();
let priorities = {
let mut r = Vec::new();
for pr in net.Priorities.iter() {
r.push(ociLinuxInterfacePriority {
name: pr.Name.clone(),
priority: pr.Priority,
});
}
r
};
Some(ociLinuxNetwork {
class_id: Some(net.ClassID),
priorities,
})
} else {
None
};
ociLinuxResources {
devices,
memory,
cpu,
pids,
block_io,
hugepage_limits,
network,
rdma: HashMap::new(),
}
}
use oci::{LinuxSeccompArg as ociLinuxSeccompArg, LinuxSyscall as ociLinuxSyscall};
fn seccomp_grpc_to_oci(sec: &grpcLinuxSeccomp) -> ociLinuxSeccomp {
let syscalls = {
let mut r = Vec::new();
for sys in sec.Syscalls.iter() {
let mut args = Vec::new();
for arg in sys.Args.iter() {
args.push(ociLinuxSeccompArg {
index: arg.Index as u32,
value: arg.Value,
value_two: arg.ValueTwo,
op: arg.Op.clone(),
});
}
r.push(ociLinuxSyscall {
names: sys.Names.clone().into_vec(),
action: sys.Action.clone(),
args,
});
}
r
};
ociLinuxSeccomp {
default_action: sec.DefaultAction.clone(),
architectures: sec.Architectures.clone().into_vec(),
syscalls,
}
}
fn linux_grpc_to_oci(l: &grpcLinux) -> ociLinux {
let uid_mappings = idmaps_grpc_to_oci(l.UIDMappings.as_ref());
let gid_mappings = idmaps_grpc_to_oci(l.GIDMappings.as_ref());
let resources = if l.Resources.is_some() {
Some(resources_grpc_to_oci(l.Resources.as_ref().unwrap()))
} else {
None
};
let seccomp = if l.Seccomp.is_some() {
Some(seccomp_grpc_to_oci(l.Seccomp.as_ref().unwrap()))
} else {
None
};
let namespaces = {
let mut r = Vec::new();
for ns in l.Namespaces.iter() {
r.push(ociLinuxNamespace {
r#type: ns.Type.clone(),
path: ns.Path.clone(),
});
}
r
};
let devices = {
let mut r = Vec::new();
for d in l.Devices.iter() {
r.push(ociLinuxDevice {
path: d.Path.clone(),
r#type: d.Type.clone(),
major: d.Major,
minor: d.Minor,
file_mode: Some(d.FileMode),
uid: Some(d.UID),
gid: Some(d.GID),
});
}
r
};
let intel_rdt = if l.IntelRdt.is_some() {
let rdt = l.IntelRdt.as_ref().unwrap();
Some(ociLinuxIntelRdt {
l3_cache_schema: rdt.L3CacheSchema.clone(),
})
} else {
None
};
ociLinux {
uid_mappings,
gid_mappings,
sysctl: l.Sysctl.clone(),
resources,
cgroups_path: l.CgroupsPath.clone(),
namespaces,
devices,
seccomp,
rootfs_propagation: l.RootfsPropagation.clone(),
masked_paths: l.MaskedPaths.clone().into_vec(),
readonly_paths: l.ReadonlyPaths.clone().into_vec(),
mount_label: l.MountLabel.clone(),
intel_rdt,
}
}
fn linux_oci_to_grpc(_l: &ociLinux) -> grpcLinux {
grpcLinux::default()
}
pub fn grpc_to_oci(grpc: &grpcSpec) -> ociSpec {
// process
let process = if grpc.Process.is_some() {
Some(process_grpc_to_oci(grpc.Process.as_ref().unwrap()))
} else {
None
};
// root
let root = if grpc.Root.is_some() {
Some(root_grpc_to_oci(grpc.Root.as_ref().unwrap()))
} else {
None
};
// mounts
let mounts = {
let mut r = Vec::new();
for m in grpc.Mounts.iter() {
r.push(mount_grpc_to_oci(m));
}
r
};
// hooks
let hooks = if grpc.Hooks.is_some() {
Some(hooks_grpc_to_oci(grpc.Hooks.as_ref().unwrap()))
} else {
None
};
// Linux
let linux = if grpc.Linux.is_some() {
Some(linux_grpc_to_oci(grpc.Linux.as_ref().unwrap()))
} else {
None
};
ociSpec {
version: grpc.Version.clone(),
process,
root,
hostname: grpc.Hostname.clone(),
mounts,
hooks,
annotations: grpc.Annotations.clone(),
linux,
solaris: None,
windows: None,
vm: None,
}
}
pub fn oci_to_grpc(_oci: &ociSpec) -> grpcSpec {
unsafe { MaybeUninit::zeroed().assume_init() }
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}

View File

@@ -0,0 +1,744 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
use libc::uid_t;
use nix::errno::Errno;
use nix::fcntl::{self, OFlag};
use nix::mount::{self, MntFlags, MsFlags};
use nix::sys::stat::{self, Mode, SFlag};
use nix::unistd::{self, Gid, Uid};
use nix::NixPath;
use oci::{LinuxDevice, Mount, Spec};
use std::collections::{HashMap, HashSet};
use std::fs::{self, OpenOptions};
use std::os::unix;
use std::os::unix::io::RawFd;
use std::path::{Path, PathBuf};
use path_absolutize::*;
use scan_fmt;
use std::fs::File;
use std::io::{BufRead, BufReader};
use crate::container::DEFAULT_DEVICES;
use crate::errors::*;
use crate::sync::write_count;
use lazy_static;
use std::string::ToString;
use crate::log_child;
// Info reveals information about a particular mounted filesystem. This
// struct is populated from the content in the /proc/<pid>/mountinfo file.
pub struct Info {
id: i32,
parent: i32,
major: i32,
minor: i32,
root: String,
mount_point: String,
opts: String,
optional: String,
fstype: String,
source: String,
vfs_opts: String,
}
const MOUNTINFOFORMAT: &'static str = "{d} {d} {d}:{d} {} {} {} {}";
lazy_static! {
static ref PROPAGATION: HashMap<&'static str, MsFlags> = {
let mut m = HashMap::new();
m.insert("shared", MsFlags::MS_SHARED | MsFlags::MS_REC);
m.insert("private", MsFlags::MS_PRIVATE | MsFlags::MS_REC);
m.insert("slave", MsFlags::MS_SLAVE | MsFlags::MS_REC);
m
};
static ref OPTIONS: HashMap<&'static str, (bool, MsFlags)> = {
let mut m = HashMap::new();
m.insert("defaults", (false, MsFlags::empty()));
m.insert("ro", (false, MsFlags::MS_RDONLY));
m.insert("rw", (true, MsFlags::MS_RDONLY));
m.insert("suid", (true, MsFlags::MS_NOSUID));
m.insert("nosuid", (false, MsFlags::MS_NOSUID));
m.insert("dev", (true, MsFlags::MS_NODEV));
m.insert("nodev", (false, MsFlags::MS_NODEV));
m.insert("exec", (true, MsFlags::MS_NOEXEC));
m.insert("noexec", (false, MsFlags::MS_NOEXEC));
m.insert("sync", (false, MsFlags::MS_SYNCHRONOUS));
m.insert("async", (true, MsFlags::MS_SYNCHRONOUS));
m.insert("dirsync", (false, MsFlags::MS_DIRSYNC));
m.insert("remount", (false, MsFlags::MS_REMOUNT));
m.insert("mand", (false, MsFlags::MS_MANDLOCK));
m.insert("nomand", (true, MsFlags::MS_MANDLOCK));
m.insert("atime", (true, MsFlags::MS_NOATIME));
m.insert("noatime", (false, MsFlags::MS_NOATIME));
m.insert("diratime", (true, MsFlags::MS_NODIRATIME));
m.insert("nodiratime", (false, MsFlags::MS_NODIRATIME));
m.insert("bind", (false, MsFlags::MS_BIND));
m.insert("rbind", (false, MsFlags::MS_BIND | MsFlags::MS_REC));
m.insert("unbindable", (false, MsFlags::MS_UNBINDABLE));
m.insert(
"runbindable",
(false, MsFlags::MS_UNBINDABLE | MsFlags::MS_REC),
);
m.insert("private", (false, MsFlags::MS_PRIVATE));
m.insert("rprivate", (false, MsFlags::MS_PRIVATE | MsFlags::MS_REC));
m.insert("shared", (false, MsFlags::MS_SHARED));
m.insert("rshared", (false, MsFlags::MS_SHARED | MsFlags::MS_REC));
m.insert("slave", (false, MsFlags::MS_SLAVE));
m.insert("rslave", (false, MsFlags::MS_SLAVE | MsFlags::MS_REC));
m.insert("relatime", (false, MsFlags::MS_RELATIME));
m.insert("norelatime", (true, MsFlags::MS_RELATIME));
m.insert("strictatime", (false, MsFlags::MS_STRICTATIME));
m.insert("nostrictatime", (true, MsFlags::MS_STRICTATIME));
m
};
}
pub fn init_rootfs(
cfd_log: RawFd,
spec: &Spec,
cpath: &HashMap<String, String>,
mounts: &HashMap<String, String>,
bind_device: bool,
) -> Result<()> {
lazy_static::initialize(&OPTIONS);
lazy_static::initialize(&PROPAGATION);
lazy_static::initialize(&LINUXDEVICETYPE);
let linux = spec.linux.as_ref().unwrap();
let mut flags = MsFlags::MS_REC;
match PROPAGATION.get(&linux.rootfs_propagation.as_str()) {
Some(fl) => flags |= *fl,
None => flags |= MsFlags::MS_SLAVE,
}
let rootfs = spec.root.as_ref().unwrap().path.as_str();
let root = fs::canonicalize(rootfs)?;
let rootfs = root.to_str().unwrap();
mount::mount(None::<&str>, "/", None::<&str>, flags, None::<&str>)?;
mount::mount(
Some(rootfs),
rootfs,
None::<&str>,
MsFlags::MS_BIND | MsFlags::MS_REC,
None::<&str>,
)?;
for m in &spec.mounts {
let (mut flags, data) = parse_mount(&m);
if !m.destination.starts_with("/") || m.destination.contains("..") {
return Err(ErrorKind::Nix(nix::Error::Sys(Errno::EINVAL)).into());
}
if m.r#type == "cgroup" {
mount_cgroups(cfd_log, &m, rootfs, flags, &data, cpath, mounts)?;
} else {
if m.destination == "/dev" {
flags &= !MsFlags::MS_RDONLY;
}
mount_from(cfd_log, &m, &rootfs, flags, &data, "")?;
}
}
let olddir = unistd::getcwd()?;
unistd::chdir(rootfs)?;
default_symlinks()?;
create_devices(&linux.devices, bind_device)?;
ensure_ptmx()?;
unistd::chdir(&olddir)?;
Ok(())
}
fn mount_cgroups(
cfd_log: RawFd,
m: &Mount,
rootfs: &str,
flags: MsFlags,
_data: &str,
cpath: &HashMap<String, String>,
mounts: &HashMap<String, String>,
) -> Result<()> {
// mount tmpfs
let ctm = Mount {
source: "tmpfs".to_string(),
r#type: "tmpfs".to_string(),
destination: m.destination.clone(),
options: Vec::new(),
};
let cflags = MsFlags::MS_NOEXEC | MsFlags::MS_NOSUID | MsFlags::MS_NODEV;
// info!(logger, "tmpfs");
mount_from(cfd_log, &ctm, rootfs, cflags, "", "")?;
let olddir = unistd::getcwd()?;
unistd::chdir(rootfs)?;
let mut srcs: HashSet<String> = HashSet::new();
// bind mount cgroups
for (key, mount) in mounts.iter() {
log_child!(cfd_log, "mount cgroup subsystem {}", key);
let source = if cpath.get(key).is_some() {
cpath.get(key).unwrap()
} else {
continue;
};
let base = if let Some(o) = mount.rfind('/') {
&mount[o + 1..]
} else {
&mount[..]
};
let destination = format!("{}/{}", m.destination.as_str(), base);
if srcs.contains(source) {
// already mounted, xxx,yyy style cgroup
if key != base {
let src = format!("{}/{}", m.destination.as_str(), key);
unix::fs::symlink(destination.as_str(), &src[1..])?;
}
continue;
}
srcs.insert(source.to_string());
log_child!(cfd_log, "mount destination: {}", destination.as_str());
let bm = Mount {
source: source.to_string(),
r#type: "bind".to_string(),
destination: destination.clone(),
options: Vec::new(),
};
let mut mount_flags: MsFlags = flags | MsFlags::MS_REC | MsFlags::MS_BIND;
if key.contains("systemd") {
mount_flags &= !MsFlags::MS_RDONLY;
}
mount_from(cfd_log, &bm, rootfs, mount_flags, "", "")?;
if key != base {
let src = format!("{}/{}", m.destination.as_str(), key);
match unix::fs::symlink(destination.as_str(), &src[1..]) {
Err(e) => {
log_child!(
cfd_log,
"symlink: {} {} err: {}",
key,
destination.as_str(),
e.to_string()
);
return Err(e.into());
}
Ok(_) => {}
}
}
}
unistd::chdir(&olddir)?;
if flags.contains(MsFlags::MS_RDONLY) {
let dest = format!("{}{}", rootfs, m.destination.as_str());
mount::mount(
Some(dest.as_str()),
dest.as_str(),
None::<&str>,
flags | MsFlags::MS_BIND | MsFlags::MS_REMOUNT,
None::<&str>,
)?;
}
Ok(())
}
pub fn pivot_rootfs<P: ?Sized + NixPath>(path: &P) -> Result<()> {
let oldroot = fcntl::open("/", OFlag::O_DIRECTORY | OFlag::O_RDONLY, Mode::empty())?;
defer!(unistd::close(oldroot).unwrap());
let newroot = fcntl::open(path, OFlag::O_DIRECTORY | OFlag::O_RDONLY, Mode::empty())?;
defer!(unistd::close(newroot).unwrap());
unistd::pivot_root(path, path)?;
mount::umount2("/", MntFlags::MNT_DETACH)?;
unistd::fchdir(newroot)?;
stat::umask(Mode::from_bits_truncate(0o022));
Ok(())
}
// Parse /proc/self/mountinfo because comparing Dev and ino does not work from
// bind mounts
fn parse_mount_table() -> Result<Vec<Info>> {
let file = File::open("/proc/self/mountinfo")?;
let reader = BufReader::new(file);
let mut infos = Vec::new();
for (_index, line) in reader.lines().enumerate() {
let line = line?;
let (id, parent, major, minor, root, mount_point, opts, optional) = scan_fmt!(
&line,
MOUNTINFOFORMAT,
i32,
i32,
i32,
i32,
String,
String,
String,
String
)?;
let fields: Vec<&str> = line.split(" - ").collect();
if fields.len() == 2 {
let (fstype, source, vfs_opts) =
scan_fmt!(fields[1], "{} {} {}", String, String, String)?;
let mut optional_new = String::new();
if optional != "-" {
optional_new = optional;
}
let info = Info {
id,
parent,
major,
minor,
root,
mount_point,
opts,
optional: optional_new,
fstype,
source,
vfs_opts,
};
infos.push(info);
} else {
return Err(ErrorKind::ErrorCode("failed to parse mount info file".to_string()).into());
}
}
Ok(infos)
}
pub fn ms_move_root(rootfs: &str) -> Result<bool> {
unistd::chdir(rootfs)?;
let mount_infos = parse_mount_table()?;
let root_path = Path::new(rootfs);
let abs_root_buf = root_path.absolutize()?;
let abs_root = abs_root_buf.to_str().ok_or::<Error>(
ErrorKind::ErrorCode(format!("failed to parse {} to absolute path", rootfs)).into(),
)?;
for info in mount_infos.iter() {
let mount_point = Path::new(&info.mount_point);
let abs_mount_buf = mount_point.absolutize()?;
let abs_mount_point = abs_mount_buf.to_str().ok_or::<Error>(
ErrorKind::ErrorCode(format!(
"failed to parse {} to absolute path",
info.mount_point
))
.into(),
)?;
let abs_mount_point_string = String::from(abs_mount_point);
// Umount every syfs and proc file systems, except those under the container rootfs
if (info.fstype != "proc" && info.fstype != "sysfs")
|| abs_mount_point_string.starts_with(abs_root)
{
continue;
}
// Be sure umount events are not propagated to the host.
mount::mount(
None::<&str>,
abs_mount_point,
None::<&str>,
MsFlags::MS_SLAVE | MsFlags::MS_REC,
None::<&str>,
)?;
match mount::umount2(abs_mount_point, MntFlags::MNT_DETACH) {
Ok(_) => (),
Err(e) => {
if e.ne(&nix::Error::from(Errno::EINVAL)) && e.ne(&nix::Error::from(Errno::EPERM)) {
return Err(ErrorKind::ErrorCode(e.to_string()).into());
}
// If we have not privileges for umounting (e.g. rootless), then
// cover the path.
mount::mount(
Some("tmpfs"),
abs_mount_point,
Some("tmpfs"),
MsFlags::empty(),
None::<&str>,
)?;
}
}
}
mount::mount(
Some(abs_root),
"/",
None::<&str>,
MsFlags::MS_MOVE,
None::<&str>,
)?;
unistd::chroot(".")?;
unistd::chdir("/")?;
Ok(true)
}
fn parse_mount(m: &Mount) -> (MsFlags, String) {
let mut flags = MsFlags::empty();
let mut data = Vec::new();
for o in &m.options {
match OPTIONS.get(o.as_str()) {
Some(v) => {
let (clear, fl) = *v;
if clear {
flags &= !fl;
} else {
flags |= fl;
}
}
None => data.push(o.clone()),
}
}
(flags, data.join(","))
}
fn mount_from(
cfd_log: RawFd,
m: &Mount,
rootfs: &str,
flags: MsFlags,
data: &str,
_label: &str,
) -> Result<()> {
let d = String::from(data);
let dest = format!("{}{}", rootfs, &m.destination);
let src = if m.r#type.as_str() == "bind" {
let src = fs::canonicalize(m.source.as_str())?;
let dir = if src.is_file() {
Path::new(&dest).parent().unwrap()
} else {
Path::new(&dest)
};
// let _ = fs::create_dir_all(&dir);
match fs::create_dir_all(&dir) {
Ok(_) => {}
Err(e) => {
log_child!(
cfd_log,
"creat dir {}: {}",
dir.to_str().unwrap(),
e.to_string()
);
}
}
// make sure file exists so we can bind over it
if src.is_file() {
let _ = OpenOptions::new().create(true).write(true).open(&dest);
}
src
} else {
let _ = fs::create_dir_all(&dest);
PathBuf::from(&m.source)
};
// ignore this check since some mount's src didn't been a directory
// such as tmpfs.
/*
match stat::stat(src.to_str().unwrap()) {
Ok(_) => {}
Err(e) => {
info!("{}: {}", src.to_str().unwrap(), e.as_errno().unwrap().desc());
}
}
*/
match stat::stat(dest.as_str()) {
Ok(_) => {}
Err(e) => {
log_child!(
cfd_log,
"{}: {}",
dest.as_str(),
e.as_errno().unwrap().desc()
);
}
}
match mount::mount(
Some(src.to_str().unwrap()),
dest.as_str(),
Some(m.r#type.as_str()),
flags,
Some(d.as_str()),
) {
Ok(_) => {}
Err(e) => {
log_child!(cfd_log, "mount error: {}", e.as_errno().unwrap().desc());
return Err(e.into());
}
}
if flags.contains(MsFlags::MS_BIND)
&& flags.intersects(
!(MsFlags::MS_REC
| MsFlags::MS_REMOUNT
| MsFlags::MS_BIND
| MsFlags::MS_PRIVATE
| MsFlags::MS_SHARED
| MsFlags::MS_SLAVE),
)
{
match mount::mount(
Some(dest.as_str()),
dest.as_str(),
None::<&str>,
flags | MsFlags::MS_REMOUNT,
None::<&str>,
) {
Err(e) => {
log_child!(
cfd_log,
"remout {}: {}",
dest.as_str(),
e.as_errno().unwrap().desc()
);
return Err(e.into());
}
Ok(_) => {}
}
}
Ok(())
}
static SYMLINKS: &'static [(&'static str, &'static str)] = &[
("/proc/self/fd", "dev/fd"),
("/proc/self/fd/0", "dev/stdin"),
("/proc/self/fd/1", "dev/stdout"),
("/proc/self/fd/2", "dev/stderr"),
];
fn default_symlinks() -> Result<()> {
if Path::new("/proc/kcore").exists() {
unix::fs::symlink("/proc/kcore", "dev/kcore")?;
}
for &(src, dst) in SYMLINKS {
unix::fs::symlink(src, dst)?;
}
Ok(())
}
fn create_devices(devices: &[LinuxDevice], bind: bool) -> Result<()> {
let op: fn(&LinuxDevice) -> Result<()> = if bind { bind_dev } else { mknod_dev };
let old = stat::umask(Mode::from_bits_truncate(0o000));
for dev in DEFAULT_DEVICES.iter() {
op(dev)?;
}
for dev in devices {
if !dev.path.starts_with("/dev") || dev.path.contains("..") {
let msg = format!("{} is not a valid device path", dev.path);
bail!(ErrorKind::ErrorCode(msg));
}
op(dev)?;
}
stat::umask(old);
Ok(())
}
fn ensure_ptmx() -> Result<()> {
let _ = fs::remove_file("dev/ptmx");
unix::fs::symlink("pts/ptmx", "dev/ptmx")?;
Ok(())
}
fn makedev(major: u64, minor: u64) -> u64 {
(minor & 0xff) | ((major & 0xfff) << 8) | ((minor & !0xff) << 12) | ((major & !0xfff) << 32)
}
lazy_static! {
static ref LINUXDEVICETYPE: HashMap<&'static str, SFlag> = {
let mut m = HashMap::new();
m.insert("c", SFlag::S_IFCHR);
m.insert("b", SFlag::S_IFBLK);
m.insert("p", SFlag::S_IFIFO);
m
};
}
fn mknod_dev(dev: &LinuxDevice) -> Result<()> {
let f = match LINUXDEVICETYPE.get(dev.r#type.as_str()) {
Some(v) => v,
None => return Err(ErrorKind::ErrorCode("invalid spec".to_string()).into()),
};
stat::mknod(
&dev.path[1..],
*f,
Mode::from_bits_truncate(dev.file_mode.unwrap_or(0)),
makedev(dev.major as u64, dev.minor as u64),
)?;
unistd::chown(
&dev.path[1..],
Some(Uid::from_raw(dev.uid.unwrap_or(0) as uid_t)),
Some(Gid::from_raw(dev.gid.unwrap_or(0) as uid_t)),
)?;
Ok(())
}
fn bind_dev(dev: &LinuxDevice) -> Result<()> {
let fd = fcntl::open(
&dev.path[1..],
OFlag::O_RDWR | OFlag::O_CREAT,
Mode::from_bits_truncate(0o644),
)?;
unistd::close(fd)?;
mount::mount(
Some(&*dev.path),
&dev.path[1..],
None::<&str>,
MsFlags::MS_BIND,
None::<&str>,
)?;
Ok(())
}
pub fn finish_rootfs(cfd_log: RawFd, spec: &Spec) -> Result<()> {
let olddir = unistd::getcwd()?;
log_child!(cfd_log, "old cwd: {}", olddir.to_str().unwrap());
unistd::chdir("/")?;
if spec.linux.is_some() {
let linux = spec.linux.as_ref().unwrap();
for path in linux.masked_paths.iter() {
mask_path(path)?;
}
for path in linux.readonly_paths.iter() {
readonly_path(path)?;
}
}
for m in spec.mounts.iter() {
if m.destination == "/dev" {
let (flags, _) = parse_mount(m);
if flags.contains(MsFlags::MS_RDONLY) {
mount::mount(
Some("/dev"),
"/dev",
None::<&str>,
flags | MsFlags::MS_REMOUNT,
None::<&str>,
)?;
}
}
}
if spec.root.as_ref().unwrap().readonly {
let flags = MsFlags::MS_BIND | MsFlags::MS_RDONLY | MsFlags::MS_NODEV | MsFlags::MS_REMOUNT;
mount::mount(Some("/"), "/", None::<&str>, flags, None::<&str>)?;
}
stat::umask(Mode::from_bits_truncate(0o022));
unistd::chdir(&olddir)?;
Ok(())
}
fn mask_path(path: &str) -> Result<()> {
if !path.starts_with("/") || path.contains("..") {
return Err(nix::Error::Sys(Errno::EINVAL).into());
}
//info!("{}", path);
match mount::mount(
Some("/dev/null"),
path,
None::<&str>,
MsFlags::MS_BIND,
None::<&str>,
) {
Err(nix::Error::Sys(e)) => {
if e != Errno::ENOENT && e != Errno::ENOTDIR {
//info!("{}: {}", path, e.desc());
return Err(nix::Error::Sys(e).into());
}
}
Err(e) => {
//info!("{}: {}", path, e.as_errno().unwrap().desc());
return Err(e.into());
}
Ok(_) => {}
}
Ok(())
}
fn readonly_path(path: &str) -> Result<()> {
if !path.starts_with("/") || path.contains("..") {
return Err(nix::Error::Sys(Errno::EINVAL).into());
}
//info!("{}", path);
match mount::mount(
Some(&path[1..]),
path,
None::<&str>,
MsFlags::MS_BIND | MsFlags::MS_REC,
None::<&str>,
) {
Err(nix::Error::Sys(e)) => {
if e == Errno::ENOENT {
return Ok(());
} else {
//info!("{}: {}", path, e.desc());
return Err(nix::Error::Sys(e).into());
}
}
Err(e) => {
//info!("{}: {}", path, e.as_errno().unwrap().desc());
return Err(e.into());
}
Ok(_) => {}
}
mount::mount(
Some(&path[1..]),
&path[1..],
None::<&str>,
MsFlags::MS_BIND | MsFlags::MS_REC | MsFlags::MS_RDONLY | MsFlags::MS_REMOUNT,
None::<&str>,
)?;
Ok(())
}

View File

@@ -0,0 +1,161 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
// use std::process::{Stdio, Command, ExitStatus};
use libc::pid_t;
use std::fs::File;
use std::os::unix::io::RawFd;
// use crate::configs::{Capabilities, Rlimit};
// use crate::cgroups::Manager as CgroupManager;
// use crate::intelrdt::Manager as RdtManager;
use nix::fcntl::{fcntl, FcntlArg, OFlag};
use nix::sys::signal::{self, Signal};
use nix::sys::socket::{self, AddressFamily, SockFlag, SockType};
use nix::sys::wait::{self, WaitStatus};
use nix::unistd::{self, Pid};
use nix::Result;
use nix::Error;
use oci::Process as OCIProcess;
use slog::Logger;
#[derive(Debug)]
pub struct Process {
pub exec_id: String,
pub stdin: Option<RawFd>,
pub stdout: Option<RawFd>,
pub stderr: Option<RawFd>,
pub exit_pipe_r: Option<RawFd>,
pub exit_pipe_w: Option<RawFd>,
pub extra_files: Vec<File>,
// pub caps: Capabilities,
// pub rlimits: Vec<Rlimit>,
pub term_master: Option<RawFd>,
pub tty: bool,
pub parent_stdin: Option<RawFd>,
pub parent_stdout: Option<RawFd>,
pub parent_stderr: Option<RawFd>,
pub init: bool,
// pid of the init/exec process. since we have no command
// struct to store pid, we must store pid here.
pub pid: pid_t,
pub exit_code: i32,
pub oci: OCIProcess,
pub logger: Logger,
}
pub trait ProcessOperations {
fn pid(&self) -> Pid;
fn wait(&self) -> Result<WaitStatus>;
fn signal(&self, sig: Signal) -> Result<()>;
}
impl ProcessOperations for Process {
fn pid(&self) -> Pid {
Pid::from_raw(self.pid)
}
fn wait(&self) -> Result<WaitStatus> {
wait::waitpid(Some(self.pid()), None)
}
fn signal(&self, sig: Signal) -> Result<()> {
signal::kill(self.pid(), Some(sig))
}
}
impl Process {
pub fn new(
logger: &Logger,
ocip: &OCIProcess,
id: &str,
init: bool,
pipe_size: i32,
) -> Result<Self> {
let logger = logger.new(o!("subsystem" => "process"));
let mut p = Process {
exec_id: String::from(id),
stdin: None,
stdout: None,
stderr: None,
exit_pipe_w: None,
exit_pipe_r: None,
extra_files: Vec::new(),
tty: ocip.terminal,
term_master: None,
parent_stdin: None,
parent_stdout: None,
parent_stderr: None,
init,
pid: -1,
exit_code: 0,
oci: ocip.clone(),
logger: logger.clone(),
};
info!(logger, "before create console socket!");
if !p.tty {
info!(logger, "created console socket!");
let (stdin, pstdin) = unistd::pipe2(OFlag::O_CLOEXEC)?;
p.parent_stdin = Some(pstdin);
p.stdin = Some(stdin);
let (pstdout, stdout) = create_extended_pipe(OFlag::O_CLOEXEC, pipe_size)?;
p.parent_stdout = Some(pstdout);
p.stdout = Some(stdout);
let (pstderr, stderr) = create_extended_pipe(OFlag::O_CLOEXEC, pipe_size)?;
p.parent_stderr = Some(pstderr);
p.stderr = Some(stderr);
}
Ok(p)
}
}
fn create_extended_pipe(flags: OFlag, pipe_size: i32) -> Result<(RawFd, RawFd)> {
let (r, w) = unistd::pipe2(flags)?;
if pipe_size > 0 {
fcntl(w, FcntlArg::F_SETPIPE_SZ(pipe_size))?;
}
Ok((r, w))
}
#[cfg(test)]
mod tests {
use crate::process::create_extended_pipe;
use nix::fcntl::{fcntl, FcntlArg, OFlag};
use std::fs;
use std::os::unix::io::RawFd;
fn get_pipe_max_size() -> i32 {
fs::read_to_string("/proc/sys/fs/pipe-max-size")
.unwrap()
.trim()
.parse::<i32>()
.unwrap()
}
fn get_pipe_size(fd: RawFd) -> i32 {
fcntl(fd, FcntlArg::F_GETPIPE_SZ).unwrap()
}
#[test]
fn test_create_extended_pipe() {
// Test the default
let (r, w) = create_extended_pipe(OFlag::O_CLOEXEC, 0).unwrap();
// Test setting to the max size
let max_size = get_pipe_max_size();
let (r, w) = create_extended_pipe(OFlag::O_CLOEXEC, max_size).unwrap();
let actual_size = get_pipe_size(w);
assert_eq!(max_size, actual_size);
}
}

View File

@@ -0,0 +1,159 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
use oci::Spec;
// use crate::configs::namespaces;
// use crate::configs::device::Device;
#[derive(Debug)]
pub struct CreateOpts {
pub cgroup_name: String,
pub use_systemd_cgroup: bool,
pub no_pivot_root: bool,
pub no_new_keyring: bool,
pub spec: Option<Spec>,
pub rootless_euid: bool,
pub rootless_cgroup: bool,
}
/*
const WILDCARD: i32 = -1;
lazy_static! {
static ref NAEMSPACEMAPPING: HashMap<&'static str, &'static str> = {
let mut m = HashMap::new();
m.insert(oci::PIDNAMESPACE, namespaces::NEWPID);
m.insert(oci::NETWORKNAMESPACE, namespaces::NEWNET);
m.insert(oci::UTSNAMESPACE, namespaces::NEWUTS);
m.insert(oci::MOUNTNAMESPACE, namespaces::NEWNS);
m.insert(oci::IPCNAMESPACE, namespaces::NEWIPC);
m.insert(oci::USERNAMESPACE, namespaces::NEWUSER);
m.insert(oci::CGROUPNAMESPACE, namespaces::NEWCGROUP);
m
};
static ref MOUNTPROPAGATIONMAPPING: HashMap<&'static str, MsFlags> = {
let mut m = HashMap::new();
m.insert("rprivate", MsFlags::MS_PRIVATE | MsFlags::MS_REC);
m.insert("private", MsFlags::MS_PRIVATE);
m.insert("rslave", MsFlags::MS_SLAVE | MsFlags::MS_REC);
m.insert("slave", MsFlags::MS_SLAVE);
m.insert("rshared", MsFlags::MS_SHARED | MsFlags::MS_REC);
m.insert("shared", MsFlags::MS_SHARED);
m.insert("runbindable", MsFlags::MS_UNBINDABLE | MsFlags::MS_REC);
m.insert("unbindable", MsFlags::MS_UNBINDABLE);
m
};
static ref ALLOWED_DEVICES: Vec<Device> = {
let mut m = Vec::new();
m.push(Device {
r#type: 'c',
major: WILDCARD,
minor: WILDCARD,
permissions: "m",
allow: true,
});
m.push(Device {
r#type: 'b',
major: WILDCARD,
minor: WILDCARD,
permissions: "m",
allow: true,
});
m.push(Device {
r#type: 'c',
path: "/dev/null".to_string(),
major: 1,
minor: 3,
permissions: "rwm",
allow: true,
});
m.push(Device {
r#type: 'c',
path: String::from("/dev/random"),
major: 1,
minor: 8,
permissions: "rwm",
allow: true,
});
m.push(Device {
r#type: 'c',
path: String::from("/dev/full"),
major: 1,
minor: 7,
permissions: "rwm",
allow: true,
});
m.push(Device {
r#type: 'c',
path: String::from("/dev/tty"),
major: 5,
minor: 0,
permissions: "rwm",
allow: true,
});
m.push(Device {
r#type: 'c',
path: String::from("/dev/zero"),
major: 1,
minor: 5,
permissions: "rwm",
allow: true,
});
m.push(Device {
r#type: 'c',
path: String::from("/dev/urandom"),
major: 1,
minor: 9,
permissions: "rwm",
allow: true,
});
m.push(Device {
r#type: 'c',
path: String::from("/dev/console"),
major: 5,
minor: 1,
permissions: "rwm",
allow: true,
});
m.push(Device {
r#type: 'c',
path: String::from(""),
major: 136,
minor: WILDCARD,
permissions: "rwm",
allow: true,
});
m.push(Device {
r#type: 'c',
path: String::from(""),
major: 5,
minor: 2,
permissions: "rwm",
allow: true,
});
m.push(Device {
r#type: 'c',
path: String::from(""),
major: 10,
minor: 200,
permissions: "rwm",
allow: true,
});
m
};
}
*/

View File

@@ -0,0 +1,177 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
use crate::errors::*;
use nix::errno::Errno;
use nix::unistd;
use nix::Error;
use std::mem;
use std::os::unix::io::RawFd;
pub const SYNC_SUCCESS: i32 = 1;
pub const SYNC_FAILED: i32 = 2;
pub const SYNC_DATA: i32 = 3;
const DATA_SIZE: usize = 100;
const MSG_SIZE: usize = mem::size_of::<i32>();
#[macro_export]
macro_rules! log_child {
($fd:expr, $($arg:tt)+) => ({
let lfd = $fd;
let mut log_str = format_args!($($arg)+).to_string();
log_str.push('\n');
write_count(lfd, log_str.as_bytes(), log_str.len());
})
}
pub fn write_count(fd: RawFd, buf: &[u8], count: usize) -> Result<usize> {
let mut len = 0;
loop {
match unistd::write(fd, &buf[len..]) {
Ok(l) => {
len += l;
if len == count {
break;
}
}
Err(e) => {
if e != Error::from_errno(Errno::EINTR) {
return Err(e.into());
}
}
}
}
Ok(len)
}
fn read_count(fd: RawFd, count: usize) -> Result<Vec<u8>> {
let mut v: Vec<u8> = vec![0; count];
let mut len = 0;
loop {
match unistd::read(fd, &mut v[len..]) {
Ok(l) => {
len += l;
if len == count || l == 0 {
break;
}
}
Err(e) => {
if e != Error::from_errno(Errno::EINTR) {
return Err(e.into());
}
}
}
}
Ok(v[0..len].to_vec())
}
pub fn read_sync(fd: RawFd) -> Result<Vec<u8>> {
let buf = read_count(fd, MSG_SIZE)?;
if buf.len() != MSG_SIZE {
return Err(ErrorKind::ErrorCode(format!(
"process: {} failed to receive sync message from peer: got msg length: {}, expected: {}",
std::process::id(),
buf.len(),
MSG_SIZE
))
.into());
}
let buf_array: [u8; MSG_SIZE] = [buf[0], buf[1], buf[2], buf[3]];
let msg: i32 = i32::from_be_bytes(buf_array);
match msg {
SYNC_SUCCESS => return Ok(Vec::new()),
SYNC_DATA => {
let buf = read_count(fd, MSG_SIZE)?;
let buf_array: [u8; MSG_SIZE] = [buf[0], buf[1], buf[2], buf[3]];
let msg_length: i32 = i32::from_be_bytes(buf_array);
let data_buf = read_count(fd, msg_length as usize)?;
return Ok(data_buf);
}
SYNC_FAILED => {
let mut error_buf = vec![];
loop {
let buf = read_count(fd, DATA_SIZE)?;
error_buf.extend(&buf);
if DATA_SIZE == buf.len() {
continue;
} else {
break;
}
}
let error_str = match std::str::from_utf8(&error_buf) {
Ok(v) => v,
Err(e) => {
return Err(ErrorKind::ErrorCode(format!(
"receive error message from child process failed: {:?}",
e
))
.into())
}
};
return Err(ErrorKind::ErrorCode(String::from(error_str)).into());
}
_ => return Err(ErrorKind::ErrorCode("error in receive sync message".to_string()).into()),
}
}
pub fn write_sync(fd: RawFd, msg_type: i32, data_str: &str) -> Result<()> {
let buf = msg_type.to_be_bytes();
let count = write_count(fd, &buf, MSG_SIZE)?;
if count != MSG_SIZE {
return Err(ErrorKind::ErrorCode("error in send sync message".to_string()).into());
}
match msg_type {
SYNC_FAILED => match write_count(fd, data_str.as_bytes(), data_str.len()) {
Ok(_count) => unistd::close(fd)?,
Err(e) => {
unistd::close(fd)?;
return Err(
ErrorKind::ErrorCode("error in send message to process".to_string()).into(),
);
}
},
SYNC_DATA => {
let length: i32 = data_str.len() as i32;
match write_count(fd, &length.to_be_bytes(), MSG_SIZE) {
Ok(_count) => (),
Err(e) => {
unistd::close(fd)?;
return Err(ErrorKind::ErrorCode(
"error in send message to process".to_string(),
)
.into());
}
}
match write_count(fd, data_str.as_bytes(), data_str.len()) {
Ok(_count) => (),
Err(e) => {
unistd::close(fd)?;
return Err(ErrorKind::ErrorCode(
"error in send message to process".to_string(),
)
.into());
}
}
}
_ => (),
};
Ok(())
}

View File

@@ -0,0 +1,309 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
use crate::container::Config;
use crate::errors::*;
use lazy_static;
use nix::errno::Errno;
use nix::Error;
use oci::{LinuxIDMapping, LinuxNamespace, Spec};
use protobuf::RepeatedField;
use std::collections::HashMap;
use std::path::{Component, PathBuf};
fn contain_namespace(nses: &Vec<LinuxNamespace>, key: &str) -> bool {
for ns in nses {
if ns.r#type.as_str() == key {
return true;
}
}
false
}
fn get_namespace_path(nses: &Vec<LinuxNamespace>, key: &str) -> Result<String> {
for ns in nses {
if ns.r#type.as_str() == key {
return Ok(ns.path.clone());
}
}
Err(ErrorKind::Nix(Error::from_errno(Errno::EINVAL)).into())
}
fn rootfs(root: &str) -> Result<()> {
let path = PathBuf::from(root);
// not absolute path or not exists
if !path.exists() || !path.is_absolute() {
return Err(ErrorKind::Nix(Error::from_errno(Errno::EINVAL)).into());
}
// symbolic link? ..?
let mut stack: Vec<String> = Vec::new();
for c in path.components() {
if stack.is_empty() {
if c == Component::RootDir || c == Component::ParentDir {
continue;
}
}
if c == Component::ParentDir {
stack.pop();
continue;
}
stack.push(c.as_os_str().to_str().unwrap().to_string());
}
let mut cleaned = PathBuf::from("/");
for e in stack.iter() {
cleaned.push(e);
}
let canon = path.canonicalize()?;
if cleaned != canon {
// There is symbolic in path
return Err(ErrorKind::Nix(Error::from_errno(Errno::EINVAL)).into());
}
Ok(())
}
fn network(_oci: &Spec) -> Result<()> {
Ok(())
}
fn hostname(oci: &Spec) -> Result<()> {
if oci.hostname.is_empty() || oci.hostname == "".to_string() {
return Ok(());
}
if oci.linux.is_none() {
return Err(ErrorKind::Nix(Error::from_errno(Errno::EINVAL)).into());
}
let linux = oci.linux.as_ref().unwrap();
if !contain_namespace(&linux.namespaces, "uts") {
return Err(ErrorKind::Nix(Error::from_errno(Errno::EINVAL)).into());
}
Ok(())
}
fn security(oci: &Spec) -> Result<()> {
let linux = oci.linux.as_ref().unwrap();
if linux.masked_paths.len() == 0 && linux.readonly_paths.len() == 0 {
return Ok(());
}
if !contain_namespace(&linux.namespaces, "mount") {
return Err(ErrorKind::Nix(Error::from_errno(Errno::EINVAL)).into());
}
// don't care about selinux at present
Ok(())
}
fn idmapping(maps: &Vec<LinuxIDMapping>) -> Result<()> {
for map in maps {
if map.size > 0 {
return Ok(());
}
}
Err(ErrorKind::Nix(Error::from_errno(Errno::EINVAL)).into())
}
fn usernamespace(oci: &Spec) -> Result<()> {
let linux = oci.linux.as_ref().unwrap();
if contain_namespace(&linux.namespaces, "user") {
let user_ns = PathBuf::from("/proc/self/ns/user");
if !user_ns.exists() {
return Err(ErrorKind::ErrorCode("user namespace not supported!".to_string()).into());
}
// check if idmappings is correct, at least I saw idmaps
// with zero size was passed to agent
idmapping(&linux.uid_mappings)?;
idmapping(&linux.gid_mappings)?;
} else {
// no user namespace but idmap
if linux.uid_mappings.len() != 0 || linux.gid_mappings.len() != 0 {
return Err(ErrorKind::Nix(Error::from_errno(Errno::EINVAL)).into());
}
}
Ok(())
}
fn cgroupnamespace(oci: &Spec) -> Result<()> {
let linux = oci.linux.as_ref().unwrap();
if contain_namespace(&linux.namespaces, "cgroup") {
let path = PathBuf::from("/proc/self/ns/cgroup");
if !path.exists() {
return Err(ErrorKind::ErrorCode("cgroup unsupported!".to_string()).into());
}
}
Ok(())
}
lazy_static! {
pub static ref SYSCTLS: HashMap<&'static str, bool> = {
let mut m = HashMap::new();
m.insert("kernel.msgmax", true);
m.insert("kernel.msgmnb", true);
m.insert("kernel.msgmni", true);
m.insert("kernel.sem", true);
m.insert("kernel.shmall", true);
m.insert("kernel.shmmax", true);
m.insert("kernel.shmmni", true);
m.insert("kernel.shm_rmid_forced", true);
m
};
}
fn check_host_ns(path: &str) -> Result<()> {
let cpath = PathBuf::from(path);
let hpath = PathBuf::from("/proc/self/ns/net");
let real_hpath = hpath.read_link()?;
let meta = cpath.symlink_metadata()?;
let file_type = meta.file_type();
if !file_type.is_symlink() {
return Ok(());
}
let real_cpath = cpath.read_link()?;
if real_cpath == real_hpath {
return Err(ErrorKind::Nix(Error::from_errno(Errno::EINVAL)).into());
}
Ok(())
}
fn sysctl(oci: &Spec) -> Result<()> {
let linux = oci.linux.as_ref().unwrap();
for (key, _) in linux.sysctl.iter() {
if SYSCTLS.contains_key(key.as_str()) || key.starts_with("fs.mqueue.") {
if contain_namespace(&linux.namespaces, "ipc") {
continue;
} else {
return Err(ErrorKind::Nix(Error::from_errno(Errno::EINVAL)).into());
}
}
if key.starts_with("net.") {
if !contain_namespace(&linux.namespaces, "network") {
return Err(ErrorKind::Nix(Error::from_errno(Errno::EINVAL)).into());
}
let net = get_namespace_path(&linux.namespaces, "network")?;
if net.is_empty() || net == "".to_string() {
continue;
}
check_host_ns(net.as_str())?;
}
if contain_namespace(&linux.namespaces, "uts") {
if key == "kernel.domainname" {
continue;
}
if key == "kernel.hostname" {
return Err(ErrorKind::Nix(Error::from_errno(Errno::EINVAL)).into());
}
}
return Err(ErrorKind::Nix(Error::from_errno(Errno::EINVAL)).into());
}
Ok(())
}
fn rootless_euid_mapping(oci: &Spec) -> Result<()> {
let linux = oci.linux.as_ref().unwrap();
if !contain_namespace(&linux.namespaces, "user") {
return Err(ErrorKind::Nix(Error::from_errno(Errno::EINVAL)).into());
}
if linux.gid_mappings.len() == 0 || linux.gid_mappings.len() == 0 {
return Err(ErrorKind::Nix(Error::from_errno(Errno::EINVAL)).into());
}
Ok(())
}
fn has_idmapping(maps: &Vec<LinuxIDMapping>, id: u32) -> bool {
for map in maps {
if id >= map.container_id && id < map.container_id + map.size {
return true;
}
}
false
}
fn rootless_euid_mount(oci: &Spec) -> Result<()> {
let linux = oci.linux.as_ref().unwrap();
for mnt in oci.mounts.iter() {
for opt in mnt.options.iter() {
if opt.starts_with("uid=") || opt.starts_with("gid=") {
let fields: Vec<&str> = opt.split('=').collect();
if fields.len() != 2 {
return Err(ErrorKind::Nix(Error::from_errno(Errno::EINVAL)).into());
}
let id = fields[1].trim().parse::<u32>()?;
if opt.starts_with("uid=") {
if !has_idmapping(&linux.uid_mappings, id) {
return Err(ErrorKind::Nix(Error::from_errno(Errno::EINVAL)).into());
}
}
if opt.starts_with("gid=") {
if !has_idmapping(&linux.gid_mappings, id) {
return Err(ErrorKind::Nix(Error::from_errno(Errno::EINVAL)).into());
}
}
}
}
}
Ok(())
}
fn rootless_euid(oci: &Spec) -> Result<()> {
rootless_euid_mapping(oci)?;
rootless_euid_mount(oci)?;
Ok(())
}
pub fn validate(conf: &Config) -> Result<()> {
lazy_static::initialize(&SYSCTLS);
let oci = conf.spec.as_ref().unwrap();
if oci.linux.is_none() {
return Err(ErrorKind::Nix(Error::from_errno(Errno::EINVAL)).into());
}
if oci.root.is_none() {
return Err(ErrorKind::Nix(Error::from_errno(Errno::EINVAL)).into());
}
let root = oci.root.as_ref().unwrap().path.as_str();
rootfs(root)?;
network(oci)?;
hostname(oci)?;
security(oci)?;
usernamespace(oci)?;
cgroupnamespace(oci)?;
sysctl(&oci)?;
if conf.rootless_euid {
rootless_euid(oci)?;
}
Ok(())
}

881
src/agent/src/config.rs Normal file
View File

@@ -0,0 +1,881 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
use rustjail::errors::*;
use std::fs;
use std::time;
const DEBUG_CONSOLE_FLAG: &str = "agent.debug_console";
const DEV_MODE_FLAG: &str = "agent.devmode";
const LOG_LEVEL_OPTION: &str = "agent.log";
const HOTPLUG_TIMOUT_OPTION: &str = "agent.hotplug_timeout";
const DEBUG_CONSOLE_VPORT_OPTION: &str = "agent.debug_console_vport";
const LOG_VPORT_OPTION: &str = "agent.log_vport";
const CONTAINER_PIPE_SIZE_OPTION: &str = "agent.container_pipe_size";
const DEFAULT_LOG_LEVEL: slog::Level = slog::Level::Info;
const DEFAULT_HOTPLUG_TIMEOUT: time::Duration = time::Duration::from_secs(3);
const DEFAULT_CONTAINER_PIPE_SIZE: i32 = 0;
// FIXME: unused
const TRACE_MODE_FLAG: &str = "agent.trace";
const USE_VSOCK_FLAG: &str = "agent.use_vsock";
#[derive(Debug)]
pub struct agentConfig {
pub debug_console: bool,
pub dev_mode: bool,
pub log_level: slog::Level,
pub hotplug_timeout: time::Duration,
pub debug_console_vport: i32,
pub log_vport: i32,
pub container_pipe_size: i32,
}
impl agentConfig {
pub fn new() -> agentConfig {
agentConfig {
debug_console: false,
dev_mode: false,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
debug_console_vport: 0,
log_vport: 0,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
}
}
pub fn parse_cmdline(&mut self, file: &str) -> Result<()> {
let cmdline = fs::read_to_string(file)?;
let params: Vec<&str> = cmdline.split_ascii_whitespace().collect();
for param in params.iter() {
if param.eq(&DEBUG_CONSOLE_FLAG) {
self.debug_console = true;
}
if param.eq(&DEV_MODE_FLAG) {
self.dev_mode = true;
}
if param.starts_with(format!("{}=", LOG_LEVEL_OPTION).as_str()) {
let level = get_log_level(param)?;
self.log_level = level;
}
if param.starts_with(format!("{}=", HOTPLUG_TIMOUT_OPTION).as_str()) {
let hotplugTimeout = get_hotplug_timeout(param)?;
// ensure the timeout is a positive value
if hotplugTimeout.as_secs() > 0 {
self.hotplug_timeout = hotplugTimeout;
}
}
if param.starts_with(format!("{}=", DEBUG_CONSOLE_VPORT_OPTION).as_str()) {
let port = get_vsock_port(param)?;
if port > 0 {
self.debug_console_vport = port;
}
}
if param.starts_with(format!("{}=", LOG_VPORT_OPTION).as_str()) {
let port = get_vsock_port(param)?;
if port > 0 {
self.log_vport = port;
}
}
if param.starts_with(format!("{}=", CONTAINER_PIPE_SIZE_OPTION).as_str()) {
let container_pipe_size = get_container_pipe_size(param)?;
self.container_pipe_size = container_pipe_size
}
}
Ok(())
}
}
fn get_vsock_port(p: &str) -> Result<i32> {
let fields: Vec<&str> = p.split("=").collect();
if fields.len() != 2 {
return Err(ErrorKind::ErrorCode("invalid port parameter".to_string()).into());
}
Ok(fields[1].parse::<i32>()?)
}
// Map logrus (https://godoc.org/github.com/sirupsen/logrus)
// log level to the equivalent slog log levels.
//
// Note: Logrus names are used for compatability with the previous
// golang-based agent.
fn logrus_to_slog_level(logrus_level: &str) -> Result<slog::Level> {
let level = match logrus_level {
// Note: different semantics to logrus: log, but don't panic.
"fatal" | "panic" => slog::Level::Critical,
"critical" => slog::Level::Critical,
"error" => slog::Level::Error,
"warn" | "warning" => slog::Level::Warning,
"info" => slog::Level::Info,
"debug" => slog::Level::Debug,
// Not in logrus
"trace" => slog::Level::Trace,
_ => {
return Err(ErrorKind::ErrorCode(String::from("invalid log level")).into());
}
};
Ok(level)
}
fn get_log_level(param: &str) -> Result<slog::Level> {
let fields: Vec<&str> = param.split("=").collect();
if fields.len() != 2 {
return Err(ErrorKind::ErrorCode(String::from("invalid log level parameter")).into());
}
if fields[0] != LOG_LEVEL_OPTION {
Err(ErrorKind::ErrorCode(String::from("invalid log level key name")).into())
} else {
Ok(logrus_to_slog_level(fields[1])?)
}
}
fn get_hotplug_timeout(param: &str) -> Result<time::Duration> {
let fields: Vec<&str> = param.split("=").collect();
if fields.len() != 2 {
return Err(ErrorKind::ErrorCode(String::from("invalid hotplug timeout parameter")).into());
}
let key = fields[0];
if key != HOTPLUG_TIMOUT_OPTION {
return Err(ErrorKind::ErrorCode(String::from("invalid hotplug timeout key name")).into());
}
let value = fields[1].parse::<u64>();
if value.is_err() {
return Err(ErrorKind::ErrorCode(String::from("unable to parse hotplug timeout")).into());
}
Ok(time::Duration::from_secs(value.unwrap()))
}
fn get_container_pipe_size(param: &str) -> Result<i32> {
let fields: Vec<&str> = param.split("=").collect();
if fields.len() != 2 {
return Err(
ErrorKind::ErrorCode(String::from("invalid container pipe size parameter")).into(),
);
}
let key = fields[0];
if key != CONTAINER_PIPE_SIZE_OPTION {
return Err(
ErrorKind::ErrorCode(String::from("invalid container pipe size key name")).into(),
);
}
let res = fields[1].parse::<i32>();
if res.is_err() {
return Err(
ErrorKind::ErrorCode(String::from("unable to parse container pipe size")).into(),
);
}
let value = res.unwrap();
if value < 0 {
return Err(ErrorKind::ErrorCode(String::from(
"container pipe size should not be negative",
))
.into());
}
Ok(value)
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::File;
use std::io::Write;
use std::time;
use tempfile::tempdir;
const ERR_INVALID_LOG_LEVEL: &str = "invalid log level";
const ERR_INVALID_LOG_LEVEL_PARAM: &str = "invalid log level parameter";
const ERR_INVALID_LOG_LEVEL_KEY: &str = "invalid log level key name";
const ERR_INVALID_HOTPLUG_TIMEOUT: &str = "invalid hotplug timeout parameter";
const ERR_INVALID_HOTPLUG_TIMEOUT_PARAM: &str = "unable to parse hotplug timeout";
const ERR_INVALID_HOTPLUG_TIMEOUT_KEY: &str = "invalid hotplug timeout key name";
const ERR_INVALID_CONTAINER_PIPE_SIZE: &str = "invalid container pipe size parameter";
const ERR_INVALID_CONTAINER_PIPE_SIZE_PARAM: &str = "unable to parse container pipe size";
const ERR_INVALID_CONTAINER_PIPE_SIZE_KEY: &str = "invalid container pipe size key name";
const ERR_INVALID_CONTAINER_PIPE_NEGATIVE: &str = "container pipe size should not be negative";
// helper function to make errors less crazy-long
fn make_err(desc: &str) -> Error {
ErrorKind::ErrorCode(desc.to_string()).into()
}
// Parameters:
//
// 1: expected Result
// 2: actual Result
// 3: string used to identify the test on error
macro_rules! assert_result {
($expected_result:expr, $actual_result:expr, $msg:expr) => {
if $expected_result.is_ok() {
let expected_level = $expected_result.as_ref().unwrap();
let actual_level = $actual_result.unwrap();
assert!(*expected_level == actual_level, $msg);
} else {
let expected_error = $expected_result.as_ref().unwrap_err();
let actual_error = $actual_result.unwrap_err();
let expected_error_msg = format!("{:?}", expected_error);
let actual_error_msg = format!("{:?}", actual_error);
assert!(expected_error_msg == actual_error_msg, $msg);
}
};
}
#[test]
fn test_new() {
let config = agentConfig::new();
assert_eq!(config.debug_console, false);
assert_eq!(config.dev_mode, false);
assert_eq!(config.log_level, DEFAULT_LOG_LEVEL);
assert_eq!(config.hotplug_timeout, DEFAULT_HOTPLUG_TIMEOUT);
}
#[test]
fn test_parse_cmdline() {
#[derive(Debug)]
struct TestData<'a> {
contents: &'a str,
debug_console: bool,
dev_mode: bool,
log_level: slog::Level,
hotplug_timeout: time::Duration,
container_pipe_size: i32,
}
let tests = &[
TestData {
contents: "agent.debug_consolex agent.devmode",
debug_console: false,
dev_mode: true,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "agent.debug_console agent.devmodex",
debug_console: true,
dev_mode: false,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "agent.logx=debug",
debug_console: false,
dev_mode: false,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "agent.log=debug",
debug_console: false,
dev_mode: false,
log_level: slog::Level::Debug,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "",
debug_console: false,
dev_mode: false,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "foo",
debug_console: false,
dev_mode: false,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "foo bar",
debug_console: false,
dev_mode: false,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "foo bar",
debug_console: false,
dev_mode: false,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "foo agent bar",
debug_console: false,
dev_mode: false,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "foo debug_console agent bar devmode",
debug_console: false,
dev_mode: false,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "agent.debug_console",
debug_console: true,
dev_mode: false,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: " agent.debug_console ",
debug_console: true,
dev_mode: false,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "agent.debug_console foo",
debug_console: true,
dev_mode: false,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: " agent.debug_console foo",
debug_console: true,
dev_mode: false,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "foo agent.debug_console bar",
debug_console: true,
dev_mode: false,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "foo agent.debug_console",
debug_console: true,
dev_mode: false,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "foo agent.debug_console ",
debug_console: true,
dev_mode: false,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "agent.devmode",
debug_console: false,
dev_mode: true,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: " agent.devmode ",
debug_console: false,
dev_mode: true,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "agent.devmode foo",
debug_console: false,
dev_mode: true,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: " agent.devmode foo",
debug_console: false,
dev_mode: true,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "foo agent.devmode bar",
debug_console: false,
dev_mode: true,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "foo agent.devmode",
debug_console: false,
dev_mode: true,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "foo agent.devmode ",
debug_console: false,
dev_mode: true,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "agent.devmode agent.debug_console",
debug_console: true,
dev_mode: true,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "agent.devmode agent.debug_console agent.hotplug_timeout=100",
debug_console: true,
dev_mode: true,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: time::Duration::from_secs(100),
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "agent.devmode agent.debug_console agent.hotplug_timeout=0",
debug_console: true,
dev_mode: true,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "agent.devmode agent.debug_console agent.container_pipe_size=2097152",
debug_console: true,
dev_mode: true,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: 2097152,
},
TestData {
contents: "agent.devmode agent.debug_console agent.container_pipe_size=100",
debug_console: true,
dev_mode: true,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: 100,
},
TestData {
contents: "agent.devmode agent.debug_console agent.container_pipe_size=0",
debug_console: true,
dev_mode: true,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
TestData {
contents: "agent.devmode agent.debug_console agent.container_pip_siz=100",
debug_console: true,
dev_mode: true,
log_level: DEFAULT_LOG_LEVEL,
hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT,
container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE,
},
];
let dir = tempdir().expect("failed to create tmpdir");
// First, check a missing file is handled
let file_path = dir.path().join("enoent");
let filename = file_path.to_str().expect("failed to create filename");
let mut config = agentConfig::new();
let result = config.parse_cmdline(&filename.to_owned());
assert!(result.is_err());
// Now, test various combinations of file contents
for (i, d) in tests.iter().enumerate() {
let msg = format!("test[{}]: {:?}", i, d);
let file_path = dir.path().join("cmdline");
let filename = file_path.to_str().expect("failed to create filename");
let mut file =
File::create(filename).expect(&format!("{}: failed to create file", msg));
file.write_all(d.contents.as_bytes())
.expect(&format!("{}: failed to write file contents", msg));
let mut config = agentConfig::new();
assert_eq!(config.debug_console, false, "{}", msg);
assert_eq!(config.dev_mode, false, "{}", msg);
assert_eq!(
config.hotplug_timeout,
time::Duration::from_secs(3),
"{}",
msg
);
assert_eq!(config.container_pipe_size, 0, "{}", msg);
let result = config.parse_cmdline(filename);
assert!(result.is_ok(), "{}", msg);
assert_eq!(d.debug_console, config.debug_console, "{}", msg);
assert_eq!(d.dev_mode, config.dev_mode, "{}", msg);
assert_eq!(d.log_level, config.log_level, "{}", msg);
assert_eq!(d.hotplug_timeout, config.hotplug_timeout, "{}", msg);
assert_eq!(d.container_pipe_size, config.container_pipe_size, "{}", msg);
}
}
#[test]
fn test_logrus_to_slog_level() {
#[derive(Debug)]
struct TestData<'a> {
logrus_level: &'a str,
result: Result<slog::Level>,
}
let tests = &[
TestData {
logrus_level: "",
result: Err(make_err(ERR_INVALID_LOG_LEVEL)),
},
TestData {
logrus_level: "foo",
result: Err(make_err(ERR_INVALID_LOG_LEVEL)),
},
TestData {
logrus_level: "debugging",
result: Err(make_err(ERR_INVALID_LOG_LEVEL)),
},
TestData {
logrus_level: "xdebug",
result: Err(make_err(ERR_INVALID_LOG_LEVEL)),
},
TestData {
logrus_level: "trace",
result: Ok(slog::Level::Trace),
},
TestData {
logrus_level: "debug",
result: Ok(slog::Level::Debug),
},
TestData {
logrus_level: "info",
result: Ok(slog::Level::Info),
},
TestData {
logrus_level: "warn",
result: Ok(slog::Level::Warning),
},
TestData {
logrus_level: "warning",
result: Ok(slog::Level::Warning),
},
TestData {
logrus_level: "error",
result: Ok(slog::Level::Error),
},
TestData {
logrus_level: "critical",
result: Ok(slog::Level::Critical),
},
TestData {
logrus_level: "fatal",
result: Ok(slog::Level::Critical),
},
TestData {
logrus_level: "panic",
result: Ok(slog::Level::Critical),
},
];
for (i, d) in tests.iter().enumerate() {
let msg = format!("test[{}]: {:?}", i, d);
let result = logrus_to_slog_level(d.logrus_level);
let msg = format!("{}: result: {:?}", msg, result);
assert_result!(d.result, result, format!("{}", msg));
}
}
#[test]
fn test_get_log_level() {
#[derive(Debug)]
struct TestData<'a> {
param: &'a str,
result: Result<slog::Level>,
}
let tests = &[
TestData {
param: "",
result: Err(make_err(ERR_INVALID_LOG_LEVEL_PARAM)),
},
TestData {
param: "=",
result: Err(make_err(ERR_INVALID_LOG_LEVEL_KEY)),
},
TestData {
param: "x=",
result: Err(make_err(ERR_INVALID_LOG_LEVEL_KEY)),
},
TestData {
param: "=y",
result: Err(make_err(ERR_INVALID_LOG_LEVEL_KEY)),
},
TestData {
param: "==",
result: Err(make_err(ERR_INVALID_LOG_LEVEL_PARAM)),
},
TestData {
param: "= =",
result: Err(make_err(ERR_INVALID_LOG_LEVEL_PARAM)),
},
TestData {
param: "x=y",
result: Err(make_err(ERR_INVALID_LOG_LEVEL_KEY)),
},
TestData {
param: "agent=debug",
result: Err(make_err(ERR_INVALID_LOG_LEVEL_KEY)),
},
TestData {
param: "agent.logg=debug",
result: Err(make_err(ERR_INVALID_LOG_LEVEL_KEY)),
},
TestData {
param: "agent.log=trace",
result: Ok(slog::Level::Trace),
},
TestData {
param: "agent.log=debug",
result: Ok(slog::Level::Debug),
},
TestData {
param: "agent.log=info",
result: Ok(slog::Level::Info),
},
TestData {
param: "agent.log=warn",
result: Ok(slog::Level::Warning),
},
TestData {
param: "agent.log=warning",
result: Ok(slog::Level::Warning),
},
TestData {
param: "agent.log=error",
result: Ok(slog::Level::Error),
},
TestData {
param: "agent.log=critical",
result: Ok(slog::Level::Critical),
},
TestData {
param: "agent.log=fatal",
result: Ok(slog::Level::Critical),
},
TestData {
param: "agent.log=panic",
result: Ok(slog::Level::Critical),
},
];
for (i, d) in tests.iter().enumerate() {
let msg = format!("test[{}]: {:?}", i, d);
let result = get_log_level(d.param);
let msg = format!("{}: result: {:?}", msg, result);
assert_result!(d.result, result, format!("{}", msg));
}
}
#[test]
fn test_get_hotplug_timeout() {
#[derive(Debug)]
struct TestData<'a> {
param: &'a str,
result: Result<time::Duration>,
}
let tests = &[
TestData {
param: "",
result: Err(make_err(ERR_INVALID_HOTPLUG_TIMEOUT)),
},
TestData {
param: "agent.hotplug_timeout",
result: Err(make_err(ERR_INVALID_HOTPLUG_TIMEOUT)),
},
TestData {
param: "foo=bar",
result: Err(make_err(ERR_INVALID_HOTPLUG_TIMEOUT_KEY)),
},
TestData {
param: "agent.hotplug_timeot=1",
result: Err(make_err(ERR_INVALID_HOTPLUG_TIMEOUT_KEY)),
},
TestData {
param: "agent.hotplug_timeout=1",
result: Ok(time::Duration::from_secs(1)),
},
TestData {
param: "agent.hotplug_timeout=3",
result: Ok(time::Duration::from_secs(3)),
},
TestData {
param: "agent.hotplug_timeout=3600",
result: Ok(time::Duration::from_secs(3600)),
},
TestData {
param: "agent.hotplug_timeout=0",
result: Ok(time::Duration::from_secs(0)),
},
TestData {
param: "agent.hotplug_timeout=-1",
result: Err(make_err(ERR_INVALID_HOTPLUG_TIMEOUT_PARAM)),
},
TestData {
param: "agent.hotplug_timeout=4jbsdja",
result: Err(make_err(ERR_INVALID_HOTPLUG_TIMEOUT_PARAM)),
},
TestData {
param: "agent.hotplug_timeout=foo",
result: Err(make_err(ERR_INVALID_HOTPLUG_TIMEOUT_PARAM)),
},
TestData {
param: "agent.hotplug_timeout=j",
result: Err(make_err(ERR_INVALID_HOTPLUG_TIMEOUT_PARAM)),
},
];
for (i, d) in tests.iter().enumerate() {
let msg = format!("test[{}]: {:?}", i, d);
let result = get_hotplug_timeout(d.param);
let msg = format!("{}: result: {:?}", msg, result);
assert_result!(d.result, result, format!("{}", msg));
}
}
#[test]
fn test_get_container_pipe_size() {
#[derive(Debug)]
struct TestData<'a> {
param: &'a str,
result: Result<i32>,
}
let tests = &[
TestData {
param: "",
result: Err(make_err(ERR_INVALID_CONTAINER_PIPE_SIZE)),
},
TestData {
param: "agent.container_pipe_size",
result: Err(make_err(ERR_INVALID_CONTAINER_PIPE_SIZE)),
},
TestData {
param: "foo=bar",
result: Err(make_err(ERR_INVALID_CONTAINER_PIPE_SIZE_KEY)),
},
TestData {
param: "agent.container_pip_siz=1",
result: Err(make_err(ERR_INVALID_CONTAINER_PIPE_SIZE_KEY)),
},
TestData {
param: "agent.container_pipe_size=1",
result: Ok(1),
},
TestData {
param: "agent.container_pipe_size=3",
result: Ok(3),
},
TestData {
param: "agent.container_pipe_size=2097152",
result: Ok(2097152),
},
TestData {
param: "agent.container_pipe_size=0",
result: Ok(0),
},
TestData {
param: "agent.container_pipe_size=-1",
result: Err(make_err(ERR_INVALID_CONTAINER_PIPE_NEGATIVE)),
},
TestData {
param: "agent.container_pipe_size=foobar",
result: Err(make_err(ERR_INVALID_CONTAINER_PIPE_SIZE_PARAM)),
},
TestData {
param: "agent.container_pipe_size=j",
result: Err(make_err(ERR_INVALID_CONTAINER_PIPE_SIZE_PARAM)),
},
TestData {
param: "agent.container_pipe_size=4jbsdja",
result: Err(make_err(ERR_INVALID_CONTAINER_PIPE_SIZE_PARAM)),
},
TestData {
param: "agent.container_pipe_size=4294967296",
result: Err(make_err(ERR_INVALID_CONTAINER_PIPE_SIZE_PARAM)),
},
];
for (i, d) in tests.iter().enumerate() {
let msg = format!("test[{}]: {:?}", i, d);
let result = get_container_pipe_size(d.param);
let msg = format!("{}: result: {:?}", msg, result);
assert_result!(d.result, result, format!("{}", msg));
}
}
}

362
src/agent/src/device.rs Normal file
View File

@@ -0,0 +1,362 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
use libc::{c_uint, major, minor};
use std::collections::HashMap;
use std::fs;
use std::os::unix::fs::MetadataExt;
use std::path::Path;
use std::sync::{mpsc, Arc, Mutex};
use crate::linux_abi::*;
use crate::mount::{DRIVERBLKTYPE, DRIVERMMIOBLKTYPE, DRIVERNVDIMMTYPE, DRIVERSCSITYPE};
use crate::sandbox::Sandbox;
use crate::{AGENT_CONFIG, GLOBAL_DEVICE_WATCHER};
use oci::Spec;
use protocols::agent::Device;
use rustjail::errors::*;
// Convenience macro to obtain the scope logger
macro_rules! sl {
() => {
slog_scope::logger().new(o!("subsystem" => "device"))
};
}
// DeviceHandler is the type of callback to be defined to handle every type of device driver.
type DeviceHandler = fn(&Device, &mut Spec, &Arc<Mutex<Sandbox>>) -> Result<()>;
// DeviceHandlerList lists the supported drivers.
#[cfg_attr(rustfmt, rustfmt_skip)]
lazy_static! {
static ref DEVICEHANDLERLIST: HashMap<&'static str, DeviceHandler> = {
let mut m: HashMap<&'static str, DeviceHandler> = HashMap::new();
m.insert(DRIVERBLKTYPE, virtio_blk_device_handler);
m.insert(DRIVERMMIOBLKTYPE, virtiommio_blk_device_handler);
m.insert(DRIVERNVDIMMTYPE, virtio_nvdimm_device_handler);
m.insert(DRIVERSCSITYPE, virtio_scsi_device_handler);
m
};
}
pub fn rescan_pci_bus() -> Result<()> {
online_device(SYSFS_PCI_BUS_RESCAN_FILE)
}
pub fn online_device(path: &str) -> Result<()> {
fs::write(path, "1")?;
Ok(())
}
// get_pci_device_address fetches the complete PCI address in sysfs, based on the PCI
// identifier provided. This should be in the format: "bridgeAddr/deviceAddr".
// Here, bridgeAddr is the address at which the bridge is attached on the root bus,
// while deviceAddr is the address at which the device is attached on the bridge.
fn get_pci_device_address(pci_id: &str) -> Result<String> {
let tokens: Vec<&str> = pci_id.split("/").collect();
if tokens.len() != 2 {
return Err(ErrorKind::ErrorCode(format!(
"PCI Identifier for device should be of format [bridgeAddr/deviceAddr], got {}",
pci_id
))
.into());
}
let bridge_id = tokens[0];
let device_id = tokens[1];
// Deduce the complete bridge address based on the bridge address identifier passed
// and the fact that bridges are attached on the main bus with function 0.
let pci_bridge_addr = format!("0000:00:{}.0", bridge_id);
// Find out the bus exposed by bridge
let bridge_bus_path = format!("{}/{}/pci_bus/", SYSFS_PCI_BUS_PREFIX, pci_bridge_addr);
let files_slice: Vec<_> = fs::read_dir(&bridge_bus_path)
.unwrap()
.map(|res| res.unwrap().path())
.collect();
let bus_num = files_slice.len();
if bus_num != 1 {
return Err(ErrorKind::ErrorCode(format!(
"Expected an entry for bus in {}, got {} entries instead",
bridge_bus_path, bus_num
))
.into());
}
let bus = files_slice[0].file_name().unwrap().to_str().unwrap();
// Device address is based on the bus of the bridge to which it is attached.
// We do not pass devices as multifunction, hence the trailing 0 in the address.
let pci_device_addr = format!("{}:{}.0", bus, device_id);
let bridge_device_pci_addr = format!("{}/{}", pci_bridge_addr, pci_device_addr);
info!(
sl!(),
"Fetched PCI address for device PCIAddr:{}\n", bridge_device_pci_addr
);
Ok(bridge_device_pci_addr)
}
fn get_device_name(sandbox: &Arc<Mutex<Sandbox>>, dev_addr: &str) -> Result<String> {
// Keep the same lock order as uevent::handle_block_add_event(), otherwise it may cause deadlock.
let mut w = GLOBAL_DEVICE_WATCHER.lock().unwrap();
let sb = sandbox.lock().unwrap();
for (key, value) in sb.pci_device_map.iter() {
if key.contains(dev_addr) {
info!(sl!(), "Device {} found in pci device map", dev_addr);
return Ok(format!("{}/{}", SYSTEM_DEV_PATH, value));
}
}
drop(sb);
// If device is not found in the device map, hotplug event has not
// been received yet, create and add channel to the watchers map.
// The key of the watchers map is the device we are interested in.
// Note this is done inside the lock, not to miss any events from the
// global udev listener.
let (tx, rx) = mpsc::channel::<String>();
w.insert(dev_addr.to_string(), tx);
drop(w);
info!(sl!(), "Waiting on channel for device notification\n");
let hotplug_timeout = AGENT_CONFIG.read().unwrap().hotplug_timeout;
let dev_name = match rx.recv_timeout(hotplug_timeout) {
Ok(name) => name,
Err(_) => {
GLOBAL_DEVICE_WATCHER.lock().unwrap().remove_entry(dev_addr);
return Err(ErrorKind::ErrorCode(format!(
"Timeout reached after {:?} waiting for device {}",
hotplug_timeout, dev_addr
))
.into());
}
};
Ok(format!("{}/{}", SYSTEM_DEV_PATH, &dev_name))
}
pub fn get_scsi_device_name(sandbox: &Arc<Mutex<Sandbox>>, scsi_addr: &str) -> Result<String> {
let dev_sub_path = format!("{}{}/{}", SCSI_HOST_CHANNEL, scsi_addr, SCSI_BLOCK_SUFFIX);
scan_scsi_bus(scsi_addr)?;
get_device_name(sandbox, &dev_sub_path)
}
pub fn get_pci_device_name(sandbox: &Arc<Mutex<Sandbox>>, pci_id: &str) -> Result<String> {
let pci_addr = get_pci_device_address(pci_id)?;
rescan_pci_bus()?;
get_device_name(sandbox, &pci_addr)
}
/// Scan SCSI bus for the given SCSI address(SCSI-Id and LUN)
fn scan_scsi_bus(scsi_addr: &str) -> Result<()> {
let tokens: Vec<&str> = scsi_addr.split(":").collect();
if tokens.len() != 2 {
return Err(ErrorKind::Msg(format!(
"Unexpected format for SCSI Address: {}, expect SCSIID:LUA",
scsi_addr
))
.into());
}
// Scan scsi host passing in the channel, SCSI id and LUN.
// Channel is always 0 because we have only one SCSI controller.
let scan_data = format!("0 {} {}", tokens[0], tokens[1]);
for entry in fs::read_dir(SYSFS_SCSI_HOST_PATH)? {
let host = entry?.file_name();
let scan_path = format!(
"{}/{}/{}",
SYSFS_SCSI_HOST_PATH,
host.to_str().unwrap(),
"scan"
);
fs::write(scan_path, &scan_data)?;
}
Ok(())
}
// update_spec_device_list takes a device description provided by the caller,
// trying to find it on the guest. Once this device has been identified, the
// "real" information that can be read from inside the VM is used to update
// the same device in the list of devices provided through the OCI spec.
// This is needed to update information about minor/major numbers that cannot
// be predicted from the caller.
fn update_spec_device_list(device: &Device, spec: &mut Spec) -> Result<()> {
let major_id: c_uint;
let minor_id: c_uint;
// If no container_path is provided, we won't be able to match and
// update the device in the OCI spec device list. This is an error.
if device.container_path == "" {
return Err(ErrorKind::Msg(format!(
"container_path cannot empty for device {:?}",
device
))
.into());
}
let linux = match spec.linux.as_mut() {
None => {
return Err(
ErrorKind::ErrorCode("Spec didn't container linux field".to_string()).into(),
)
}
Some(l) => l,
};
if !Path::new(&device.vm_path).exists() {
return Err(ErrorKind::Msg(format!("vm_path:{} doesn't exist", device.vm_path)).into());
}
let meta = fs::metadata(&device.vm_path)?;
let dev_id = meta.rdev();
unsafe {
major_id = major(dev_id);
minor_id = minor(dev_id);
}
info!(
sl!(),
"got the device: dev_path: {}, major: {}, minor: {}\n", &device.vm_path, major_id, minor_id
);
let devices = linux.devices.as_mut_slice();
for dev in devices.iter_mut() {
if dev.path == device.container_path {
let host_major = dev.major;
let host_minor = dev.minor;
dev.major = major_id as i64;
dev.minor = minor_id as i64;
info!(
sl!(),
"change the device from major: {} minor: {} to vm device major: {} minor: {}",
host_major,
host_minor,
major_id,
minor_id
);
// Resources must be updated since they are used to identify the
// device in the devices cgroup.
if let Some(res) = linux.resources.as_mut() {
let ds = res.devices.as_mut_slice();
for d in ds.iter_mut() {
if d.major == Some(host_major) && d.minor == Some(host_minor) {
d.major = Some(major_id as i64);
d.minor = Some(minor_id as i64);
info!(
sl!(),
"set resources for device major: {} minor: {}\n", major_id, minor_id
);
}
}
}
}
}
Ok(())
}
// device.Id should be the predicted device name (vda, vdb, ...)
// device.VmPath already provides a way to send it in
fn virtiommio_blk_device_handler(
device: &Device,
spec: &mut Spec,
_sandbox: &Arc<Mutex<Sandbox>>,
) -> Result<()> {
if device.vm_path == "" {
return Err(ErrorKind::Msg("Invalid path for virtio mmio blk device".to_string()).into());
}
update_spec_device_list(device, spec)
}
// device.Id should be the PCI address in the format "bridgeAddr/deviceAddr".
// Here, bridgeAddr is the address at which the brige is attached on the root bus,
// while deviceAddr is the address at which the device is attached on the bridge.
fn virtio_blk_device_handler(
device: &Device,
spec: &mut Spec,
sandbox: &Arc<Mutex<Sandbox>>,
) -> Result<()> {
let mut dev = device.clone();
dev.vm_path = get_pci_device_name(sandbox, &device.id)?;
update_spec_device_list(&dev, spec)
}
// device.Id should be the SCSI address of the disk in the format "scsiID:lunID"
fn virtio_scsi_device_handler(
device: &Device,
spec: &mut Spec,
sandbox: &Arc<Mutex<Sandbox>>,
) -> Result<()> {
let mut dev = device.clone();
dev.vm_path = get_scsi_device_name(sandbox, &device.id)?;
update_spec_device_list(&dev, spec)
}
fn virtio_nvdimm_device_handler(
device: &Device,
spec: &mut Spec,
_sandbox: &Arc<Mutex<Sandbox>>,
) -> Result<()> {
if device.vm_path == "" {
return Err(ErrorKind::Msg("Invalid path for nvdimm device".to_string()).into());
}
update_spec_device_list(device, spec)
}
pub fn add_devices(
devices: &[Device],
spec: &mut Spec,
sandbox: &Arc<Mutex<Sandbox>>,
) -> Result<()> {
for device in devices.iter() {
add_device(device, spec, sandbox)?;
}
Ok(())
}
fn add_device(device: &Device, spec: &mut Spec, sandbox: &Arc<Mutex<Sandbox>>) -> Result<()> {
// log before validation to help with debugging gRPC protocol version differences.
info!(sl!(), "device-id: {}, device-type: {}, device-vm-path: {}, device-container-path: {}, device-options: {:?}",
device.id, device.field_type, device.vm_path, device.container_path, device.options);
if device.field_type == "" {
return Err(ErrorKind::Msg(format!("invalid type for device {:?}", device)).into());
}
if device.id == "" && device.vm_path == "" {
return Err(
ErrorKind::Msg(format!("invalid ID and VM path for device {:?}", device)).into(),
);
}
if device.container_path == "" {
return Err(
ErrorKind::Msg(format!("invalid container path for device {:?}", device)).into(),
);
}
match DEVICEHANDLERLIST.get(device.field_type.as_str()) {
None => Err(ErrorKind::Msg(format!("Unknown device type {}", device.field_type)).into()),
Some(dev_handler) => dev_handler(device, spec, sandbox),
}
}

1856
src/agent/src/grpc.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,50 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
/// Linux ABI related constants.
pub const SYSFS_DIR: &str = "/sys";
pub const SYSFS_PCI_BUS_PREFIX: &str = "/sys/bus/pci/devices";
pub const SYSFS_PCI_BUS_RESCAN_FILE: &str = "/sys/bus/pci/rescan";
#[cfg(any(
target_arch = "powerpc64le",
target_arch = "s390x",
target_arch = "x86_64",
target_arch = "x86"
))]
pub const PCI_ROOT_BUS_PATH: &str = "/devices/pci0000:00";
#[cfg(target_arch = "aarch64")]
pub const PCI_ROOT_BUS_PATH: &str = "/devices/platform/4010000000.pcie/pci0000:00";
pub const SYSFS_CPU_ONLINE_PATH: &str = "/sys/devices/system/cpu";
pub const SYSFS_MEMORY_BLOCK_SIZE_PATH: &str = "/sys/devices/system/memory/block_size_bytes";
pub const SYSFS_MEMORY_HOTPLUG_PROBE_PATH: &str = "/sys/devices/system/memory/probe";
pub const SYSFS_MEMORY_ONLINE_PATH: &str = "/sys/devices/system/memory";
// Here in "0:0", the first number is the SCSI host number because
// only one SCSI controller has been plugged, while the second number
// is always 0.
pub const SCSI_HOST_CHANNEL: &str = "0:0:";
pub const SCSI_BLOCK_SUFFIX: &str = "block";
pub const SYSFS_SCSI_HOST_PATH: &str = "/sys/class/scsi_host";
pub const SYSFS_CGROUPPATH: &str = "/sys/fs/cgroup";
pub const SYSFS_ONLINE_FILE: &str = "online";
pub const PROC_MOUNTSTATS: &str = "/proc/self/mountstats";
pub const PROC_CGROUPS: &str = "/proc/cgroups";
pub const SYSTEM_DEV_PATH: &str = "/dev";
// Linux UEvent related consts.
pub const U_EVENT_ACTION: &str = "ACTION";
pub const U_EVENT_ACTION_ADD: &str = "add";
pub const U_EVENT_DEV_PATH: &str = "DEVPATH";
pub const U_EVENT_SUB_SYSTEM: &str = "SUBSYSTEM";
pub const U_EVENT_SEQ_NUM: &str = "SEQNUM";
pub const U_EVENT_DEV_NAME: &str = "DEVNAME";
pub const U_EVENT_INTERFACE: &str = "INTERFACE";

468
src/agent/src/main.rs Normal file
View File

@@ -0,0 +1,468 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
#![allow(non_camel_case_types)]
#![allow(unused_parens)]
#![allow(unused_unsafe)]
#![allow(dead_code)]
#![allow(non_snake_case)]
#[macro_use]
extern crate lazy_static;
extern crate oci;
extern crate prctl;
extern crate protocols;
extern crate regex;
extern crate rustjail;
extern crate scan_fmt;
extern crate serde_json;
extern crate signal_hook;
#[macro_use]
extern crate scopeguard;
#[macro_use]
extern crate slog;
#[macro_use]
extern crate netlink;
use futures::*;
use nix::fcntl::{self, OFlag};
use nix::sys::socket::{self, AddressFamily, SockAddr, SockFlag, SockType};
use nix::sys::wait::{self, WaitStatus};
use nix::unistd;
use prctl::set_child_subreaper;
use rustjail::errors::*;
use signal_hook::{iterator::Signals, SIGCHLD};
use std::collections::HashMap;
use std::env;
use std::fs::{self, File};
use std::os::unix::fs as unixfs;
use std::os::unix::io::AsRawFd;
use std::path::Path;
use std::sync::mpsc::{self, Sender};
use std::sync::{Arc, Mutex, RwLock};
use std::{io, thread};
use unistd::Pid;
mod config;
mod device;
mod linux_abi;
mod mount;
mod namespace;
mod network;
pub mod random;
mod sandbox;
#[cfg(test)]
mod test_utils;
mod uevent;
mod version;
use mount::{cgroups_mount, general_mount};
use sandbox::Sandbox;
use slog::Logger;
use uevent::watch_uevents;
mod grpc;
const NAME: &str = "kata-agent";
const VSOCK_ADDR: &str = "vsock://-1";
const VSOCK_PORT: u16 = 1024;
const KERNEL_CMDLINE_FILE: &str = "/proc/cmdline";
const CONSOLE_PATH: &str = "/dev/console";
lazy_static! {
static ref GLOBAL_DEVICE_WATCHER: Arc<Mutex<HashMap<String, Sender<String>>>> =
Arc::new(Mutex::new(HashMap::new()));
static ref AGENT_CONFIG: Arc<RwLock<agentConfig>> =
Arc::new(RwLock::new(config::agentConfig::new()));
}
use std::mem::MaybeUninit;
fn announce(logger: &Logger) {
let commit = match env::var("VERSION_COMMIT") {
Ok(s) => s,
Err(_) => String::from(""),
};
info!(logger, "announce";
"agent-commit" => commit.as_str(),
// Avoid any possibility of confusion with the old agent
"agent-type" => "rust",
"agent-version" => version::AGENT_VERSION,
"api-version" => version::API_VERSION,
);
}
fn main() -> Result<()> {
let args: Vec<String> = env::args().collect();
if args.len() == 2 && args[1] == "init" {
rustjail::container::init_child();
exit(0);
}
env::set_var("RUST_BACKTRACE", "full");
lazy_static::initialize(&SHELLS);
lazy_static::initialize(&AGENT_CONFIG);
// support vsock log
let (rfd, wfd) = unistd::pipe2(OFlag::O_CLOEXEC)?;
let writer = unsafe { File::from_raw_fd(wfd) };
let agentConfig = AGENT_CONFIG.clone();
if unistd::getpid() == Pid::from_raw(1) {
// Init a temporary logger used by init agent as init process
// since before do the base mount, it wouldn't access "/proc/cmdline"
// to get the customzied debug level.
let logger = logging::create_logger(NAME, "agent", slog::Level::Debug, writer);
init_agent_as_init(&logger)?;
}
// once parsed cmdline and set the config, release the write lock
// as soon as possible in case other thread would get read lock on
// it.
{
let mut config = agentConfig.write().unwrap();
config.parse_cmdline(KERNEL_CMDLINE_FILE)?;
}
let config = agentConfig.read().unwrap();
let log_vport = config.log_vport as u32;
let log_handle = thread::spawn(move || -> Result<()> {
let mut reader = unsafe { File::from_raw_fd(rfd) };
if log_vport > 0 {
let listenfd = socket::socket(
AddressFamily::Vsock,
SockType::Stream,
SockFlag::SOCK_CLOEXEC,
None,
)?;
let addr = SockAddr::new_vsock(libc::VMADDR_CID_ANY, log_vport);
socket::bind(listenfd, &addr)?;
socket::listen(listenfd, 1)?;
let datafd = socket::accept4(listenfd, SockFlag::SOCK_CLOEXEC)?;
let mut log_writer = unsafe { File::from_raw_fd(datafd) };
let _ = io::copy(&mut reader, &mut log_writer)?;
let _ = unistd::close(listenfd);
let _ = unistd::close(datafd);
}
// copy log to stdout
let mut stdout_writer = io::stdout();
let _ = io::copy(&mut reader, &mut stdout_writer)?;
Ok(())
});
let writer = unsafe { File::from_raw_fd(wfd) };
// Recreate a logger with the log level get from "/proc/cmdline".
let logger = logging::create_logger(NAME, "agent", config.log_level, writer);
announce(&logger);
if args.len() == 2 && args[1] == "--version" {
// force logger to flush
drop(logger);
exit(0);
}
// This "unused" variable is required as it enables the global (and crucially static) logger,
// which is required to satisfy the the lifetime constraints of the auto-generated gRPC code.
let _guard = slog_scope::set_global_logger(logger.new(o!("subsystem" => "grpc")));
let shells = SHELLS.clone();
let debug_console_vport = config.debug_console_vport as u32;
let shell_handle = if config.debug_console {
let thread_logger = logger.clone();
thread::spawn(move || {
let shells = shells.lock().unwrap();
let result = setup_debug_console(shells.to_vec(), debug_console_vport);
if result.is_err() {
// Report error, but don't fail
warn!(thread_logger, "failed to setup debug console";
"error" => format!("{}", result.unwrap_err()));
}
})
} else {
unsafe { MaybeUninit::zeroed().assume_init() }
};
// Initialize unique sandbox structure.
let s = Sandbox::new(&logger).map_err(|e| {
error!(logger, "Failed to create sandbox with error: {:?}", e);
e
})?;
let sandbox = Arc::new(Mutex::new(s));
setup_signal_handler(&logger, sandbox.clone()).unwrap();
watch_uevents(sandbox.clone());
let (tx, rx) = mpsc::channel::<i32>();
sandbox.lock().unwrap().sender = Some(tx);
//vsock:///dev/vsock, port
let mut server = grpc::start(sandbox.clone(), VSOCK_ADDR, VSOCK_PORT);
/*
let _ = fs::remove_file("/tmp/testagent");
let _ = fs::remove_dir_all("/run/agent");
let mut server = grpc::start(sandbox.clone(), "unix:///tmp/testagent", 1);
*/
let handle = thread::spawn(move || {
// info!("Press ENTER to exit...");
// let _ = io::stdin().read(&mut [0]).unwrap();
// thread::sleep(Duration::from_secs(3000));
let _ = rx.recv().unwrap();
});
// receive something from destroy_sandbox here?
// or in the thread above? It depneds whether grpc request
// are run in another thread or in the main thead?
// let _ = rx.wait();
handle.join().unwrap();
let _ = log_handle.join();
if config.debug_console {
shell_handle.join().unwrap();
}
let _ = server.shutdown().wait();
let _ = fs::remove_file("/tmp/testagent");
Ok(())
}
use nix::sys::wait::WaitPidFlag;
fn setup_signal_handler(logger: &Logger, sandbox: Arc<Mutex<Sandbox>>) -> Result<()> {
let logger = logger.new(o!("subsystem" => "signals"));
set_child_subreaper(true).map_err(|err| {
format!(
"failed to setup agent as a child subreaper, failed with {}",
err
)
})?;
let signals = Signals::new(&[SIGCHLD])?;
let s = sandbox.clone();
thread::spawn(move || {
'outer: for sig in signals.forever() {
info!(logger, "received signal"; "signal" => sig);
// sevral signals can be combined together
// as one. So loop around to reap all
// exited children
'inner: loop {
let wait_status = match wait::waitpid(
Some(Pid::from_raw(-1)),
Some(WaitPidFlag::WNOHANG | WaitPidFlag::__WALL),
) {
Ok(s) => {
if s == WaitStatus::StillAlive {
continue 'outer;
}
s
}
Err(e) => {
info!(
logger,
"waitpid reaper failed";
"error" => e.as_errno().unwrap().desc()
);
continue 'outer;
}
};
let pid = wait_status.pid();
if pid.is_some() {
let raw_pid = pid.unwrap().as_raw();
let child_pid = format!("{}", raw_pid);
let logger = logger.new(o!("child-pid" => child_pid));
let mut sandbox = s.lock().unwrap();
let process = sandbox.find_process(raw_pid);
if process.is_none() {
info!(logger, "child exited unexpectedly");
continue 'inner;
}
let mut p = process.unwrap();
if p.exit_pipe_w.is_none() {
error!(logger, "the process's exit_pipe_w isn't set");
continue 'inner;
}
let pipe_write = p.exit_pipe_w.unwrap();
let ret: i32;
match wait_status {
WaitStatus::Exited(_, c) => ret = c,
WaitStatus::Signaled(_, sig, _) => ret = sig as i32,
_ => {
info!(logger, "got wrong status for process";
"child-status" => format!("{:?}", wait_status));
continue 'inner;
}
}
p.exit_code = ret;
let _ = unistd::close(pipe_write);
}
}
}
});
Ok(())
}
// init_agent_as_init will do the initializations such as setting up the rootfs
// when this agent has been run as the init process.
fn init_agent_as_init(logger: &Logger) -> Result<()> {
general_mount(logger)?;
cgroups_mount(logger)?;
fs::remove_file(Path::new("/dev/ptmx"))?;
unixfs::symlink(Path::new("/dev/pts/ptmx"), Path::new("/dev/ptmx"))?;
unistd::setsid()?;
unsafe {
libc::ioctl(io::stdin().as_raw_fd(), libc::TIOCSCTTY, 1);
}
env::set_var("PATH", "/bin:/sbin/:/usr/bin/:/usr/sbin/");
Ok(())
}
lazy_static! {
static ref SHELLS: Arc<Mutex<Vec<String>>> = {
let mut v = Vec::new();
if !cfg!(test) {
v.push("/bin/bash".to_string());
v.push("/bin/sh".to_string());
}
Arc::new(Mutex::new(v))
};
}
// pub static mut LOG_LEVEL: ;
// pub static mut TRACE_MODE: ;
use crate::config::agentConfig;
use nix::sys::stat::Mode;
use std::os::unix::io::{FromRawFd, RawFd};
use std::path::PathBuf;
use std::process::{exit, Command, Stdio};
fn setup_debug_console(shells: Vec<String>, port: u32) -> Result<()> {
for shell in shells.iter() {
let binary = PathBuf::from(shell);
if binary.exists() {
let f: RawFd = if port > 0 {
let listenfd = socket::socket(
AddressFamily::Vsock,
SockType::Stream,
SockFlag::SOCK_CLOEXEC,
None,
)?;
let addr = SockAddr::new_vsock(libc::VMADDR_CID_ANY, port);
socket::bind(listenfd, &addr)?;
socket::listen(listenfd, 1)?;
socket::accept4(listenfd, SockFlag::SOCK_CLOEXEC)?
} else {
let mut flags = OFlag::empty();
flags.insert(OFlag::O_RDWR);
flags.insert(OFlag::O_CLOEXEC);
fcntl::open(CONSOLE_PATH, flags, Mode::empty())?
};
let cmd = Command::new(shell)
.arg("-i")
.stdin(unsafe { Stdio::from_raw_fd(f) })
.stdout(unsafe { Stdio::from_raw_fd(f) })
.stderr(unsafe { Stdio::from_raw_fd(f) })
.spawn();
let mut cmd = match cmd {
Ok(c) => c,
Err(_) => {
return Err(ErrorKind::ErrorCode("failed to spawn shell".to_string()).into())
}
};
cmd.wait()?;
return Ok(());
} else {
return Err(ErrorKind::ErrorCode("invalid shell".to_string()).into());
}
}
Err(ErrorKind::ErrorCode("no shell".to_string()).into())
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::File;
use std::io::Write;
use tempfile::tempdir;
#[test]
fn test_setup_debug_console_no_shells() {
// Guarantee no shells have been added
// (required to avoid racing with
// test_setup_debug_console_invalid_shell()).
let shells_ref = SHELLS.clone();
let mut shells = shells_ref.lock().unwrap();
shells.clear();
let result = setup_debug_console(shells.to_vec(), 0);
assert!(result.is_err());
assert_eq!(result.unwrap_err().to_string(), "Error Code: 'no shell'");
}
#[test]
fn test_setup_debug_console_invalid_shell() {
let shells_ref = SHELLS.clone();
let mut shells = shells_ref.lock().unwrap();
let dir = tempdir().expect("failed to create tmpdir");
// Add an invalid shell
let shell = dir
.path()
.join("enoent")
.to_str()
.expect("failed to construct shell path")
.to_string();
shells.push(shell);
let result = setup_debug_console(shells.to_vec(), 0);
assert!(result.is_err());
assert_eq!(
result.unwrap_err().to_string(),
"Error Code: 'invalid shell'"
);
}
}

1238
src/agent/src/mount.rs Normal file

File diff suppressed because it is too large Load Diff

239
src/agent/src/namespace.rs Normal file
View File

@@ -0,0 +1,239 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
use nix::mount::MsFlags;
use nix::sched::{unshare, CloneFlags};
use nix::unistd::{getpid, gettid};
use std::fmt;
use std::fs;
use std::fs::File;
use std::os::unix::io::AsRawFd;
use std::path::{Path, PathBuf};
use std::thread;
use crate::mount::{BareMount, FLAGS};
use slog::Logger;
//use container::Process;
const PERSISTENT_NS_DIR: &str = "/var/run/sandbox-ns";
pub const NSTYPEIPC: &str = "ipc";
pub const NSTYPEUTS: &str = "uts";
pub const NSTYPEPID: &str = "pid";
pub fn get_current_thread_ns_path(ns_type: &str) -> String {
format!(
"/proc/{}/task/{}/ns/{}",
getpid().to_string(),
gettid().to_string(),
ns_type
)
}
#[derive(Debug)]
pub struct Namespace {
logger: Logger,
pub path: String,
persistent_ns_dir: String,
ns_type: NamespaceType,
//only used for uts namespace
pub hostname: Option<String>,
}
impl Namespace {
pub fn new(logger: &Logger) -> Self {
Namespace {
logger: logger.clone(),
path: String::from(""),
persistent_ns_dir: String::from(PERSISTENT_NS_DIR),
ns_type: NamespaceType::IPC,
hostname: None,
}
}
pub fn as_ipc(mut self) -> Self {
self.ns_type = NamespaceType::IPC;
self
}
pub fn as_uts(mut self, hostname: &str) -> Self {
self.ns_type = NamespaceType::UTS;
if hostname != "" {
self.hostname = Some(String::from(hostname));
}
self
}
pub fn set_root_dir(mut self, dir: &str) -> Self {
self.persistent_ns_dir = dir.to_string();
self
}
// setup_persistent_ns creates persistent namespace without switching to it.
// Note, pid namespaces cannot be persisted.
pub fn setup(mut self) -> Result<Self, String> {
if let Err(err) = fs::create_dir_all(&self.persistent_ns_dir) {
return Err(err.to_string());
}
let ns_path = PathBuf::from(&self.persistent_ns_dir);
let ns_type = self.ns_type.clone();
let logger = self.logger.clone();
let new_ns_path = ns_path.join(&ns_type.get());
if let Err(err) = File::create(new_ns_path.as_path()) {
return Err(err.to_string());
}
self.path = new_ns_path.clone().into_os_string().into_string().unwrap();
let hostname = self.hostname.clone();
let new_thread = thread::spawn(move || {
let origin_ns_path = get_current_thread_ns_path(&ns_type.get());
let _origin_ns_fd = match File::open(Path::new(&origin_ns_path)) {
Err(err) => return Err(err.to_string()),
Ok(file) => file.as_raw_fd(),
};
// Create a new netns on the current thread.
let cf = ns_type.get_flags().clone();
if let Err(err) = unshare(cf) {
return Err(err.to_string());
}
if ns_type == NamespaceType::UTS && hostname.is_some() {
match nix::unistd::sethostname(hostname.unwrap()) {
Err(err) => return Err(err.to_string()),
Ok(_) => (),
}
}
// Bind mount the new namespace from the current thread onto the mount point to persist it.
let source: &str = origin_ns_path.as_str();
let destination: &str = new_ns_path.as_path().to_str().unwrap_or("none");
let mut flags = MsFlags::empty();
match FLAGS.get("rbind") {
Some(x) => {
let (_, f) = *x;
flags = flags | f;
}
None => (),
};
let bare_mount = BareMount::new(source, destination, "none", flags, "", &logger);
if let Err(err) = bare_mount.mount() {
return Err(format!(
"Failed to mount {} to {} with err:{:?}",
source, destination, err
));
}
Ok(())
});
match new_thread.join() {
Ok(t) => match t {
Err(err) => return Err(err),
Ok(()) => (),
},
Err(err) => return Err(format!("Failed to join thread {:?}!", err)),
}
Ok(self)
}
}
/// Represents the Namespace type.
#[derive(Clone, Copy, PartialEq)]
enum NamespaceType {
IPC,
UTS,
PID,
}
impl NamespaceType {
/// Get the string representation of the namespace type.
pub fn get(&self) -> &str {
match *self {
Self::IPC => "ipc",
Self::UTS => "uts",
Self::PID => "pid",
}
}
/// Get the associate flags with the namespace type.
pub fn get_flags(&self) -> CloneFlags {
match *self {
Self::IPC => CloneFlags::CLONE_NEWIPC,
Self::UTS => CloneFlags::CLONE_NEWUTS,
Self::PID => CloneFlags::CLONE_NEWPID,
}
}
}
impl fmt::Debug for NamespaceType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.get())
}
}
impl Default for NamespaceType {
fn default() -> Self {
NamespaceType::IPC
}
}
#[cfg(test)]
mod tests {
use super::{Namespace, NamespaceType};
use crate::{mount::remove_mounts, skip_if_not_root};
use nix::sched::CloneFlags;
use tempfile::Builder;
#[test]
fn test_setup_persistent_ns() {
skip_if_not_root!();
// Create dummy logger and temp folder.
let logger = slog::Logger::root(slog::Discard, o!());
let tmpdir = Builder::new().prefix("ipc").tempdir().unwrap();
let ns_ipc = Namespace::new(&logger)
.as_ipc()
.set_root_dir(tmpdir.path().to_str().unwrap())
.setup();
assert!(ns_ipc.is_ok());
assert!(remove_mounts(&vec![ns_ipc.unwrap().path]).is_ok());
let logger = slog::Logger::root(slog::Discard, o!());
let tmpdir = Builder::new().prefix("ipc").tempdir().unwrap();
let ns_uts = Namespace::new(&logger)
.as_uts("test_hostname")
.set_root_dir(tmpdir.path().to_str().unwrap())
.setup();
assert!(ns_uts.is_ok());
assert!(remove_mounts(&vec![ns_uts.unwrap().path]).is_ok());
}
#[test]
fn test_namespace_type() {
let ipc = NamespaceType::IPC;
assert_eq!("ipc", ipc.get());
assert_eq!(CloneFlags::CLONE_NEWIPC, ipc.get_flags());
let uts = NamespaceType::UTS;
assert_eq!("uts", uts.get());
assert_eq!(CloneFlags::CLONE_NEWUTS, uts.get_flags());
let pid = NamespaceType::PID;
assert_eq!("pid", pid.get());
assert_eq!(CloneFlags::CLONE_NEWPID, pid.get_flags());
}
}

30
src/agent/src/network.rs Normal file
View File

@@ -0,0 +1,30 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
use protocols::types::{Interface, Route};
use std::collections::HashMap;
// Network fully describes a sandbox network with its interfaces, routes and dns
// related information.
#[derive(Debug, Default)]
pub struct Network {
ifaces: HashMap<String, Interface>,
routes: Vec<Route>,
dns: Vec<String>,
}
impl Network {
pub fn new() -> Network {
Network {
ifaces: HashMap::new(),
routes: Vec::new(),
dns: Vec::new(),
}
}
pub fn set_dns(&mut self, dns: String) {
self.dns.push(dns);
}
}

42
src/agent/src/random.rs Normal file
View File

@@ -0,0 +1,42 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
use libc;
use nix::errno::Errno;
use nix::fcntl::{self, OFlag};
use nix::sys::stat::Mode;
use rustjail::errors::*;
use std::fs;
pub const RNGDEV: &str = "/dev/random";
pub const RNDADDTOENTCNT: libc::c_int = 0x40045201;
pub const RNDRESEEDRNG: libc::c_int = 0x5207;
// Handle the differing ioctl(2) request types for different targets
#[cfg(target_env = "musl")]
type IoctlRequestType = libc::c_int;
#[cfg(target_env = "gnu")]
type IoctlRequestType = libc::c_ulong;
pub fn reseed_rng(data: &[u8]) -> Result<()> {
let len = data.len() as libc::c_long;
fs::write(RNGDEV, data)?;
let fd = fcntl::open(RNGDEV, OFlag::O_RDWR, Mode::from_bits_truncate(0o022))?;
let ret = unsafe {
libc::ioctl(
fd,
RNDADDTOENTCNT as IoctlRequestType,
&len as *const libc::c_long,
)
};
let _ = Errno::result(ret).map(drop)?;
let ret = unsafe { libc::ioctl(fd, RNDRESEEDRNG as IoctlRequestType, 0) };
let _ = Errno::result(ret).map(drop)?;
Ok(())
}

556
src/agent/src/sandbox.rs Normal file
View File

@@ -0,0 +1,556 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
//use crate::container::Container;
use crate::linux_abi::*;
use crate::mount::{get_mount_fs_type, remove_mounts, TYPEROOTFS};
use crate::namespace::Namespace;
use crate::network::Network;
use libc::pid_t;
use netlink::{RtnlHandle, NETLINK_ROUTE};
use protocols::agent::OnlineCPUMemRequest;
use regex::Regex;
use rustjail::cgroups;
use rustjail::container::BaseContainer;
use rustjail::container::LinuxContainer;
use rustjail::errors::*;
use rustjail::process::Process;
use slog::Logger;
use std::collections::HashMap;
use std::fs;
use std::sync::mpsc::Sender;
#[derive(Debug)]
pub struct Sandbox {
pub logger: Logger,
pub id: String,
pub hostname: String,
pub containers: HashMap<String, LinuxContainer>,
pub network: Network,
pub mounts: Vec<String>,
pub container_mounts: HashMap<String, Vec<String>>,
pub pci_device_map: HashMap<String, String>,
pub shared_utsns: Namespace,
pub shared_ipcns: Namespace,
pub storages: HashMap<String, u32>,
pub running: bool,
pub no_pivot_root: bool,
pub sandbox_pid_ns: bool,
pub sender: Option<Sender<i32>>,
pub rtnl: Option<RtnlHandle>,
}
impl Sandbox {
pub fn new(logger: &Logger) -> Result<Self> {
let fs_type = get_mount_fs_type("/")?;
let logger = logger.new(o!("subsystem" => "sandbox"));
Ok(Sandbox {
logger: logger.clone(),
id: String::new(),
hostname: String::new(),
network: Network::new(),
containers: HashMap::new(),
mounts: Vec::new(),
container_mounts: HashMap::new(),
pci_device_map: HashMap::new(),
shared_utsns: Namespace::new(&logger),
shared_ipcns: Namespace::new(&logger),
storages: HashMap::new(),
running: false,
no_pivot_root: fs_type.eq(TYPEROOTFS),
sandbox_pid_ns: false,
sender: None,
rtnl: Some(RtnlHandle::new(NETLINK_ROUTE, 0).unwrap()),
})
}
// set_sandbox_storage sets the sandbox level reference
// counter for the sandbox storage.
// This method also returns a boolean to let
// callers know if the storage already existed or not.
// It will return true if storage is new.
//
// It's assumed that caller is calling this method after
// acquiring a lock on sandbox.
pub fn set_sandbox_storage(&mut self, path: &str) -> bool {
match self.storages.get_mut(path) {
None => {
self.storages.insert(path.to_string(), 1);
true
}
Some(count) => {
*count += 1;
false
}
}
}
// unset_sandbox_storage will decrement the sandbox storage
// reference counter. If there aren't any containers using
// that sandbox storage, this method will remove the
// storage reference from the sandbox and return 'true' to
// let the caller know that they can clean up the storage
// related directories by calling remove_sandbox_storage
//
// It's assumed that caller is calling this method after
// acquiring a lock on sandbox.
pub fn unset_sandbox_storage(&mut self, path: &str) -> Result<bool> {
match self.storages.get_mut(path) {
None => {
return Err(ErrorKind::ErrorCode(format!(
"Sandbox storage with path {} not found",
path
))
.into())
}
Some(count) => {
*count -= 1;
if *count < 1 {
self.storages.remove(path);
return Ok(true);
}
return Ok(false);
}
}
}
// remove_sandbox_storage removes the sandbox storage if no
// containers are using that storage.
//
// It's assumed that caller is calling this method after
// acquiring a lock on sandbox.
pub fn remove_sandbox_storage(&self, path: &str) -> Result<()> {
let mounts = vec![path.to_string()];
remove_mounts(&mounts)?;
fs::remove_dir_all(path)?;
Ok(())
}
// unset_and_remove_sandbox_storage unsets the storage from sandbox
// and if there are no containers using this storage it will
// remove it from the sandbox.
//
// It's assumed that caller is calling this method after
// acquiring a lock on sandbox.
pub fn unset_and_remove_sandbox_storage(&mut self, path: &str) -> Result<()> {
match self.unset_sandbox_storage(path) {
Ok(res) => {
if res {
return self.remove_sandbox_storage(path);
}
}
Err(err) => {
return Err(err);
}
}
Ok(())
}
pub fn is_running(&self) -> bool {
self.running
}
pub fn set_hostname(&mut self, hostname: String) {
self.hostname = hostname;
}
pub fn setup_shared_namespaces(&mut self) -> Result<bool> {
// Set up shared IPC namespace
self.shared_ipcns = match Namespace::new(&self.logger).as_ipc().setup() {
Ok(ns) => ns,
Err(err) => {
return Err(ErrorKind::ErrorCode(format!(
"Failed to setup persistent IPC namespace with error: {}",
err
))
.into())
}
};
// // Set up shared UTS namespace
self.shared_utsns = match Namespace::new(&self.logger)
.as_uts(self.hostname.as_str())
.setup()
{
Ok(ns) => ns,
Err(err) => {
return Err(ErrorKind::ErrorCode(format!(
"Failed to setup persistent UTS namespace with error: {}",
err
))
.into())
}
};
Ok(true)
}
pub fn add_container(&mut self, c: LinuxContainer) {
self.containers.insert(c.id.clone(), c);
}
pub fn get_container(&mut self, id: &str) -> Option<&mut LinuxContainer> {
self.containers.get_mut(id)
}
pub fn find_process(&mut self, pid: pid_t) -> Option<&mut Process> {
for (_, c) in self.containers.iter_mut() {
if c.processes.get(&pid).is_some() {
return c.processes.get_mut(&pid);
}
}
None
}
pub fn destroy(&mut self) -> Result<()> {
for (_, ctr) in &mut self.containers {
ctr.destroy()?;
}
Ok(())
}
pub fn online_cpu_memory(&self, req: &OnlineCPUMemRequest) -> Result<()> {
if req.nb_cpus > 0 {
// online cpus
online_cpus(&self.logger, req.nb_cpus as i32)?;
}
if !req.cpu_only {
// online memory
online_memory(&self.logger)?;
}
let cpuset = cgroups::fs::get_guest_cpuset()?;
for (_, ctr) in self.containers.iter() {
info!(self.logger, "updating {}", ctr.id.as_str());
ctr.cgroup_manager
.as_ref()
.unwrap()
.update_cpuset_path(cpuset.as_str())?;
}
Ok(())
}
}
fn online_resources(logger: &Logger, path: &str, pattern: &str, num: i32) -> Result<i32> {
let mut count = 0;
let re = Regex::new(pattern)?;
for e in fs::read_dir(path)? {
let entry = e?;
let tmpname = entry.file_name();
let name = tmpname.to_str().unwrap();
let p = entry.path();
if re.is_match(name) {
let file = format!("{}/{}", p.to_str().unwrap(), SYSFS_ONLINE_FILE);
info!(logger, "{}", file.as_str());
let c = fs::read_to_string(file.as_str())?;
if c.trim().contains("0") {
fs::write(file.as_str(), "1")?;
count += 1;
if num > 0 && count == num {
break;
}
}
}
}
if num > 0 {
return Ok(count);
}
Ok(0)
}
fn online_cpus(logger: &Logger, num: i32) -> Result<i32> {
online_resources(logger, SYSFS_CPU_ONLINE_PATH, r"cpu[0-9]+", num)
}
fn online_memory(logger: &Logger) -> Result<()> {
online_resources(logger, SYSFS_MEMORY_ONLINE_PATH, r"memory[0-9]+", -1)?;
Ok(())
}
#[cfg(test)]
mod tests {
//use rustjail::Error;
use super::Sandbox;
use crate::{mount::BareMount, skip_if_not_root};
use nix::mount::MsFlags;
use oci::{Linux, Root, Spec};
use rustjail::container::LinuxContainer;
use rustjail::specconv::CreateOpts;
use slog::Logger;
use tempfile::Builder;
fn bind_mount(src: &str, dst: &str, logger: &Logger) -> Result<(), rustjail::errors::Error> {
let baremount = BareMount::new(src, dst, "bind", MsFlags::MS_BIND, "", &logger);
baremount.mount()
}
#[test]
fn set_sandbox_storage() {
let logger = slog::Logger::root(slog::Discard, o!());
let mut s = Sandbox::new(&logger).unwrap();
let tmpdir = Builder::new().tempdir().unwrap();
let tmpdir_path = tmpdir.path().to_str().unwrap();
// Add a new sandbox storage
let new_storage = s.set_sandbox_storage(&tmpdir_path);
// Check the reference counter
let ref_count = s.storages[tmpdir_path];
assert_eq!(
ref_count, 1,
"Invalid refcount, got {} expected 1.",
ref_count
);
assert_eq!(new_storage, true);
// Use the existing sandbox storage
let new_storage = s.set_sandbox_storage(&tmpdir_path);
assert_eq!(new_storage, false, "Should be false as already exists.");
// Since we are using existing storage, the reference counter
// should be 2 by now.
let ref_count = s.storages[tmpdir_path];
assert_eq!(
ref_count, 2,
"Invalid refcount, got {} expected 2.",
ref_count
);
}
#[test]
fn remove_sandbox_storage() {
skip_if_not_root!();
let logger = slog::Logger::root(slog::Discard, o!());
let s = Sandbox::new(&logger).unwrap();
let tmpdir = Builder::new().tempdir().unwrap();
let tmpdir_path = tmpdir.path().to_str().unwrap();
let srcdir = Builder::new()
.prefix("src")
.tempdir_in(tmpdir_path)
.unwrap();
let srcdir_path = srcdir.path().to_str().unwrap();
let destdir = Builder::new()
.prefix("dest")
.tempdir_in(tmpdir_path)
.unwrap();
let destdir_path = destdir.path().to_str().unwrap();
let emptydir = Builder::new()
.prefix("empty")
.tempdir_in(tmpdir_path)
.unwrap();
assert!(
s.remove_sandbox_storage(&srcdir_path).is_err(),
"Expect Err as the directory i not a mountpoint"
);
assert!(s.remove_sandbox_storage("").is_err());
let invalid_dir = emptydir.path().join("invalid");
assert!(s
.remove_sandbox_storage(invalid_dir.to_str().unwrap())
.is_err());
// Now, create a double mount as this guarantees the directory cannot
// be deleted after the first umount.
for _i in 0..2 {
assert!(bind_mount(srcdir_path, destdir_path, &logger).is_ok());
}
assert!(
s.remove_sandbox_storage(destdir_path).is_err(),
"Expect fail as deletion cannot happen due to the second mount."
);
// This time it should work as the previous two calls have undone the double
// mount.
assert!(s.remove_sandbox_storage(destdir_path).is_ok());
}
#[test]
#[allow(unused_assignments)]
fn unset_and_remove_sandbox_storage() {
skip_if_not_root!();
let logger = slog::Logger::root(slog::Discard, o!());
let mut s = Sandbox::new(&logger).unwrap();
// FIX: This test fails, not sure why yet.
assert!(
s.unset_and_remove_sandbox_storage("/tmp/testEphePath")
.is_err(),
"Should fail because sandbox storage doesn't exist"
);
let tmpdir = Builder::new().tempdir().unwrap();
let tmpdir_path = tmpdir.path().to_str().unwrap();
let srcdir = Builder::new()
.prefix("src")
.tempdir_in(tmpdir_path)
.unwrap();
let srcdir_path = srcdir.path().to_str().unwrap();
let destdir = Builder::new()
.prefix("dest")
.tempdir_in(tmpdir_path)
.unwrap();
let destdir_path = destdir.path().to_str().unwrap();
assert!(bind_mount(srcdir_path, destdir_path, &logger).is_ok());
assert_eq!(s.set_sandbox_storage(&destdir_path), true);
assert!(s.unset_and_remove_sandbox_storage(&destdir_path).is_ok());
let mut other_dir_str = String::new();
{
// Create another folder in a separate scope to ensure that is
// deleted
let other_dir = Builder::new()
.prefix("dir")
.tempdir_in(tmpdir_path)
.unwrap();
let other_dir_path = other_dir.path().to_str().unwrap();
other_dir_str = other_dir_path.to_string();
assert_eq!(s.set_sandbox_storage(&other_dir_path), true);
}
assert!(s.unset_and_remove_sandbox_storage(&other_dir_str).is_err());
}
#[test]
fn unset_sandbox_storage() {
let logger = slog::Logger::root(slog::Discard, o!());
let mut s = Sandbox::new(&logger).unwrap();
let storage_path = "/tmp/testEphe";
// Add a new sandbox storage
assert_eq!(s.set_sandbox_storage(&storage_path), true);
// Use the existing sandbox storage
assert_eq!(
s.set_sandbox_storage(&storage_path),
false,
"Expects false as the storage is not new."
);
assert_eq!(
s.unset_sandbox_storage(&storage_path).unwrap(),
false,
"Expects false as there is still a storage."
);
// Reference counter should decrement to 1.
let ref_count = s.storages[storage_path];
assert_eq!(
ref_count, 1,
"Invalid refcount, got {} expected 1.",
ref_count
);
assert_eq!(
s.unset_sandbox_storage(&storage_path).unwrap(),
true,
"Expects true as there is still a storage."
);
// Since no container is using this sandbox storage anymore
// there should not be any reference in sandbox struct
// for the given storage
assert!(
!s.storages.contains_key(storage_path),
"The storages map should not contain the key {}",
storage_path
);
// If no container is using the sandbox storage, the reference
// counter for it should not exist.
assert!(
s.unset_sandbox_storage(&storage_path).is_err(),
"Expects false as the reference counter should no exist."
);
}
fn create_dummy_opts() -> CreateOpts {
let mut root = Root::default();
root.path = String::from("/");
let linux = Linux::default();
let mut spec = Spec::default();
spec.root = Some(root).into();
spec.linux = Some(linux).into();
CreateOpts {
cgroup_name: "".to_string(),
use_systemd_cgroup: false,
no_pivot_root: false,
no_new_keyring: false,
spec: Some(spec),
rootless_euid: false,
rootless_cgroup: false,
}
}
fn create_linuxcontainer() -> LinuxContainer {
LinuxContainer::new(
"some_id",
"/run/agent",
create_dummy_opts(),
&slog_scope::logger(),
)
.unwrap()
}
#[test]
fn get_container_entry_exist() {
skip_if_not_root!();
let logger = slog::Logger::root(slog::Discard, o!());
let mut s = Sandbox::new(&logger).unwrap();
let linux_container = create_linuxcontainer();
s.containers
.insert("testContainerID".to_string(), linux_container);
let cnt = s.get_container("testContainerID");
assert!(cnt.is_some());
}
#[test]
fn get_container_no_entry() {
let logger = slog::Logger::root(slog::Discard, o!());
let mut s = Sandbox::new(&logger).unwrap();
let cnt = s.get_container("testContainerID");
assert!(cnt.is_none());
}
#[test]
fn add_and_get_container() {
skip_if_not_root!();
let logger = slog::Logger::root(slog::Discard, o!());
let mut s = Sandbox::new(&logger).unwrap();
let linux_container = create_linuxcontainer();
s.add_container(linux_container);
assert!(s.get_container("some_id").is_some());
}
}

View File

@@ -0,0 +1,59 @@
// Copyright (c) 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
#[cfg(test)]
mod test_utils {
#[macro_export]
#[allow(unused_macros)]
macro_rules! skip_if_root {
() => {
if nix::unistd::Uid::effective().is_root() {
println!("INFO: skipping {} which needs non-root", module_path!());
return;
}
};
}
#[macro_export]
#[allow(unused_macros)]
macro_rules! skip_if_not_root {
() => {
if !nix::unistd::Uid::effective().is_root() {
println!("INFO: skipping {} which needs root", module_path!());
return;
}
};
}
#[macro_export]
#[allow(unused_macros)]
macro_rules! skip_loop_if_root {
($msg:expr) => {
if nix::unistd::Uid::effective().is_root() {
println!(
"INFO: skipping loop {} in {} which needs non-root",
$msg,
module_path!()
);
continue;
}
};
}
#[macro_export]
#[allow(unused_macros)]
macro_rules! skip_loop_if_not_root {
($msg:expr) => {
if !nix::unistd::Uid::effective().is_root() {
println!(
"INFO: skipping loop {} in {} which needs root",
$msg,
module_path!()
);
continue;
}
};
}
}

147
src/agent/src/uevent.rs Normal file
View File

@@ -0,0 +1,147 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
use crate::device::online_device;
use crate::linux_abi::*;
use crate::sandbox::Sandbox;
use crate::GLOBAL_DEVICE_WATCHER;
use netlink::{RtnlHandle, NETLINK_UEVENT};
use slog::Logger;
use std::sync::{Arc, Mutex};
use std::thread;
#[derive(Debug, Default)]
struct Uevent {
action: String,
devpath: String,
devname: String,
subsystem: String,
seqnum: String,
interface: String,
}
impl Uevent {
fn new(message: &str) -> Self {
let mut msg_iter = message.split('\0');
let mut event = Uevent::default();
msg_iter.next(); // skip the first value
for arg in msg_iter {
let key_val: Vec<&str> = arg.splitn(2, '=').collect();
if key_val.len() == 2 {
match key_val[0] {
U_EVENT_ACTION => event.action = String::from(key_val[1]),
U_EVENT_DEV_NAME => event.devname = String::from(key_val[1]),
U_EVENT_SUB_SYSTEM => event.subsystem = String::from(key_val[1]),
U_EVENT_DEV_PATH => event.devpath = String::from(key_val[1]),
U_EVENT_SEQ_NUM => event.seqnum = String::from(key_val[1]),
U_EVENT_INTERFACE => event.interface = String::from(key_val[1]),
_ => (),
}
}
}
event
}
// Check whether this is a block device hot-add event.
fn is_block_add_event(&self) -> bool {
self.action == U_EVENT_ACTION_ADD
&& self.subsystem == "block"
&& self.devpath.starts_with(PCI_ROOT_BUS_PATH)
&& self.devname != ""
}
fn handle_block_add_event(&self, sandbox: &Arc<Mutex<Sandbox>>) {
// Keep the same lock order as device::get_device_name(), otherwise it may cause deadlock.
let mut w = GLOBAL_DEVICE_WATCHER.lock().unwrap();
let mut sb = sandbox.lock().unwrap();
// Add the device node name to the pci device map.
sb.pci_device_map
.insert(self.devpath.clone(), self.devname.clone());
// Notify watchers that are interested in the udev event.
// Close the channel after watcher has been notified.
let devpath = self.devpath.clone();
let empties: Vec<_> = w
.iter()
.filter(|(dev_addr, _)| {
let pci_p = format!("{}/{}", PCI_ROOT_BUS_PATH, *dev_addr);
// blk block device
devpath.starts_with(pci_p.as_str()) ||
// scsi block device
{
(*dev_addr).ends_with(SCSI_BLOCK_SUFFIX) &&
devpath.contains(*dev_addr)
}
})
.map(|(k, sender)| {
let devname = self.devname.clone();
let _ = sender.send(devname);
k.clone()
})
.collect();
// Remove notified nodes from the watcher map.
for empty in empties {
w.remove(&empty);
}
}
fn process(&self, logger: &Logger, sandbox: &Arc<Mutex<Sandbox>>) {
if self.is_block_add_event() {
return self.handle_block_add_event(sandbox);
} else if self.action == U_EVENT_ACTION_ADD {
let online_path = format!("{}/{}/online", SYSFS_DIR, &self.devpath);
// It's a memory hot-add event.
if online_path.starts_with(SYSFS_MEMORY_ONLINE_PATH) {
if let Err(e) = online_device(online_path.as_ref()) {
error!(
*logger,
"failed to online device";
"device" => &self.devpath,
"error" => format!("{}", e),
);
}
return;
}
}
debug!(*logger, "ignoring event"; "uevent" => format!("{:?}", self));
}
}
pub fn watch_uevents(sandbox: Arc<Mutex<Sandbox>>) {
thread::spawn(move || {
let rtnl = RtnlHandle::new(NETLINK_UEVENT, 1).unwrap();
let logger = sandbox
.lock()
.unwrap()
.logger
.new(o!("subsystem" => "uevent"));
loop {
match rtnl.recv_message() {
Err(e) => {
error!(logger, "receive uevent message failed"; "error" => format!("{}", e))
}
Ok(data) => {
let text = String::from_utf8(data);
match text {
Err(e) => {
error!(logger, "failed to convert bytes to text"; "error" => format!("{}", e))
}
Ok(text) => {
let event = Uevent::new(&text);
info!(logger, "got uevent message"; "event" => format!("{:?}", event));
event.process(&logger, &sandbox);
}
}
}
}
}
});
}

7
src/agent/src/version.rs Normal file
View File

@@ -0,0 +1,7 @@
// Copyright (c) 2019 Ant Financial
//
// SPDX-License-Identifier: Apache-2.0
//
pub const AGENT_VERSION: &str = "1.4.5";
pub const API_VERSION: &str = "0.0.1";