poco

command module
v0.0.0-...-32f50fd Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 3, 2023 License: GPL-3.0 Imports: 15 Imported by: 0

README ΒΆ


logo
poCo

Containers -> Binaries
Create statically linked, portable binaries from container images

license go report card


A simple, static golang bundler!

poCo (portable-Containers) packs and converts container images into single, portable, statically linked binaries leveraging golang native embed.

❓ How it works

poCo is extremely simple in the design.

poCo generates and builds golang code which embeds the container content compressed as an asset. It does bundle the assets by using the native golang embed primitives. The resulting binary on the first run will extract the content over the application store and will execute the entrypoint in a pivotroot environment, without requiring special permissions.

πŸ’» Install

Download poCO from the releases and install it in your PATH. poCO releases are statically built, so no dependencies (besides golang to create bundles, are required)

πŸƒ Run

poCO is a no-frills binary bundler, we will see an example of how to bundle a container image into a binary.

Requires:

  • poco installed
  • sudo
  • golang >1.17 installed in the system where are you building

poCo bundles container images available remotely or locally (by specifying --local to the bundle subcommand).

For instance to pack the alpine image into a sample binary is as simple as:

CGO_ENABLED=0 ./poco bundle --image alpine --output sample

CGO_ENABLED=0 will instruct the golang compiler behind the scenes to create a statically linked executable.

We can specify optionally a default entrypoint for the resulting binary with --entrypoint, which is by default set to /bin/sh.

You can run the --help subcommand on sample to inspect its output, and you will see there are available few options:

❯ ./sample --help     
NAME:
   sample - sample

USAGE:
    [global options] command [command options] [arguments...]

VERSION:
   0.1

DESCRIPTION:
   sample

AUTHOR:
   sample

COMMANDS:
   exec       
   uninstall  
   help, h    Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --store value       Default application store. Empty for TMPDIR
   --entrypoint value  Default application entrypoint (default: "/bin/sh")
   --add-mounts value  Additional mountpoints
   --mounts value      Default app mountpoints (default: "/sys", "/tmp", "/run")
   --help, -h          show help
   --version, -v       print the version

The binary has some defaults that can be override during build time with bundle, to run the application (in our case, sh from alpine), just run:

./sample # spawns a new /bin/sh shell

You can also pass all the args to the entrypoint of the binary (/bin/sh), by specifying -:

./sample - -c "echo foo"

See the example/ folder for a more complete example.

Supports: CGO_ENABLED, GOOS, GOARCH, etc.

It can target all architectures supported by golang.

Github Action

To run it with Github actions, see https://github.com/skrashevich/poco-github-action

bundle

bundle creates a binary from a container image, it takes several options listed here:

Flag Description
--entrypoint Default binary entrypoint. This is the first binary from the container image which will be executed. It defaults to /bin/sh
--output Default binary output location
--compression Compression format used to pack the container image into the bundle. Supported formats: (bz2, zst, gz, xz, lz4, br, sz)
--app-description This is the description of the app that will be displayed in the resulting binary --help
--app-author This is the author of the app that will be displayed in the resulting binary --help
--app-name This is the name of the app that will be displayed in the resulting binary --help
--app-version This is the version of the app that will be displayed in the resulting binary --help. The version will be used between different binary bundles to handle upgrades.
--local Tells poco to get the container image from the local Docker daemon instead of fetching it remotely. By default poco doesn't require a Docker daemon running locally
--app-mounts A list of default mount binding for the app. The application runs in a chroot-alike environment, without access to the files of the system unless explictly mounted. Multiple mounts can be specified.
--app-store A default store for your app. This is where the bundle gets extracted before being executed, and where the real app data lives afterward on subsequent calls.
--image The container image to bundle.
--command-prefix Command prefix for auto-generated code. Usually you don't need to change that unless you are running the builds as root
--directory A directory to bundle (in place of the container image)
Mounts

A poCo bundle runs in a sandboxed environment. To expose directories or files, the resulting binary in runtime tales the --mounts or --add-mounts option (also multiple times) to specify a list of directories or files to expose from the host environment.

While creating the binary, it is also possible to specify a default set, so the binary runs with the directory shared from the host already, this is possible by passing --app-mounts.

For instance, consider:

CGO_ENABLED=0 ./poco bundle --image alpine --output sample --app-mounts /tmp --app-mounts 'ro:/home/.bar:/home/.bar'

