feat: basic functionality
This commit is contained in:
@@ -3,14 +3,14 @@
|
||||
## Synopsis
|
||||
|
||||
```
|
||||
ecr <distro>[:<version>] [-a <arch>] [options] [-- command [args...]]
|
||||
ecr [OPTIONS] <DISTRO[:VERSION]> -- [COMMAND]...
|
||||
```
|
||||
|
||||
## CLI Interface
|
||||
|
||||
### Positional Arguments
|
||||
|
||||
- `<distro>` (required): Distribution name
|
||||
- `<distro>` (required): Distribution name or OCI image reference
|
||||
- `<version>` (optional): Distribution version/codename
|
||||
|
||||
### Options
|
||||
@@ -18,9 +18,10 @@ ecr <distro>[:<version>] [-a <arch>] [options] [-- command [args...]]
|
||||
| Flag | Default | Description |
|
||||
|------|---------|-------------|
|
||||
| `-a, --arch <arch>` | host arch | Target architecture |
|
||||
| `--bind [path]` | cwd | Directory to overlay-mount |
|
||||
| `--bind-rw [path]` | none | Read-write bind mount at `/root/<basename>` (bypasses overlay) |
|
||||
| `--bind <path>` | cwd | Directory to overlay-mount (can be specified multiple times) |
|
||||
| `--bind-rw <path>` | none | Read-write bind mount at `/mnt/<basename>` (can be specified multiple times, overrides `--bind` for same path) |
|
||||
| `--no-cache` | false | Download fresh tarball, ignore cache |
|
||||
| `--no-bind` | false | Skip mounting any directory |
|
||||
| `-h, --help` | - | Show help |
|
||||
| `-V, --version` | - | Show version |
|
||||
|
||||
@@ -31,8 +32,8 @@ ecr <distro>[:<version>] [-a <arch>] [options] [-- command [args...]]
|
||||
```
|
||||
~/.cache/ecr/
|
||||
├── ubuntu-noble-amd64.tar.gz
|
||||
├── debian-bookworm-arm64.tar.gz
|
||||
├── arch-latest-riscv64.tar.gz
|
||||
├── alpine-latest-x86_64.tar.gz
|
||||
├── debian-bookworm-amd64.tar.gz
|
||||
└── ...
|
||||
```
|
||||
|
||||
@@ -45,54 +46,82 @@ No metadata files. Tarballs are downloaded once and never redownloaded. Users ca
|
||||
```yaml
|
||||
dns:
|
||||
- 1.1.1.1
|
||||
cache_dir: ~/.cache/ecr
|
||||
```
|
||||
|
||||
## Distro Sources
|
||||
|
||||
| Distro | Version Format | URL Pattern |
|
||||
|--------|----------------|-------------|
|
||||
| Ubuntu | noble, jammy, mantic or 26.04, 25.10, 22.04, latest, lts | https://cdimage.ubuntu.com/ubuntu-base/bionic/daily/current/bionic-base-amd64.tar.gz |
|
||||
| Debian | bookworm, bullseye, sid, latest | https://cloud.debian.org/images/cloud/trixie/latest/debian-13-nocloud-amd64.tar.xz |
|
||||
| Arch | latest | https://geo.mirror.pkgbuild.com/iso/latest/archlinux-bootstrap-x86_64.tar.zst |
|
||||
| Alpine | 3.20, 3.19, latest, edge | https://dl-cdn.alpinelinux.org/alpine/latest-stable/releases/x86_64/alpine-minirootfs-3.23.0-x86_64.tar.gz |
|
||||
| Fedora | 40, 41, rawhide, latest | Extract from `registry.fedoraproject.org/fedora:<version>` |
|
||||
### Direct Tarball Downloads
|
||||
|
||||
| Distro | Version Format | Source |
|
||||
|--------|----------------|--------|
|
||||
| Ubuntu | noble, jammy, mantic or 26.04, 25.10, 22.04, latest, lts | cdimage.ubuntu.com |
|
||||
| Alpine | 3.20, 3.19, latest, edge | dl-cdn.alpinelinux.org |
|
||||
|
||||
### Docker Hub (OCI Registry)
|
||||
|
||||
All other distributions use Docker Hub images via OCI registry API:
|
||||
|
||||
| Distro | Image Reference |
|
||||
|--------|-----------------|
|
||||
| Debian | `library/debian` |
|
||||
| Arch | `library/archlinux` |
|
||||
| Fedora | `library/fedora` |
|
||||
| Gentoo | `gentoo/stage3` |
|
||||
| Custom | `<image>[:tag]` or `<registry>/<image>[:tag]` |
|
||||
|
||||
### Custom Image References
|
||||
|
||||
Users can specify any OCI-compatible image:
|
||||
|
||||
```
|
||||
ecr debian:bookworm -- ./build.sh
|
||||
ecr gentoo/stage3 -- emerge --sync
|
||||
ecr gcr.io/my-project/my-image:v1.0 -- /app/test
|
||||
```
|
||||
|
||||
### Architecture Mapping
|
||||
|
||||
| ecr | Ubuntu | Debian | Arch | Alpine | Fedora |
|
||||
|-----|--------|--------|------|--------|--------|
|
||||
| amd64 | amd64 | amd64 | x86_64 | x86_64 | x86_64 |
|
||||
| arm64 | arm64 | arm64 | aarch64 | aarch64 | aarch64 |
|
||||
| armhf | armhf | armhf | - | armv7 | - |
|
||||
| riscv64 | riscv64 | riscv64 | riscv64 | riscv64 | riscv64 |
|
||||
| ppc64el | ppc64el | ppc64el | - | ppc64le | ppc64le |
|
||||
| s390x | s390x | s390x | - | s390x | s390x |
|
||||
| ecr | Ubuntu | Alpine | Docker Hub |
|
||||
|-----|--------|--------|------------|
|
||||
| amd64 | amd64 | x86_64 | amd64 |
|
||||
| arm64 | arm64 | aarch64 | arm64 |
|
||||
| armhf | armhf | armv7 | arm/v7 |
|
||||
| riscv64 | riscv64 | riscv64 | riscv64 |
|
||||
| ppc64el | ppc64el | ppc64le | ppc64le |
|
||||
| s390x | s390x | s390x | s390x |
|
||||
|
||||
### Fedora Container Extraction
|
||||
### OCI Image Download
|
||||
|
||||
For Fedora, download the container image layer and extract:
|
||||
For Docker Hub images:
|
||||
|
||||
1. Query manifest: `GET https://registry.fedoraproject.org/v2/fedora/manifests/<version>`
|
||||
2. Parse manifest, get layer digest
|
||||
3. Download layer blob
|
||||
4. Extract tarball
|
||||
1. Get anonymous bearer token from `https://auth.docker.io/token`
|
||||
2. Query manifest list: `GET https://registry.hub.docker.com/v2/<repo>/manifests/<tag>`
|
||||
3. Select manifest matching target architecture
|
||||
4. Download layer blobs with authentication
|
||||
5. Extract layers to rootfs
|
||||
|
||||
If architecture is not available in manifest list, error with available architectures:
|
||||
|
||||
```
|
||||
Error: No manifest found for architecture 'riscv64'. Available: amd64, arm64, ppc64le, s390x
|
||||
```
|
||||
|
||||
## Execution Flow
|
||||
|
||||
1. Parse CLI arguments and config file
|
||||
2. Resolve distro/version/arch to tarball URL
|
||||
1. Parse CLI arguments
|
||||
2. Resolve distro/version/arch to image source
|
||||
3. Check cache for existing tarball
|
||||
4. If not cached, download tarball
|
||||
4. If not cached, download tarball (direct or OCI)
|
||||
5. Create temp directory for extraction
|
||||
6. Extract tarball to temp directory
|
||||
7. Create namespaces: user, pid, mount, uts
|
||||
8. Set up mounts: /proc, /sys (ro), /dev, /dev/pts
|
||||
9. Write /etc/resolv.conf with DNS servers
|
||||
10. Set up overlay mount for workspace directory
|
||||
11. Set environment variables
|
||||
12. Exec shell or command in chroot
|
||||
13. On exit, clean up temp directory
|
||||
10. Set up overlay mounts for bind paths
|
||||
11. Set up read-write bind mounts
|
||||
12. Set environment variables
|
||||
13. Exec shell or command in chroot
|
||||
14. On exit, clean up temp directory
|
||||
|
||||
## Namespace Setup
|
||||
|
||||
@@ -125,6 +154,7 @@ This makes the user appear as root inside the chroot while remaining unprivilege
|
||||
| /dev | devtmpfs | nosuid |
|
||||
| /dev/pts | devpts | nosuid,noexec |
|
||||
| /root/<basename> | overlay | lowerdir=<bind_path>, upperdir=<temp>, workdir=<temp> |
|
||||
| /mnt/<basename> | bind | rw (for --bind-rw) |
|
||||
| /etc/resolv.conf | file | written with DNS |
|
||||
|
||||
## QEMU Integration
|
||||
@@ -165,6 +195,8 @@ Overlay configuration:
|
||||
|
||||
Changes made inside the chroot are written to upperdir and discarded on exit. The host directory is never modified.
|
||||
|
||||
Multiple `--bind` paths can be specified, each creates an overlay at `/root/<basename>`.
|
||||
|
||||
Example:
|
||||
```
|
||||
$ cd ~/projects/myapp
|
||||
@@ -175,7 +207,11 @@ $ ecr ubuntu:noble -- make build
|
||||
|
||||
### Read-Write Bind Mount
|
||||
|
||||
`--bind-rw <path>` bypasses overlay and creates a true read-write bind mount at `/mnt/<basename>`. This modifies the host filesystem directly. Use with caution.
|
||||
`--bind-rw <path>` creates a true read-write bind mount at `/mnt/<basename>`. This modifies the host filesystem directly. Use with caution.
|
||||
|
||||
Multiple `--bind-rw` paths can be specified. If a path is specified in both `--bind` and `--bind-rw`, the read-write mount takes precedence.
|
||||
|
||||
If no path is specified, defaults to current working directory.
|
||||
|
||||
### No Mount
|
||||
|
||||
@@ -189,7 +225,7 @@ Default DNS server is 1.1.1.1. Configured via `/etc/resolv.conf` in chroot:
|
||||
nameserver 1.1.1.1
|
||||
```
|
||||
|
||||
Override with `--dns` flag or config file:
|
||||
Override with config file (`~/.config/ecr.yaml`):
|
||||
|
||||
```yaml
|
||||
dns:
|
||||
@@ -227,26 +263,3 @@ Enable with:
|
||||
|
||||
Or check AppArmor profile restrictions.
|
||||
```
|
||||
|
||||
### No Root Fallback
|
||||
|
||||
There is no fallback to running as real root. The tool is designed for unprivileged use.
|
||||
|
||||
## Implementation
|
||||
|
||||
### Language
|
||||
|
||||
Rust.
|
||||
|
||||
### Dependencies
|
||||
|
||||
- `nix`: Unix syscall bindings (clone, unshare, mount, chroot, namespaces)
|
||||
- `serde` + `serde_yaml`: config parsing
|
||||
- `reqwest`: HTTP downloads
|
||||
- `tar`: tarball extraction
|
||||
- `xz2`: xz decompression
|
||||
- `zstd`: zstd decompression
|
||||
- `flate2`: gzip decompression
|
||||
- `clap`: CLI parsing
|
||||
- `signal-hook`: signal handling
|
||||
- `tempfile`: temp directories
|
||||
Reference in New Issue
Block a user