Debugging
Since Youki uses pipe and double-fork in the creating phase, it is hard to debug what happened. -You might encounter the error message, "Broken pipe ..." Unfortunately, +You might encounter the error message, "Broken pipe ..." Unfortunately, this error message has only information that a child process exists with an error for some reason.
This section will give some tips to debug youki to know what happens in the child processes.
bpftrace
@@ -216,15 +216,15 @@bpftrace
$ just test-kind docker buildx build --output=bin/ -f tests/k8s/Dockerfile --target kind-bin . ... -Creating cluster "youki" ... +Creating cluster "youki" ... ... kubectl --context=kind-youki apply -f tests/k8s/deploy.yaml runtimeclass.node.k8s.io/youki created deployment.apps/nginx-deployment created ... kubectl --context=kind-youki delete -f tests/k8s/deploy.yaml -runtimeclass.node.k8s.io "youki" deleted -deployment.apps "nginx-deployment" deleted +runtimeclass.node.k8s.io "youki" deleted +deployment.apps "nginx-deployment" deletedbpftrace
207066996623 4 13743 open errno=2, fd=-1, file=/opt/containerd/lib/glibc-hwcaps/x86-64-v3/libc.so.6 ... 207070130175 4 13743 clone3 -207070418829 youki:[1:INTER] 13747 write fd=4, {"timestamp":"2023-09-24T10:47:07.427846Z","level":"INFO","message":"cgroup manager V2 will be used","target":"libcgrou +207070418829 youki:[1:INTER] 13747 write fd=4, {"timestamp":"2023-09-24T10:47:07.427846Z","level":"INFO","message":"cgroup manager V2 will be used","target":"libcgrou ... 207084948440 youki:[1:INTER] 13747 clone3 -207085058811 youki:[1:INTER] 13747 write fd=4, {"timestamp":"2023-09-24T10:47:07.442502Z","level":"DEBUG","message":"sending init pid (Pid(1305))","target":"libcontai -207085343170 youki:[2:INIT] 13750 write fd=4, {"timestamp":"2023-09-24T10:47:07.442746Z","level":"DEBUG","message":"unshare or setns: LinuxNamespace { typ: Uts, path +207085058811 youki:[1:INTER] 13747 write fd=4, {"timestamp":"2023-09-24T10:47:07.442502Z","level":"DEBUG","message":"sending init pid (Pid(1305))","target":"libcontai +207085343170 youki:[2:INIT] 13750 write fd=4, {"timestamp":"2023-09-24T10:47:07.442746Z","level":"DEBUG","message":"unshare or setns: LinuxNamespace { typ: Uts, path ... 207088256843 youki:[2:INIT] 13750 pivt_root new_root=/run/containerd/io.containerd.runtime.v2.task/k8s.io/0fea8cf5f8d1619a35ca67fd6fa73d8d7c8fc70ac2ed43ee2ac2f8610bb938f6/r, put_old=/run/containerd/io.containerd.runtime.v2.task/k8s.io/0fea8cf5f8d1619a35ca67fd6fa73d8d7c8fc70ac2ed43ee2ac2f8610bb938f6/r ... -207097207551 youki:[2:INIT] 13750 write fd=4, {"timestamp":"2023-09-24T10:47:07.454645Z","level":"DEBUG","message":"found executable in executor","executable":"\"/pa +207097207551 youki:[2:INIT] 13750 write fd=4, {"timestamp":"2023-09-24T10:47:07.454645Z","level":"DEBUG","message":"found executable in executor","executable":"\"/pa ... -207139391811 youki:[2:INIT] 13750 write fd=4, {"timestamp":"2023-09-24T10:47:07.496815Z","level":"DEBUG","message":"received: start container","target":"libcontainer -207139423243 youki:[2:INIT] 13750 write fd=4, {"timestamp":"2023-09-24T10:47:07.496868Z","level":"DEBUG","message":"executing workload with default handler","target" +207139391811 youki:[2:INIT] 13750 write fd=4, {"timestamp":"2023-09-24T10:47:07.496815Z","level":"DEBUG","message":"received: start container","target":"libcontainer +207139423243 youki:[2:INIT] 13750 write fd=4, {"timestamp":"2023-09-24T10:47:07.496868Z","level":"DEBUG","message":"executing workload with default handler","target"Notes
Now runtimetest needs at least oci-spec
and nix
package for its operations, which are also dependencies of other packages in the workspace. Thus both of these, and recursively their dependencies must be compiled twice, each time, once for dynamic linking and once for static. The took a long time in the compilation stage, especially when developing / adding new tests. Separating runtimetest from the workspace allows it to have a separate target/ directory, where it can store the statically compiled dependencies, and the workspace can have its target/ directory, where it can store its dynamically compiled dependencies. That way only the crates which have changes need to be compiled (runtimetest or integration test), and not their dependencies.
In case in future this separation is not required, or some other configuration is chosen, make sure the multiple compilation issue does not arise, or the advantages of new method outweigh the time spent in double compilation.
To see if a binary can be run inside the container process, run
-readelf -l path/to/binary |grep "program interpreter"
+readelf -l path/to/binary |grep "program interpreter"
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
means that the binary is not statically linked, and cannot be run inside the container process. If the above command gives no output, that means it does not require any program interpreter and can be run inside the container.
Another way is to run
diff --git a/developer/e2e/rust_oci_test.html b/developer/e2e/rust_oci_test.html
index f5bb19ef2..db1974dfd 100644
--- a/developer/e2e/rust_oci_test.html
+++ b/developer/e2e/rust_oci_test.html
@@ -184,7 +184,46 @@ How to write
Fully the code of the example test
-
{{#include ../../../../tests/integration_test/src/tests/example/hello_world.rs}}
+use anyhow::{Context, Result};
+use oci_spec::runtime::{ProcessBuilder, Spec, SpecBuilder};
+use test_framework::{test_result, Test, TestGroup, TestResult};
+
+use crate::utils::test_inside_container;
+
+////////// ANCHOR: get_example_spec
+fn create_spec() -> Result<Spec> {
+ SpecBuilder::default()
+ .process(
+ ProcessBuilder::default()
+ .args(
+ ["runtimetest", "hello_world"]
+ .iter()
+ .map(|s| s.to_string())
+ .collect::<Vec<String>>(),
+ )
+ .build()?,
+ )
+ .build()
+ .context("failed to create spec")
+}
+////////// ANCHOR_END: get_example_spec
+
+////////// ANCHOR: example_test
+fn example_test() -> TestResult {
+ let spec = test_result!(create_spec());
+ test_inside_container(spec, &|_| Ok(()))
+}
+////////// ANCHOR_END: example_test
+
+////////// ANCHOR: get_example_test
+pub fn get_example_test() -> TestGroup {
+ let mut test_group = TestGroup::new("example");
+ let test1 = Test::new("hello world", Box::new(example_test));
+ test_group.add(vec![Box::new(test1)]);
+
+ test_group
+}
+////////// ANCHOR_END: get_example_test