Skip to content

Commit

Permalink
Convert "bashbrew" to Go and add a new RFC 2822 manifest file format
Browse files Browse the repository at this point in the history
To satisfy both Windows (docker-library/golang#92) building and "non-AUFS" (#1612, #1537) building ("build machine" constraints, if you will), an update to the manifest file format is necessary.

To this end, we're introducing a new format which uses RFC 2822, allowing much more expressivity (especially for additional metadata like `Constraints`) and readability.  The new tool also includes backwards compatibility for the older line-based manifest file format, but its usage is discouraged (and we will be slowly converting at least the `docker-library` repos to use the new format in their `generate-stackbrew-library.sh` scripts).

One of the tangential benefits of this conversion is a massive increase in the speed of the tool (`bashbrew list --all` on a very fast system with an SSD and a flaming hot cache used to take ~5s and now takes ~0.012s).
  • Loading branch information
tianon committed Jun 3, 2016
1 parent 789016c commit dfc5512
Show file tree
Hide file tree
Showing 171 changed files with 38,157 additions and 610 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.git
bashbrew/go/bin
bashbrew/go/pkg
bashbrew/go/vendor/bin
bashbrew/go/vendor/pkg
bashbrew/logs
bashbrew/src
20 changes: 18 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
language: bash
language: go
go: 1.6.2

# allow for use of Docker-based workers
sudo: false

install:
- |
GB_VERSION=$(awk '$1 == "ENV" && $2 == "GB_VERSION" { print $3; exit }' Dockerfile)
firstGo="${GOPATH%%:*}"
mkdir -p "$firstGo/src/github.com/constabulary"
(
cd "$firstGo/src/github.com/constabulary"
wget -qO- "https://github.com/constabulary/gb/archive/v${GB_VERSION}.tar.gz" \
| tar -xz
mv gb-* gb
cd gb
go install -v ./...
)
before_script:
- env | sort
- ( cd bashbrew/go && gb build )
- export PATH="$PWD/bashbrew/go/bin:$PATH"

# --no-build because we has no Docker in Travis :)
script:
- ./bashbrew/travis.sh
27 changes: 25 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,35 @@
FROM docker:1.9-git

# add "edge" since Alpine 3.3 only has Go 1.5 and we need 1.6+
RUN sed -ri -e 'p; s!^!@edge !; s!v[0-9.]+!edge!' /etc/apk/repositories

RUN apk add --update \
bash \
go@edge \
&& rm -rf /var/cache/apk/*

ENV GOPATH /go
ENV PATH $GOPATH/bin:$PATH

ENV GB_VERSION 0.4.1
RUN set -x \
&& mkdir -p /go/src/github.com/constabulary \
&& cd /go/src/github.com/constabulary \
&& wget -qO- "https://github.com/constabulary/gb/archive/v${GB_VERSION}.tar.gz" \
| tar -xz \
&& mv gb-* gb \
&& cd gb \
&& go install -v ./...

ENV DIR /usr/src/official-images
ENV PATH $DIR/bashbrew/go/bin:$PATH

ENV BASHBREW_LIBRARY $DIR/library
ENV BASHBREW_CACHE /bashbrew-cache

WORKDIR $DIR
COPY . $DIR
RUN ln -s "$(readlink -f bashbrew/bashbrew.sh)" /usr/local/bin/bashbrew

VOLUME $DIR/bashbrew/logs $DIR/bashbrew/src
RUN cd bashbrew/go && gb build

VOLUME $BASHBREW_CACHE
59 changes: 43 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,9 @@ The `Dockerfile` should be written to help mitigate man-in-the-middle attacks du

##### Runtime Configuration

By default, Docker containers are executed with reduced privileges: whitelisted Linux capabilities, Control Groups, and a default Seccomp profile (1.10+ w/ host support). Software running in a container may require additional privileges in order to function correctly, and there are a number of command line options to customize container execution. See [`docker run` Reference](https://docs.docker.com/engine/reference/run/) and [Seccomp for Docker](https://docs.docker.com/engine/security/seccomp/) for reference.
By default, Docker containers are executed with reduced privileges: whitelisted Linux capabilities, Control Groups, and a default Seccomp profile (1.10+ w/ host support). Software running in a container may require additional privileges in order to function correctly, and there are a number of command line options to customize container execution. See [`docker run` Reference](https://docs.docker.com/engine/reference/run/) and [Seccomp for Docker](https://docs.docker.com/engine/security/seccomp/) for reference.

Official Repositories that require additional privileges should specify the minimal set of command line options for the software to function, and may still be rejected if this introduces significant portability or security issues. In general, `--privileged` is not allowed, but a combination of `--cap-add` and `--device` options may be acceptable. Additionally, `--volume` can be tricky as there are many host filesystem locations that introduce portability/security issues (i.e. X11 socket).
Official Repositories that require additional privileges should specify the minimal set of command line options for the software to function, and may still be rejected if this introduces significant portability or security issues. In general, `--privileged` is not allowed, but a combination of `--cap-add` and `--device` options may be acceptable. Additionally, `--volume` can be tricky as there are many host filesystem locations that introduce portability/security issues (i.e. X11 socket).

### Commitment

Expand All @@ -223,15 +223,52 @@ The filename of a definition file will determine the name of the image repositor

### Instruction format

<docker-tag>: <git-url>@<git-commit-id>
The manifest file format is officially based on [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt), and as such should be familiar to folks who are already familiar with the "headers" of many popular internet protocols/formats such as HTTP or email.

Maintainers: John Smith <jsmith@example.com> (@example-jsmith),
Anne Smith <asmith@example.com> (@example-asmith)

Tags: 4.1.1, 4.1, 4, latest
GitRepo: https://github.com/docker-library/wordpress.git
GitCommit: bbef6075afa043cbfe791b8de185105065c02c01

Tags: 2.6.17, 2.6
GitRepo: https://github.com/docker-library/redis.git
GitCommit: 062335e0a8d20cab2041f25dfff2fbaf58544471
Directory: 2.6

Tags: 13.2, harlequin
GitRepo: https://github.com/openSUSE/docker-containers-build.git
GitFetch: refs/heads/openSUSE-13.1
GitCommit: 0d21bc58cd26da2a0a59588affc506b977d6a846
Directory: docker
Constraints: !aufs

Bashbrew will fetch code out of the Git repository (`GitRepo`) at the commit specified (`GitCommit`). If the commit referenced is not available by fetching `master` of the associated `GitRepo`, it becomes necessary to supply a value for `GitFetch` in order to tell Bashbrew what ref to fetch in order to get the commit necessary.

The built image will be tagged as `<manifest-filename>:<tag>` (ie, `library/golang` with a `Tags` value of `1.6, 1, latest` will create tags of `golang:1.6`, `golang:1`, and `golang:latest`).

Optionally, if `Directory` is present, Bashbrew will look for the `Dockerfile` inside the specified subdirectory instead of at the root (and `Directory` will be used as the ["context" for the build](https://docs.docker.com/reference/builder/) instead of the top-level of the repository).

#### Deprecated format

This is the older, now-deprecated format for library manifest files. Its usage is discouraged (although it is still supported).

# maintainer: Your Name <your@email.com> (@github.name)

# maintainer: John Smith <jsmith@example.com> (@example-jsmith)
# maintainer: Anne Smith <asmith@example.com> (@example-asmith)


<Tag>: <GitRepo>@<GitCommit>

4.1.1: git://github.com/docker-library/wordpress@bbef6075afa043cbfe791b8de185105065c02c01
4.1: git://github.com/docker-library/wordpress@bbef6075afa043cbfe791b8de185105065c02c01
4: git://github.com/docker-library/wordpress@bbef6075afa043cbfe791b8de185105065c02c01
latest: git://github.com/docker-library/wordpress@bbef6075afa043cbfe791b8de185105065c02c01


<docker-tag>: <git-url>@<git-commit-id> <dockerfile-dir>
<Tag>: <GitRepo>@<GitCommit> <Directory>

2.6.17: git://github.com/docker-library/redis@062335e0a8d20cab2041f25dfff2fbaf58544471 2.6
2.6: git://github.com/docker-library/redis@062335e0a8d20cab2041f25dfff2fbaf58544471 2.6
Expand All @@ -243,34 +280,24 @@ The filename of a definition file will determine the name of the image repositor

experimental: git://github.com/tianon/dockerfiles@90d86ad63c4a06b7d04d14ad830381b876183b3c debian/experimental

Bashbrew will fetch code out of the Git repository at the commit specified here. The generated image will be tagged as `<manifest-filename>:<docker-tag>`.

Using Git tags instead of explicit Git commit references is supported, but heavily discouraged. For example, if a Git tag is changed on the referenced repository to point to another commit, **the image will not be rebuilt**. Instead, either create a new tag (or reference an exact commit) and submit a pull request.

Optionally, if `<dockerfile-dir>` is present, Bashbrew will look for the `Dockerfile` inside the specified subdirectory instead of at the root (and `<dockerfile-dir>` will be used as the ["context" for the build](https://docs.docker.com/reference/builder/)).
Using Git tags instead of explicit Git commit references is supported for the deprecated format only, but is heavily discouraged. For example, if a Git tag is changed on the referenced repository to point to another commit, **the image will not be rebuilt**. Instead, either create a new tag (or reference an exact commit) and submit a pull request.

### Creating a new repository

- Create a new file in the `library/` folder. Its name will be the name of your repository on the Hub.
- Add your tag definitions using the appropriate syntax (see above).
- Add a line similar to the following to the top of the file:

# maintainer: Your Name <your@email.com> (@github.name)

- Create a pull request adding the file from your forked repository to this one. Please be sure to add details as to what your repository does.

### Adding a new tag in an existing repository (that you're the maintainer of)

- Add your tag definition using the instruction format documented above.
- Create a pull request from your Git repository to this one. Please be sure to add details about what's new, if possible.
- In the pull request comments, feel free to prod the repository's maintainers (found in the relevant `MAINTAINERS` file) using GitHub's @-mentions.

### Change to a tag in an existing repository (that you're the maintainer of)

- Update the relevant tag definition using the instruction format documented above.
- Create a pull request from your Git repository to this one. Please be sure to add details about what's changed, if possible.
- In the pull request comments, feel free to prod the repository's maintainers (found in the relevant `MAINTAINERS` file) using GitHub's @-mentions.

## Bashbrew

Bashbrew is a set of bash scripts for cloning, building, tagging, and pushing the Docker official images. See [`README.md` in the `bashbrew/` subfolder](bashbrew/README.md) for more information.
Bashbrew (`bashbrew`) is a tool for cloning, building, tagging, and pushing the Docker official images. See [`README.md` in the `bashbrew/` subfolder](bashbrew/README.md) for more information.
157 changes: 59 additions & 98 deletions bashbrew/README.md
Original file line number Diff line number Diff line change
@@ -1,120 +1,81 @@
# Bashbrew

The recommended way to use `bashbrew.sh` is to install a symlink in your `PATH` somewhere as `bashbrew`, for example `~/bin/bashbrew -> /path/to/official-images/bashbrew/bashbrew.sh` (assuming `~/bin` is in `PATH`).
# Bashbrew (`bashbrew`)

```console
$ bashbrew --help

usage: bashbrew [build|push|list] [options] [repo[:tag] ...]
ie: bashbrew build --all
bashbrew push debian ubuntu:12.04
bashbrew list --namespaces='_' debian:7 hello-world

This script processes the specified Docker images using the corresponding
repository manifest files.

common options:
--all Build all repositories specified in library
--docker="docker"
Use a custom Docker binary
--retries="4"
How many times to try again if the build/push fails before
considering it a lost cause (always attempts a minimum of
one time, but maximum of one plus this number)
--help, -h, -? Print this help message
--library="/home/tianon/docker/stackbrew/library"
Where to find repository manifest files
--logs="/home/tianon/docker/stackbrew/bashbrew/logs"
Where to store the build logs
--namespaces="_"
Space separated list of image namespaces to act upon
Note that "_" is a special case here for the unprefixed
namespace (ie, "debian" vs "library/debian"), and as such
will be implicitly ignored by the "push" subcommand
Also note that "build" will always tag to the unprefixed
namespace because it is necessary to do so for dependent
images to use FROM correctly (think "onbuild" variants that
are "FROM base-image:some-version")
--uniq
Only process the first tag of identical images
This is not recommended for build or push
i.e. process python:2.7, but not python:2

build options:
--no-build Don't build, print what would build
--no-clone Don't pull/clone Git repositories
--src="/home/tianon/docker/stackbrew/bashbrew/src"
Where to store cloned Git repositories (GOPATH style)

push options:
--no-push Don't push, print what would push
NAME:
bashbrew - canonical build tool for the official images

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

COMMANDS:
list list repo:tag combinations for a given repo
build build (and tag) repo:tag combinations for a given repo
tag tag repo:tag into a namespace (especially for pushing)
push push namespace/repo:tag (see also "tag")

plumbing:
children print the repos built FROM a given repo or repo:tag
parents print the repos this repo or repo:tag is FROM
cat print manifest contents for repo or repo:tag
from print FROM for repo:tag

GLOBAL OPTIONS:
--verbose, -v enable more output (esp. "docker build" output) [$BASHBREW_VERBOSE]
--no-sort do not apply any sorting, even via --build-order
--constraint value build constraints (see Constraints in Manifest2822Entry)
--exclusive-constraints skip entries which do not have Constraints
--library value where the bodies are buried (default: "/home/jsmith/docker/official-images/library") [$BASHBREW_LIBRARY]
--cache value where the git wizardry is stashed (default: "/home/jsmith/.cache/bashbrew") [$BASHBREW_CACHE]
--help, -h, -? show help

```

## Subcommands

### `bashbrew build`
## Building

This script reads the library files for the images specified and then clones the required Git repositories into the specified `--src` directory. If the Git repository already exists, the script verifies that the Git ref specified in the library file exists and does `git fetch` as necessary.
Bashbrew itself is built using `gb` ([github.com/constabulary/gb](https://github.com/constabulary/gb)).

The next step in the script is to build each image specified. All the `image:tag` combinations are placed into a queue. The processing order is determined by the order of items passed in on the command line (or alphabetical if `--all` is used). When a whole image, like `debian`, is specified the `image:tag` combinations are added to the queue in the order that they appear in the library file. For each `image:tag` to be processed, the system checks out the specified commit and sets mtimes (see [`git-set-mtimes`](#git-set-mtimes)) of all files in the Git repository to take advantage of Docker caching. If the `image:tag` is `FROM` another image that is later in the queue, it is deferred to the end of the queue.
Once in the `go` subdirectory, `gb build` should produce `go/bin/bashbrew`, ready for use.

After the image is built, the final step of the script is to tag the image into each of the given `--namespaces`.
## Usage

The `--no-clone` option skips the `git clone` step and will cause the script to fail on the build step if the Git repository does not exist or is missing the required Git refs.
In general, `bashbrew build some-repo` or `bashbrew build ./some-file` should be sufficient for using the tool at a surface level, especially for testing. For more complex usage, please see the built-in help (`bashbrew --help`, `bashbrew build --help`, etc).

The `--no-build` option skips all the building, including setting the mtimes.
## Configuration

**WARNING:** setting `--src` so that it uses a local working copy of your Git directory for a specified build will delete untracked and uncommitted changes, and will disable `gc.auto`. It is not recommended to symlink in your working directories for use during build.
The default "flags" configuration is in `~/.config/bashbrew/flags`, but the base path can be overridden with `--config` or `BASHBREW_CONFIG` (technically, the full path to the `flags` configuration file is `${BASHBREW_CONFIG:-${XDG_CONFIG_HOME:-$HOME/.config}/bashbrew}/flags`).

### `bashbrew push`
To set a default namespace for specific commands only:

This script takes advantage of `docker login` and does a `docker push` on each `image:tag` specified for the given `--namespaces`. The script will warn if a given `namespace/image:tag` does not exist.

The `--no-push` option prints out the `docker push` instructions that would have been executed.
```
Commands: tag, push
Namespace: officialstaging
```

### `bashbrew list`
To set a default namespace for all commands:

Takes the same arguments as `bashbrew build` and `bashbrew push`, but prints a list of image names and quits.
```
Namespace: jsmith
```

For example:
A more complex example:

```console
$ # count the number of tags in the official library
$ bashbrew list --all | wc -l
802
$ # count the number of _unique_ tags in the official library
$ bashbrew list --all --uniq | wc -l
296

$ # pull all officially supported tags of "debian"
$ bashbrew list debian | xargs -n1 --verbose docker pull
...

$ # list all unique supported tags of "python"
$ bashbrew list --uniq python
python:2.7.10
python:2.7.10-onbuild
python:2.7.10-slim
python:2.7.10-wheezy
python:3.2.6
python:3.2.6-onbuild
python:3.2.6-slim
python:3.2.6-wheezy
python:3.3.6
python:3.3.6-onbuild
python:3.3.6-slim
python:3.3.6-wheezy
python:3.4.3
python:3.4.3-onbuild
python:3.4.3-slim
python:3.4.3-wheezy
```
# comments are allowed anywhere (and are ignored)
Library: /mnt/docker/official-images/library
Cache: /mnt/docker/bashbrew-cache
Constraints: aufs, docker-1.9, tianon
ExclusiveConstraints: true
## Helper Scripts
# namespace just "tag" and "push" (not "build")
Commands: tag, push
Namespace: tianon
### `git-set-mtimes`
Commands: list
ApplyConstraints: true
Commands: tag
Verbose: true
```

Since Docker caching of layers is based upon the mtimes of files and folders, this script sets each file's mtime to the time of the commit that most recently modified it and sets each directory's mtime to be the most recent mtime of any file or folder contained within it. This gives a deterministic time for all files and folders in the Git repository.
In this example, `bashbrew tag` will get both `Namespace` and `Verbose` applied.
Loading

0 comments on commit dfc5512

Please sign in to comment.