Updated comments
This commit is contained in:
parent
f71dff4e3d
commit
27c8932a05
54
README.md
54
README.md
@ -1,52 +1,14 @@
|
|||||||
This is a starting point for Rust solutions to the
|
# codecrafters-docker-rust
|
||||||
["Build Your Own Docker" Challenge](https://codecrafters.io/challenges/docker).
|
My code for CodeCrafter's ["Build Your Own Docker" Challenge](https://codecrafters.io/challenges/docker).
|
||||||
|
|
||||||
In this challenge, you'll build a program that can pull an image from
|
## Requirements
|
||||||
[Docker Hub](https://hub.docker.com/) and execute commands in it. Along the way,
|
- docker
|
||||||
we'll learn about [chroot](https://en.wikipedia.org/wiki/Chroot),
|
|
||||||
[kernel namespaces](https://en.wikipedia.org/wiki/Linux_namespaces), the
|
|
||||||
[docker registry API](https://docs.docker.com/registry/spec/api/) and much more.
|
|
||||||
|
|
||||||
**Note**: If you're viewing this repo on GitHub, head over to
|
|
||||||
[codecrafters.io](https://codecrafters.io) to signup for early access.
|
|
||||||
|
|
||||||
# Passing the first stage
|
|
||||||
|
|
||||||
The entry point for your Docker implementation is `src/main.rs`. Study and
|
|
||||||
uncomment the relevant code, and push your changes to pass the first stage:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
git add .
|
|
||||||
git commit -m "pass 1st stage" # any msg
|
|
||||||
git push origin master
|
|
||||||
```
|
|
||||||
|
|
||||||
That's all!
|
|
||||||
|
|
||||||
# Stage 2 & beyond
|
|
||||||
|
|
||||||
Note: This section is for stages 2 and beyond.
|
|
||||||
|
|
||||||
You'll use linux-specific syscalls in this challenge. so we'll run your code
|
|
||||||
_inside_ a Docker container.
|
|
||||||
|
|
||||||
Please ensure you have [Docker installed](https://docs.docker.com/get-docker/)
|
|
||||||
locally.
|
|
||||||
|
|
||||||
Next, add a [shell alias](https://shapeshed.com/unix-alias/):
|
|
||||||
|
|
||||||
|
## Getting started
|
||||||
```sh
|
```sh
|
||||||
alias mydocker='docker build -t mydocker . && docker run --cap-add="SYS_ADMIN" mydocker'
|
alias mydocker='docker build -t mydocker . && docker run --cap-add="SYS_ADMIN" mydocker'
|
||||||
|
mydocker run debian:latest /bin/sh -c "ls -la /"
|
||||||
```
|
```
|
||||||
|
|
||||||
(The `--cap-add="SYS_ADMIN"` flag is required to create
|
Note: The `--cap-add="SYS_ADMIN"` flag is required to create
|
||||||
[PID Namespaces](https://man7.org/linux/man-pages/man7/pid_namespaces.7.html))
|
[PID Namespaces](https://man7.org/linux/man-pages/man7/pid_namespaces.7.html)
|
||||||
|
|
||||||
You can now execute your program like this:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
mydocker run ubuntu:latest /usr/local/bin/docker-explorer echo hey
|
|
||||||
```
|
|
||||||
|
|
||||||
This command compiles your Rust project, so it might be slow the first time you
|
|
||||||
run it. Subsequent runs will be fast.
|
|
||||||
|
13
src/main.rs
13
src/main.rs
@ -9,7 +9,7 @@ fn main() {
|
|||||||
let command = &args[3];
|
let command = &args[3];
|
||||||
let command_args = &args[4..];
|
let command_args = &args[4..];
|
||||||
|
|
||||||
let base_path = std::env::temp_dir().join("mydocker");
|
let base_path = std::env::temp_dir().join("docker");
|
||||||
|
|
||||||
// prevent cryptic "no such file or directory" error inside chroot
|
// prevent cryptic "no such file or directory" error inside chroot
|
||||||
std::fs::create_dir_all(base_path.join("dev")).unwrap();
|
std::fs::create_dir_all(base_path.join("dev")).unwrap();
|
||||||
@ -19,14 +19,14 @@ fn main() {
|
|||||||
let mut reg = registry::Registry::default();
|
let mut reg = registry::Registry::default();
|
||||||
reg.pull(&image, base_path.to_str().unwrap());
|
reg.pull(&image, base_path.to_str().unwrap());
|
||||||
|
|
||||||
// copy over binary into chroot
|
// copy over binary into chroot directory
|
||||||
let command_path = path::Path::new(command).strip_prefix("/").unwrap();
|
let command_path = path::Path::new(command).strip_prefix("/").unwrap();
|
||||||
std::fs::create_dir_all(base_path.join(command_path.parent().unwrap()))
|
std::fs::create_dir_all(base_path.join(command_path.parent().unwrap()))
|
||||||
.expect("Failed to create directory for executed binary");
|
.expect("Failed to create directory for executed binary");
|
||||||
std::fs::copy(command, base_path.join(command_path))
|
std::fs::copy(command, base_path.join(command_path))
|
||||||
.expect("Failed copying executed binary to chroot directory");
|
.expect("Failed copying executed binary to chroot directory");
|
||||||
|
|
||||||
// create and change into chroot
|
// create and change into chroot directory
|
||||||
let cbase_path = CString::new(base_path.to_str().unwrap().to_owned()).unwrap();
|
let cbase_path = CString::new(base_path.to_str().unwrap().to_owned()).unwrap();
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::chroot(cbase_path.as_ptr());
|
libc::chroot(cbase_path.as_ptr());
|
||||||
@ -35,6 +35,7 @@ fn main() {
|
|||||||
// ensure that directory changed to root of jail
|
// ensure that directory changed to root of jail
|
||||||
std::env::set_current_dir("/").expect("Failed to change to root dir");
|
std::env::set_current_dir("/").expect("Failed to change to root dir");
|
||||||
|
|
||||||
|
// `unshare` puts the next created process in a seperate PID namespace
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::unshare(libc::CLONE_NEWPID);
|
libc::unshare(libc::CLONE_NEWPID);
|
||||||
}
|
}
|
||||||
@ -44,10 +45,8 @@ fn main() {
|
|||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let std_out = std::str::from_utf8(&output.stdout).unwrap();
|
print!("{}", std::str::from_utf8(&output.stdout).unwrap());
|
||||||
print!("{}", std_out);
|
eprint!("{}", std::str::from_utf8(&output.stderr).unwrap());
|
||||||
let std_err = std::str::from_utf8(&output.stderr).unwrap();
|
|
||||||
eprint!("{}", std_err);
|
|
||||||
|
|
||||||
std::process::exit(output.status.code().unwrap_or(1));
|
std::process::exit(output.status.code().unwrap_or(1));
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// TODO: Enable derive feature
|
// TODO: Enable derive feature and convert dynamic to typed json
|
||||||
|
|
||||||
pub struct Registry {
|
pub struct Registry {
|
||||||
http_client: reqwest::blocking::Client,
|
http_client: reqwest::blocking::Client,
|
||||||
@ -60,7 +60,7 @@ impl Registry {
|
|||||||
let layer_path = temp_path.join(digest);
|
let layer_path = temp_path.join(digest);
|
||||||
std::fs::write(layer_path.to_owned(), blob).unwrap();
|
std::fs::write(layer_path.to_owned(), blob).unwrap();
|
||||||
|
|
||||||
// TODO: handle exit code of untar
|
// TODO: handle exit code
|
||||||
std::process::Command::new("tar")
|
std::process::Command::new("tar")
|
||||||
.args(["-xf", layer_path.to_str().unwrap(), "-C", destination])
|
.args(["-xf", layer_path.to_str().unwrap(), "-C", destination])
|
||||||
.output()
|
.output()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user