Skip to content

Commit

Permalink
More bug squashing
Browse files Browse the repository at this point in the history
Implement list and install
Add some images to README
  • Loading branch information
Colonial-Dev committed Sep 9, 2024
1 parent 0b50bb4 commit 25c4c19
Show file tree
Hide file tree
Showing 12 changed files with 170 additions and 70 deletions.
Binary file added .github/README_B.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
91 changes: 82 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,23 @@
## Features
Easily create and manage container environments for interactive use. All host integration is strictly opt-in; you choose what (if anything) is shared with each container.


<p align="center">
<img src=".github/demo.gif">
</p>

Bring your existing Docker-style container definitions...

{Image or ASCIInema}
<p align="center">
<img src=".github/README_B.png">
</p>

... or take advantage of Fishtank's custom shell-based format that bundles together all the information needed to build *and* run your containers.

<p align="center">
<img src=".github/README_A.png">
</p>

Lightweight, easy to install, and works on almost any Linux machine with `podman`.
Lightweight[^1], easy to install, and works on any[^2] Linux machine with `podman` and `fish`,

{Image or ASCIInema}

Expand All @@ -33,15 +37,15 @@ Before installing, make sure you have the following packages on your system:
- `coreutils`

```sh
curl --proto '=https' --tlsv1.2 -sSf $URL | source - install self
curl -Lf https://github.com/Colonial-Dev/fishtank/releases/latest/download/tankctl | source - install
```

This downloads the latest stable version of Fishtank and uses the self-update functionality to bootstrap a persistent install.

By default, this installs two scripts (`tankctl` and `tankcfg`) in `$XDG_CONFIG_HOME` (by default, `$HOME/.local/bin`) - to override the install location, simply pass your preferred path as the third argument:
By default, this installs two scripts (`tankctl` and `tankcfg`) in the XDG-specified `$HOME/.local/bin` directory - to override the install location, simply pass your preferred path as the third argument:

```sh
curl --proto '=https' --tlsv1.2 -sSf $URL | source - install self /usr/bin
curl -Lf https://github.com/Colonial-Dev/fishtank/releases/latest/download/tankctl | source - install /usr/bin
```

### From Source
Expand All @@ -54,6 +58,68 @@ git clone https://github.com/Colonial-Dev/fishtank && cd fishtank

## Getting Started

Fishtank requires a definition ("tank") for each container you'd like to create. Definitions can be in two different formats:
- Standard Container/Dockerfiles - just add `# fishtank containerfile` to the text and you're good to go.
- `fish` shell scripts that run in a special harness. This injects additional functions and wraps a few others to provide additional functionality not present in Containerfiles, like the ability to declare runtime arguments such as mounts.

Either type must be stored under `$XDG_CONFIG_HOME/fishtank/` (typically `~/.config/fishtank/`) with the file extension `.tank`.

To create and edit a new definition, you can simply run `tankctl create <NAME>`. This will create the file and open it using your `$EDITOR`.

`tankctl edit <NAME>` can be used to alter existing definitions; both commands will use a temporary file for editing and perform syntax checks before finalizing any changes.

Shell-based definitions run in the same directory as the definition, and should look something like this:

```sh
# Create a new working container.
set ctr (buildah from fedora-toolbox:latest)

# Set up the new container...
RUN dnf install gcc

# Commit the configured container as an image.
buildah commit $ctr toolbox
```

The harness for shell-based definitions enables two primary toolkits for setting up your container.
- All Containerfile directives like `RUN` and `ADD` are polyfilled as Fish functions, and generally act the same as their real counterparts.
- (The most notable exception is pipes and redirections in `RUN` - you must wrap them in an `sh -c` to execute them wholly inside the working container.)
- The `tankcfg` script, which lets you:
- Set various build-time (some of which are duplicated from the above) and runtime switches
- Provide arbitrary additional arguments to pass to `podman run`
- Apply several prepackaged presets (such as copying a user from the host into the container, or applying security options to fix bind mounts with SELinux)

Click to show all `tankcfg` options:

```sh
tankcfg entrypoint
tankcfg env
tankcfg healthcheck
tankcfg hostname
tankcfg port
tankcfg shell
tankcfg user
tankcfg volume
tankcfg workingdir

tankcfg args
tankcfg cap-add
tankcfg cap-drop
tankcfg cpus
tankcfg ram
tankcfg ulimit
tankcfg device
tankcfg userns
tankcfg security-opt
tankcfg mount
tankcfg restart
tankcfg secret

tankcfp preset cp-user
tankcfg preset bind-fix
tankcfg preset ssh-agent
```

## FAQ

### "Why `fish` instead of POSIX `sh`?"
Expand All @@ -63,7 +129,7 @@ More seriously - I started this as an excuse to learn some scripting after migra

If you prefer a different shell, you can still use Fishtank! The scripts are self-contained and properly shebanged, so simply installing `fish` and placing them somewhere in your `$PATH` should work fine.

### How does this compare to Toolbx or Distrobox?
### "How does this compare to Toolbx or Distrobox?"
It depends!

I used to heavily rely on Toolbx for my development environments, and I also dabbled with Distrobox. Both are excellent tools, but I have one big gripe with both: host integration.
Expand All @@ -73,7 +139,14 @@ I used to heavily rely on Toolbx for my development environments, and I also dab

Fishtank, by contrast, is entirely opt-in when it comes to host integrations. You get to choose precisely what (if anything) is shared.

Fishtank also offers a custom container definition format ("tanks") that bundle together information on how to build *and* run the container.
Fishtank also requires that every container be associated with a "definition," rather than defaulting to a standard "toolbox" image for each container. These can either be standard Containerfiles, or they can use Fishtank's custom shell-based format to declare runtime arguments (like mounts)[^3] during build time.

So:
- If you don't mind the above caveats and want containerized environments that Just Work with the host, use Toolbx or Distrobox.
- If you *do* mind the above caveats and/or want some declarative-ness in your containers, give Fishtank a try.

[^1]: Only ~1000 lines of pure Fish shell code.

This works by baking information you would normally see in a `docker-compose` file or Kubernetes YAML (mounts, security options...) into the image as OCI annotations, which are then read back and applied during container creation.
[^2]: Fishtank was developed on a system that uses GNU Coreutils and GNU `libc` - if you find that Fishtank doesn't work with alternative implementations, please file an issue!

[^3]: If you are wondering how this works: Fishtank bakes the arguments you provide at build time into the image using OCI annotations, then reads them out and applies them when creating a container from the image.
11 changes: 0 additions & 11 deletions TODO.md

This file was deleted.

1 change: 1 addition & 0 deletions src/common/00-header.fish
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

2 changes: 1 addition & 1 deletion src/common/10-util.fish
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ end
# Checks if the provided command exists in $PATH,
# aborting with an error message if not.
function require -a command
if not type -P "$command" >/dev/null 2>&1
if not command -q "$command"
eprintf "runtime dependency '$command' not found in \$PATH"
exit 1
end
Expand Down
33 changes: 8 additions & 25 deletions src/common/20-trap.fish
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
function arm -a victim -d "Arm a non-zero exit code trap for the provided executable."
# This function remains in scope, even after 'trap' ends.
function $victim -V victim -w $victim
# Capture both stderr and stdout, using quoted substitution
# to avoid splitting into a list.
set -l output "$(command $victim $argv 2>&1)"
command $victim $argv
# Capture the status.
set -l stat $status
set -l s $status

# If the status is not zero, we've trapped an error.
if [ "$stat" -ne 0 ]
error $victim $stat "$argv" "$output"
if [ $s -ne 0 ]
error $victim $s "$argv"
else
# If the status is zero, all is well. Print the captured output and return.
printf "%s" "$(echo $output)"
# If the status is zero, all is well.
return 0
end
end
end
Expand All @@ -30,30 +28,15 @@ function disarm -d "Remove a non-zero exit code trap for the provided executable
end

# Pretty-prints an error.
function error -a victim stat args output
# Split the output into a list (on newlines.)
set -l output (echo $output)

function error -a victim stat args
# Write out the command, arguments, and exit code.
printf "%serror trapped%s\n" (set_color red -o) (set_color normal) >&2
printf "├── command\t%s\n" "$victim" >&2
printf "├── argv \t%s\n" "$args" >&2
printf "├── code \t%s\n" "$stat" >&2
printf "└── " >&2
printf "└── code \t%s\n" "$stat" >&2

echo -n (set_color brblack) >&2

# Write out the output (pretty-formatted) if any exists.
if [ -z "$output" ]
printf "(no output)\n" >&2
else
printf "%s\n" $output[1] >&2

for line in $output[2..]
printf " %s\n" $line >&2
end
end

# Print backtrace, if enabled.
if [ -n "$fish_backtrace" ]
echo -n (set_color normal) >&2
Expand Down
3 changes: 0 additions & 3 deletions src/tankcfg/00-help.fish

This file was deleted.

5 changes: 1 addition & 4 deletions src/tankcfg/10-main.fish → src/tankcfg/00-main.fish
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# TODO use argparse to make these not suck
function RUN
buildah run $__FISHTANK_BUILD_CTR $argv
end
Expand Down Expand Up @@ -65,8 +66,6 @@ end
function tankcfg_preset -a preset
switch $preset
case cp-user
set -l USER

if [ (count $argv) -gt 1 ]
set USER $argv[2]
else
Expand Down Expand Up @@ -116,7 +115,6 @@ if [ -z "$__FISHTANK_IN_BUILD" ]
end

if [ -z "$argv[1]" ]
tankcfg_help
abort "no subcommand specified"
else if functions -q "tankcfg_$argv[1]"
tankcfg_$argv[1] $argv[2..]
Expand All @@ -125,6 +123,5 @@ else if contains "$argv[1]" $__CONFIG_FLAGS
else if contains "$argv[1]" $__ANNOTATIONS
p_annotation "fishtank.$argv[1]" "$argv[2..]"
else
tankcfg_help
abort "unknown subcommand '$argv[1]'"
end
4 changes: 4 additions & 0 deletions src/tankctl/00-help.fish
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# TODO all help for commands (automate somehow?)
# TODO man pages/Github wiki docs
# TODO completions

function tankctl_help
echo "\
A simple interactive container manager for the fish shell.
Expand Down
31 changes: 19 additions & 12 deletions src/tankctl/10-podman.fish
Original file line number Diff line number Diff line change
Expand Up @@ -60,37 +60,45 @@ function check_ctr -a ctr
end
end

function make_ctr -a img replace
function make_ctr -a img
set -l name (img_annotation $img "fishtank.name")
set -l hash (img_annotation $img "fishtank.hash")

set -l command podman run -d

set -a command --name $name
set -a command --hostname $name

for a in $__ANNOTATIONS
if [ $a = args ]
continue
end

for entry in (img_annotation $img "fishtank.$a" | string split \x1F)
set -a command "--$a"
set -a command $entry
if [ -n "$entry" ]
set -a command "--$a"
set -a command $entry
end
end
end

set -a command --name $name
set -a command --hostname $name

for entry in (img_annotation $img "fishtank.args" | string split \x1F)
set -a command $entry
if [ -n "$entry" ]
set -a command $entry
end
end

set -a command --annotation "manager=fishtank"
set -a command --annotation "fishtank.name=$name"
set -a command --annotation "fishtank.hash=$hash"

if set -q replace
if [ -n "$_flag_replace" ]
set -a command --replace
end

$command $img
printf " (%s)\n" $name
set -a command "$img"

printf "%s (%s)\n" ($command) $name
end

function start_ctr -a ctr
Expand All @@ -107,6 +115,5 @@ end

function rm_ctr -a ctr
set -l name (ctr_annotation $ctr "fishtank.name")
podman rm -ft 0 $ctr
printf " (%s)\n" $name
printf "%s (%s)\n" (podman rm -ft 0 $ctr) $name
end
Loading

0 comments on commit 25c4c19

Please sign in to comment.