Skip to content

Commit 637dac2

Browse files
committed
feat(course): add deprecated Docker course fixture
Add a new course fixture for "Build your own Docker" with multiple stages covering process isolation, filesystem isolation, and Docker registry API integration. The course supports several programming languages and includes marketing info and testimonials. Mark the course as deprecated due to its release status. This enables users to explore container fundamentals and build a simple Docker implementation.
1 parent 4f0bda1 commit 637dac2

File tree

154 files changed

+8901
-1643
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

154 files changed

+8901
-1643
lines changed

mirage/course-fixtures/docker.js

Lines changed: 0 additions & 107 deletions
This file was deleted.
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
slug: "docker"
2+
name: "Build your own Docker"
3+
short_name: "Docker"
4+
release_status: "deprecated"
5+
6+
# This is shown on the course overview page. Markdown supported, recommended length ~40 words.
7+
#
8+
# Recommended format:
9+
#
10+
# > ABC is <whatever>. In this challenge, you'll build your own ABC that's capable of D, E, F and G.
11+
# >
12+
# > Along the way, we'll learn about X, Y, Z and more.
13+
#
14+
# Example:
15+
#
16+
# > Redis is an in-memory data structure store often used as a database, cache, message broken and streaming engine. In this challenge
17+
# > you'll build your own Redis server that is capable of serving basic commands, reading RDB files and more.
18+
# >
19+
# > Along the way, you'll learn about TCP servers, the Redis Protocol and more.
20+
description_md: |-
21+
Docker is a tool used to build & run applications in containers. In this challenge, you'll build
22+
your own Docker implementation that can pull an image from Docker Hub and execute commands in it.
23+
24+
Along the way, you'll learn about chroot, kernel namespaces, the Docker registry API and much more.
25+
26+
# Keep this under 70 characters
27+
short_description_md: |-
28+
Learn about kernel namespaces, chroot, the registry API and more
29+
30+
completion_percentage: 30
31+
32+
languages:
33+
- slug: "c"
34+
35+
- slug: "go"
36+
37+
- slug: "nim"
38+
39+
- slug: "php"
40+
41+
- slug: "python"
42+
release_status: "beta"
43+
44+
- slug: "ruby"
45+
release_status: "beta"
46+
47+
- slug: "rust"
48+
49+
- slug: "swift"
50+
release_status: "alpha"
51+
alpha_tester_usernames: ["Terky"]
52+
53+
marketing:
54+
difficulty: medium
55+
sample_extension_idea_title: "Build from Dockerfile"
56+
sample_extension_idea_description: "A Docker implementation that can build images from a Dockerfile"
57+
testimonials:
58+
- author_name: "Raghav Dua"
59+
author_description: "SRE, Coinbase"
60+
author_avatar: "https://codecrafters.io/images/external/testimonials/raghav-dua.jpeg"
61+
link: "https://github.com/duaraghav8"
62+
text: |-
63+
I spent a full day on your Docker building course and ended up building the whole thing myself. As a SRE (and
64+
mostly a user of docker), digging into the internals blew me away.
65+
66+
- author_name: "Beyang Liu"
67+
author_description: "CTO at SourceGraph"
68+
author_avatar: "https://codecrafters.io/images/external/testimonials/beyang-liu.jpeg"
69+
link: "https://twitter.com/beyang"
70+
text: |-
71+
CodeCrafters has you build your own version of things like Git and Docker from scratch. A cool way to build a stronger mental model of how those tools work.
72+
73+
stages:
74+
- slug: "je9"
75+
name: "Execute a program"
76+
difficulty: very_easy
77+
marketing_md: |-
78+
In this stage, you'll execute a program using `fork` + `exec`.
79+
tester_source_code_url: "https://github.com/codecrafters-io/docker-tester/blob/18245703a5beed8ee0a7e1cbb7204a7ee3b3b5d1/internal/stage_basic_exec.go#L9"
80+
81+
- slug: "kf3"
82+
name: "Wireup stdout & stderr"
83+
difficulty: easy
84+
marketing_md: |-
85+
In this stage, you'll relay the child program's stdout & stderr to the
86+
parent process.
87+
tester_source_code_url: "https://github.com/codecrafters-io/docker-tester/blob/18245703a5beed8ee0a7e1cbb7204a7ee3b3b5d1/internal/stage_stdio.go#L9"
88+
89+
- slug: "cn8"
90+
name: "Handle exit codes"
91+
difficulty: easy
92+
marketing_md: |-
93+
In this stage, you'll wait for the child program's exit code and exit with
94+
it.
95+
tester_source_code_url: "https://github.com/codecrafters-io/docker-tester/blob/18245703a5beed8ee0a7e1cbb7204a7ee3b3b5d1/internal/stage_exit_code.go#L9"
96+
97+
- slug: "if6"
98+
name: "Filesystem isolation"
99+
difficulty: medium
100+
marketing_md: |-
101+
In this stage, you'll restrict a program's access to the host filesystem
102+
by using [chroot](https://en.wikipedia.org/wiki/Chroot).
103+
tester_source_code_url: "https://github.com/codecrafters-io/docker-tester/blob/18245703a5beed8ee0a7e1cbb7204a7ee3b3b5d1/internal/stage_fs_isolation.go#L8"
104+
105+
- slug: "lu7"
106+
name: "Process isolation"
107+
difficulty: medium
108+
marketing_md: |-
109+
In this stage, you'll restrict a program's access to the host's process
110+
tree by using [PID
111+
namespaces](http://man7.org/linux/man-pages/man7/pid_namespaces.7.html).
112+
tester_source_code_url: "https://github.com/codecrafters-io/docker-tester/blob/18245703a5beed8ee0a7e1cbb7204a7ee3b3b5d1/internal/stage_process_isolation.go#L5"
113+
114+
- slug: "hs1"
115+
name: "Fetch an image from the Docker Registry"
116+
should_skip_previous_stages_for_test_run: true
117+
difficulty: hard
118+
marketing_md: |-
119+
In this stage, you'll fetch an image from Docker Hub and execute a command
120+
in it. You'll need to use [the Docker Registry
121+
API](https://docs.docker.com/registry/spec/api/) for this.
122+
tester_source_code_url: "https://github.com/codecrafters-io/docker-tester/blob/18245703a5beed8ee0a7e1cbb7204a7ee3b3b5d1/internal/stage_fetch_from_registry.go#L8"
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Your task is to implement a very basic version
2+
of [`docker run`](https://docs.docker.com/engine/reference/run/)</a>. It will
3+
be executed similar to `docker run`:
4+
5+
```
6+
mydocker run alpine:latest /usr/local/bin/docker-explorer echo hey
7+
```
8+
9+
[docker-explorer](https://github.com/codecrafters-io/docker-explorer) is a custom test program that exposes
10+
commands like `echo` and `ls`.
11+
12+
For now, don't worry about pulling the `alpine:latest` image. We will just
13+
execute a local program for this stage and print its output. You'll work on
14+
pulling images from Docker Hub in stage 6.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
You'll now pipe the program's stdout and stderr to the
2+
parent process.
3+
4+
Like the last stage, the tester will run your program like this:
5+
6+
```
7+
mydocker run alpine:latest /usr/local/bin/docker-explorer echo hey
8+
```
9+
10+
To test this behaviour locally, you could use the `echo` + `echo_stderr`
11+
commands that `docker-explorer` exposes. Run `docker-explorer --help` to
12+
view usage.
13+
14+
If you've got any logs or print statements in your code, make sure to remove
15+
them. The tester can't differentiate between debug logs and the actual
16+
output!
17+
18+
Note: The **README** in your repository contains setup
19+
information for this stage and beyond (takes < 5 min).
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
In this stage, you'll need to relay the program's exit code to the parent
2+
process.
3+
4+
If the program you're executing exits with exit code 1, your program
5+
should exit with exit code 1 too.
6+
7+
To test this behaviour locally, you could use the `exit` command that
8+
`docker-explorer` exposes. Run `docker-explorer --help` to view usage.
9+
10+
Just like the previous stage, the tester will run your program like this:
11+
12+
```
13+
mydocker run alpine:latest /usr/local/bin/docker-explorer exit 1
14+
```
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
In the previous stage, we executed a program that existed locally on our
2+
machine. This program had write access to the whole filesystem, which
3+
means that it could do **dangerous** things!
4+
5+
In this stage, you'll use [chroot](https://en.wikipedia.org/wiki/Chroot)
6+
to ensure that the program you execute doesn't have access to any files on
7+
the host machine. Create an empty temporary directory and `chroot` into it
8+
when executing the command. You'll need to copy the binary being executed
9+
too.
10+
11+
{{#lang_is_rust}}
12+
At the time of writing this, the implementation of chroot in Rust's standard library
13+
([std::os::unix::fs::chroot](https://doc.rust-lang.org/std/os/unix/fs/fn.chroot.html)) is still a
14+
nightly-only experimental API. We've included [libc](https://crates.io/crates/libc) as a dependency
15+
instead.
16+
{{/lang_is_rust}}
17+
18+
{{#lang_is_nim}}
19+
Since Nim's [posix module](https://nim-lang.org/docs/posix.html) doesn't
20+
have `chroot` defined, you'll need to implement this yourself! For
21+
examples on how to do this, view the source for other syscalls like
22+
[chdir](https://nim-lang.org/docs/posix.html#chdir%2Ccstring).
23+
{{/lang_is_nim}}
24+
25+
{{#lang_is_go}}
26+
When executing your program within the chroot directory, you might run into an error that says
27+
`open /dev/null: no such file or directory`. This is because [Cmd.Run()](https://golang.org/pkg/os/exec/#Cmd.Run)
28+
and its siblings expect `/dev/null` to be present. You can work around this by either creating an empty
29+
`/dev/null` file inside the chroot directory, or by ensuring that `Cmd.Stdout`, `Cmd.Stderr` and `Cmd.Stdin` are not `nil`.
30+
More details about this [here](https://rohitpaulk.com/articles/cmd-run-dev-null).
31+
{{/lang_is_go}}
32+
33+
{{#lang_is_rust}}
34+
When executing your program within the chroot directory, you might run into an error that says
35+
`no such file or directory` even if the binary exists within the chroot. This is because
36+
[Command::output()](https://doc.rust-lang.org/std/process/struct.Command.html#method.output)
37+
expects `/dev/null` to be present. You can work around this by creating an empty
38+
`/dev/null` file inside the chroot directory. This cryptic error effects Go programs too, more details
39+
[here](https://rohitpaulk.com/articles/cmd-run-dev-null).
40+
{{/lang_is_rust}}
41+
42+
Just like the previous stage, the tester will run your program like this:
43+
44+
```
45+
mydocker run alpine:latest /usr/local/bin/docker-explorer ls /some_dir
46+
```
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
In the previous stage, we guarded against malicious activity by
2+
restricting an executable's access to the filesystem.
3+
4+
There's another resource that needs to be guarded: the process tree. The
5+
process you're executing is currently capable of viewing all other
6+
processes running on the host system, and sending signals to them.
7+
8+
In this stage, you'll use [PID
9+
namespaces](http://man7.org/linux/man-pages/man7/pid_namespaces.7.html) to
10+
ensure that the program you execute has its own isolated process tree.
11+
The process being executed must see itself as PID 1.
12+
13+
{{#lang_is_php}}
14+
You'll need to use the `pcntl_unshare` function for this, which was
15+
[added in PHP 7.4](https://www.php.net/manual/en/migration74.new-functions.php), and isn't properly documented
16+
yet (as of 22 Jan 2021). Here's the [pull request](https://github.com/php/php-src/pull/3760) where it was added.
17+
{{/lang_is_php}}
18+
19+
Just like the previous stage, the tester will run your program like this:
20+
21+
```
22+
mydocker run alpine:latest /usr/local/bin/docker-explorer mypid
23+
```

0 commit comments

Comments
 (0)