will create a sample binary with alpine which /tmp will be mapped rw and /home/.bar ro from the host.

Default store

Every application has a default store. By default, each application will unpack its content to a temporary directory. To change this behavior and persist data in the system which is running the app, specify a default location with --app-store.

For instance, the following will use the ~/.poco/alpine folder to unpack the bundle content on the first run:

CGO_ENABLED=0 ./poco bundle --image alpine --output sample --app-store '$HOME/.poco/alpine'

Every application can indeed be uninstalled, which just deletes the default app-store:

./sample uninstall
Metadata

Every generated bundle will have a default --help which is being displayed. It is possible to set metadata such as description, name, author, copyright that will be automatically available in the resulting binary --help.

The version is more relevant if a default --app-store is being specified. The app-version is used during the first run to determine if the installed bundle should be replaced or not.

render

render allows to render the generated golang code into a specified directory. This is might be helpful if you want to change the generated binary before build.

$ mkdir alpine
$ ./poco render --image alpine alpine
$ ls alpine/
go.mod main.go

pack

pack is an internal utility to pack directories as container images that can be docker loaded afterwards:

$ mkdir foo
$ touch foo/bar
$ poco pack myimage:tag foo --destination output.tar
$ docker load -i output.tar
$ docker push myimage:tag

extract

extract is an internal utility to scan a binary and all its dynamic linked libraries. It will copy the binary and the libraries needed by it into the specified folder, respecting the path hierarchy.

$ poco extract /my/dynamic/bin /output
$ poco pack myimage:tag /output --destination image.tar
$ docker load -i image.tar
...

pack-assets

pack-assets is an internal utility to pack assets for the bundle.

$ mkdir foo
$ touch foo/bar
$ poco pack-assets -C foo .
$ ls
assets.tar.xz

unpack

unpack is an internal utility to unpack a container image into a directory

$ mkdir alpine
$ poco unpack alpine alpine
$ ls alpine
bin etc usr ...
$ poco bundle --directory alpine ...

πŸ““ Troubleshooting

When troubleshooting issues with bundles created by poco, it might be helpful to open a shell within a bundle:

./<bundle> --entrypoint /bin/sh

The --entrypoint command is available in all binaries generated by poco and as such you can override the default entrypoint anytime.

NOTE The --mounts command is also available in all binaries generated by poco and will OVERRIDE the default mount points specified during bundle time. To have additional mounts besides default, use --add-mounts instead.

⚠ Notes

  • During build sudo is required in order to preserve container permissions.
  • By default bundles do have network access, but as Docker images mount during build /etc/resolv.conf, the file is empty in docker images, which results in no ability to resolv network address. In order to let the bundle read the /etc/resolv.conf from the host, use --app-mounts /etc/resolv.conf.

πŸ” Examples

See the examples folder or linuxbundles for a collection of popular distributions packaged or either caramel for a more complete example involving popular apps.

The pipeline builds the firefox image which can be downloaded and to run locally as a standard binary:

./firefox

To uninstall:

./firefox uninstall

πŸ““ TODO

  • Multi-platform support (Windows, MacOS at least..)

❓ Why?

Β―_(ツ)_/Β―

Someone told me this wasn't possible, so here we are.

I know there is flatpak and also tons of AppImages out there and this is just yet another bundler for most of you, so to make it clear: the scope of this project is not even comparable to them.

While I was sketching this up I realized I wanted something VERY simple that doesn't gets in the way and opinionated enough that can leverage already existing container image - without the need of additional docs or specific procedures for users. This bundler might fit just simple and specific purposes - and most likely - people like me that doesn't have high end goals and rely on golang daily.

So focus of this project is - to prove a point of course - and on semplicity and portability rather than, for example, security.

And besides, let's be frank. Building bundles with go+docker is really easy to go with as a stack.

πŸ““ Credits

Docker authors for the pivotroot code part, was very helpful read to get that right.

🐜 Contribution

You can improve this project by contributing in following ways:

  • report bugs
  • fix issues
  • request features
  • asking questions (just open an issue)

and any other way if not mentioned here.

πŸ““ Author

poCo is released under GPL-3, Copyright Ettore Di Giacinto [email protected]

Documentation ΒΆ

The Go Gopher

There is no documentation for this package.

Directories ΒΆ

Path Synopsis
pkg

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL