diff --git a/.gitignore b/.gitignore index 3cac772..33b501d 100644 --- a/.gitignore +++ b/.gitignore @@ -49,7 +49,10 @@ cross/ coverage.txt profile.out -*.tar rootfs config.json .ip +*.tar + +busybox +alpine diff --git a/Makefile b/Makefile index 6dd69a6..16a5553 100644 --- a/Makefile +++ b/Makefile @@ -1,45 +1,31 @@ # Set an output prefix, which is the local directory if not specified PREFIX?=$(shell pwd) -# Setup name variables for the package/tool -NAME := binctr -PKG := github.com/genuinetools/$(NAME) - # Set any default go build tags BUILDTAGS := seccomp apparmor -# Set the build dir, where built cross-compiled binaries will be output -BUILDDIR := ${PREFIX}/cross - -IMAGE := alpine -IMAGE_DATA_FILE := container/bindata.go - -# Populate version variables -# Add to compile time flags -VERSION := $(shell cat VERSION.txt) -GITCOMMIT := $(shell git rev-parse --short HEAD) -GITUNTRACKEDCHANGES := $(shell git status --porcelain --untracked-files=no) -ifneq ($(GITUNTRACKEDCHANGES),) - GITCOMMIT := $(GITCOMMIT)-dirty -endif -CTIMEVAR=-X $(PKG)/version.GITCOMMIT=$(GITCOMMIT) -X $(PKG)/version.VERSION=$(VERSION) -GO_LDFLAGS=-ldflags "-w $(CTIMEVAR)" -GO_LDFLAGS_STATIC=-ldflags "-w $(CTIMEVAR) -extldflags -static" +GO_LDFLAGS_STATIC=-ldflags "-w -extldflags -static" all: clean build fmt lint test staticcheck vet ## Runs a clean, build, fmt, lint, test, staticcheck, and vet .PHONY: build -build: $(BUILDDIR)/$(notdir $(IMAGE)) ## Builds a static executable or package +build: alpine busybox ## Builds a static executable or package -$(BUILDDIR): - @mkdir -p $@ - -$(BUILDDIR)/$(notdir $(IMAGE)): $(BUILDDIR) $(IMAGE_DATA_FILE) *.go VERSION.txt +.PHONY: alpine +alpine: generate @echo "+ $@" CGO_ENABLED=1 go build \ -tags "$(BUILDTAGS) static_build" \ - ${GO_LDFLAGS_STATIC} -o $@ . - @echo "Static container for $(IMAGE) created at: $@" + ${GO_LDFLAGS_STATIC} -o $@ ./examples/$@/... + @echo "Static container for alpine created at: ./$@" + +.PHONY: busybox +busybox: generate + @echo "+ $@" + CGO_ENABLED=1 go build \ + -tags "$(BUILDTAGS) static_build" \ + ${GO_LDFLAGS_STATIC} -o $@ ./examples/$@/... + @echo "Static container for alpine created at: ./$@" .PHONY: fmt fmt: ## Verifies all files have men `gofmt`ed @@ -95,15 +81,14 @@ tag: ## Create a new git tag to prepare to build a release git tag -sa $(VERSION) -m "$(VERSION)" @echo "Run git push origin $(VERSION) to push your new tag to GitHub and trigger a travis build." -.PHONY: $(IMAGE_DATA_FILE) -$(IMAGE_DATA_FILE): - GOMAXPROCS=1 go generate +.PHONY: generate +generate: + GOMAXPROCS=1 go generate ./... .PHONY: clean clean: ## Cleanup any build binaries or packages @echo "+ $@" - $(RM) $(NAME) - $(RM) -r $(BUILDDIR) + $(RM) alpine busybox @sudo $(RM) -r rootfs .PHONY: help diff --git a/README.md b/README.md index ca7d7db..ee5abef 100644 --- a/README.md +++ b/README.md @@ -17,11 +17,6 @@ got rootless containers into upstream! Woohoo! Check out the original thread on the [mailing list](https://groups.google.com/a/opencontainers.org/forum/#!topic/dev/yutVaSLcqWI). -**Nginx running with my user "jessie".** - -![nginx.png](nginx.png) - - ### Building You will need `libapparmor-dev` and `libseccomp-dev`. @@ -30,14 +25,13 @@ Most importantly you need userns in your kernel (`CONFIG_USER_NS=y`) or else this won't even work. ```console -$ make build -Static container created at: ./bin/alpine -Run with ./bin/alpine +# building the alpine example +$ make alpine +Static container created at: ./alpine -# building a different base image -$ make build IMAGE=busybox -Static container created at: ./bin/busybox -Run with ./bin/busybox +# building the busybox example +$ make alpine +Static container created at: ./busybox ``` ### Running @@ -47,42 +41,6 @@ $ ./alpine $ ./busybox --read-only ``` -### Usage - -```console - _ _ _ -| |__ (_)_ __ ___| |_ _ __ -| '_ \| | '_ \ / __| __| '__| -| |_) | | | | | (__| |_| | -|_.__/|_|_| |_|\___|\__|_| - - Fully static, self-contained container including the rootfs - that can be run by an unprivileged user. - - Embedded Image: alpine - sha256:3fd9065eaf02feaf94d68376da52541925650b81698c53c6824d92ff63f98353 - Version: 0.1.0 - Build: 91b3ab5-dirty - - -D run in debug mode - -console-socket string - path to an AF_UNIX socket which will receive a file descriptor referencing the master end of the console's pseudoterminal - -d detach from the container's process - -hook value - Hooks to prefill into spec file. (ex. --hook prestart:netns) - -id string - container ID - -pid-file string - specify the file to write the process id to - -read-only - make container filesystem readonly - -root string - root directory of container state, should be tmpfs (default "/tmp/binctr") - -t allocate a tty for the container (default true) - -v print version and exit (shorthand) - -version - print version and exit -``` - ## Cool things The binary spawned does NOT need to oversee the container process if you diff --git a/VERSION.txt b/VERSION.txt deleted file mode 100644 index 6e8bf73..0000000 --- a/VERSION.txt +++ /dev/null @@ -1 +0,0 @@ -0.1.0 diff --git a/generate.go b/generate.go deleted file mode 100644 index 597d89e..0000000 --- a/generate.go +++ /dev/null @@ -1,18 +0,0 @@ -// +build ignore - -package main - -import ( - "fmt" - "os" - - "github.com/genuinetools/binctr/container" -) - -// Pulls an image and saves the binary data in the container package bindata.go. -func main() { - if err := container.EmbedImage("alpine"); err != nil { - fmt.Fprintf(os.Stderr, "embed image failed: %v\n", err) - os.Exit(1) - } -} diff --git a/main.go b/main.go deleted file mode 100644 index 2790e7b..0000000 --- a/main.go +++ /dev/null @@ -1,198 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "os" - "os/exec" - "runtime" - "strings" - - "github.com/genuinetools/binctr/container" - "github.com/genuinetools/binctr/version" - "github.com/opencontainers/runc/libcontainer" - _ "github.com/opencontainers/runc/libcontainer/nsenter" - specs "github.com/opencontainers/runtime-spec/specs-go" - "github.com/sirupsen/logrus" -) - -const ( - // BANNER is what is printed for help/info output. - BANNER = ` _ _ _ -| |__ (_)_ __ ___| |_ _ __ -| '_ \| | '_ \ / __| __| '__| -| |_) | | | | | (__| |_| | -|_.__/|_|_| |_|\___|\__|_| - - Fully static, self-contained container including the rootfs - that can be run by an unprivileged user. - - Version: %s - Build: %s - -` - - defaultRoot = "/tmp/binctr" - defaultRootfsDir = "rootfs" -) - -var ( - containerID string - pidFile string - root string - - allocateTty bool - consoleSocket string - detach bool - readonly bool - - hooks specs.Hooks - hookflags stringSlice - - debug bool - vrsn bool -) - -// stringSlice is a slice of strings -type stringSlice []string - -// implement the flag interface for stringSlice -func (s *stringSlice) String() string { - return fmt.Sprintf("%s", *s) -} -func (s *stringSlice) Set(value string) error { - *s = append(*s, value) - return nil -} -func (s stringSlice) ParseHooks() (hooks specs.Hooks, err error) { - for _, v := range s { - parts := strings.SplitN(v, ":", 2) - if len(parts) <= 1 { - return hooks, fmt.Errorf("parsing %s as hook_name:exec failed", v) - } - cmd := strings.Split(parts[1], " ") - exec, err := exec.LookPath(cmd[0]) - if err != nil { - return hooks, fmt.Errorf("looking up exec path for %s failed: %v", cmd[0], err) - } - hook := specs.Hook{ - Path: exec, - } - if len(cmd) > 1 { - hook.Args = cmd[:1] - } - switch parts[0] { - case "prestart": - hooks.Prestart = append(hooks.Prestart, hook) - case "poststart": - hooks.Poststart = append(hooks.Poststart, hook) - case "poststop": - hooks.Poststop = append(hooks.Poststop, hook) - default: - return hooks, fmt.Errorf("%s is not a valid hook, try 'prestart', 'poststart', or 'poststop'", parts[0]) - } - } - return hooks, nil -} - -func init() { - // Parse flags - flag.StringVar(&containerID, "id", "binctr", "container ID") - flag.StringVar(&pidFile, "pid-file", "", "specify the file to write the process id to") - flag.StringVar(&root, "root", defaultRoot, "root directory of container state, should be tmpfs") - - flag.Var(&hookflags, "hook", "Hooks to prefill into spec file. (ex. --hook prestart:netns)") - - flag.BoolVar(&allocateTty, "t", true, "allocate a tty for the container") - flag.StringVar(&consoleSocket, "console-socket", "", "path to an AF_UNIX socket which will receive a file descriptor referencing the master end of the console's pseudoterminal") - flag.BoolVar(&detach, "d", false, "detach from the container's process") - flag.BoolVar(&readonly, "read-only", false, "make container filesystem readonly") - - flag.BoolVar(&vrsn, "version", false, "print version and exit") - flag.BoolVar(&vrsn, "v", false, "print version and exit (shorthand)") - flag.BoolVar(&debug, "D", false, "run in debug mode") - - flag.Usage = func() { - fmt.Fprint(os.Stderr, fmt.Sprintf(BANNER, version.VERSION, version.GITCOMMIT)) - flag.PrintDefaults() - } - - flag.Parse() - - if vrsn { - fmt.Printf("%s, commit: %s", version.VERSION, version.GITCOMMIT) - os.Exit(0) - } - - // Set log level. - if debug { - logrus.SetLevel(logrus.DebugLevel) - } - - // Parse the hook flags. - var err error - hooks, err = hookflags.ParseHooks() - if err != nil { - logrus.Fatal(err) - } - -} - -//go:generate go run generate.go -func main() { - if len(os.Args) > 1 && os.Args[1] == "init" { - runInit() - return - } - - // Create a new container spec with the following options. - opts := container.SpecOpts{ - Rootless: true, - Readonly: readonly, - Terminal: allocateTty, - Hooks: &hooks, - } - spec := container.Spec(opts) - - // Initialize the container object. - c := &container.Container{ - ID: containerID, - Spec: spec, - PIDFile: pidFile, - ConsoleSocket: consoleSocket, - Root: root, - Detach: detach, - Rootless: true, - } - - // Unpack the rootfs. - if err := c.UnpackRootfs(defaultRootfsDir, Asset); err != nil { - logrus.Fatal(err) - } - - // Run the container. - status, err := c.Run() - if err != nil { - logrus.Fatal(err) - } - - // Remove the rootfs after the container has exited. - if err := os.RemoveAll(defaultRootfsDir); err != nil { - logrus.Warnf("removing rootfs failed: %v", err) - } - - // Exit with the container's exit status. - os.Exit(status) -} - -func runInit() { - runtime.GOMAXPROCS(1) - runtime.LockOSThread() - factory, _ := libcontainer.New("") - if err := factory.StartInitialization(); err != nil { - // as the error is sent back to the parent there is no need to log - // or write it to stderr because the parent process will handle this - os.Exit(1) - } - panic("libcontainer: container init failed to exec") -} diff --git a/nginx.png b/nginx.png deleted file mode 100644 index 5e51b93..0000000 Binary files a/nginx.png and /dev/null differ diff --git a/version/version.go b/version/version.go deleted file mode 100644 index 8088a27..0000000 --- a/version/version.go +++ /dev/null @@ -1,7 +0,0 @@ -package version - -// VERSION indicates which version of the binary is running. -var VERSION string - -// GITCOMMIT indicates which git hash the binary was built off of -var GITCOMMIT string