Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
jwilder committed Oct 13, 2014
0 parents commit 482f216
Show file tree
Hide file tree
Showing 9 changed files with 404 additions and 0 deletions.
3 changes: 3 additions & 0 deletions GLOCKFILE
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
github.com/ActiveState/tail 068b72961a6bc5b4a82cf4fc14ccc724c0cfa73a
github.com/howeyc/fsnotify 6b1ef893dc11e0447abda6da20a5203481878dda
gopkg.in/tomb.v1 c131134a1947e9afd9cecfe11f4c6dff0732ae58
21 changes: 21 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.SILENT :
.PHONY : dockerize clean fmt

TAG:=`git describe --abbrev=0 --tags`
LDFLAGS:=-X main.buildVersion $(TAG)

all: dockerize

dockerize:
echo "Building dockerize"
go install -ldflags "$(LDFLAGS)"

dist-clean:
rm -rf dist
rm -f dockerize-linux-*.tar.gz

dist: dist-clean
mkdir -p dist/linux/amd64 && GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o dist/linux/amd64/dockerize

release: dist
tar -cvzf dockerize-linux-amd64-$(TAG).tar.gz -C dist/linux/amd64 dockerize
95 changes: 95 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
dockerize
=============

Utility to simplify running applications in docker containers.

dockerize is a utility to simplify running applications in docker containers. It allows you
to generate application configuration files at container startup time from templates and
container environment variables. It also allows log files to be tailed to stdout and/or
stderr.

The typical use case for dockerize is when you have an application that has one or more
configuration files and you would like to control some of the values using environment variables.

For example, a Python application using Sqlalchemy may be able to use environment variables directly.
It may require that the database URL be read from a python settings file with a variable named
`SQLALCHEMY_DATABASE_URI`. dockerize allows you to set an environment variable such as
`DATABASE_URL` and update the python file when the container starts.

Another use case is when the application logs to specific files on the filesystem and not stdout
or stderr. This makes it difficult to troubleshoot the container using the `docker logs` command.
For example, nginx will log to `/var/log/nginx/access.log' and
'/var/log/nginx/error.log' by default. While you can sometimes work around this, it's tedious to find
the a solution for every application. dockerize allows you to specify which logs files should
be tailed and where they should be sent.


## Installation

Download the latest version in your container:

* [linux/amd64](https://github.com/jwilder/dockerize/releases/download/v0.0.1/dockerize-linux-amd64-v0.0.1.tar.gz)

For Ubuntu Images:

```
RUN apt-get update && apt-get install -y wget
RUN wget https://github.com/jwilder/dockerize/releases/download/v0.0.1/dockerize-linux-amd64-v0.0.1.tar.gz
RUN tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.0.1.tar.gz
```

## Usage

dockerize works by wrapping the call to your application using the `ENTRYPOINT` or `CMD` directives.

This would generate `/etc/nginx/nginx.conf` from the template located at `/etc/nginx/nginx.tmpl` and
send `/var/log/nginx/access.log' to `STDOUT` and `/var/log/nginx/error.log` to `STDERR` after running
`nginx`.

```
CMD dockerize -template /etc/nginx/nginx.tmpl:/etc/nginx/nginx.conf -stdout /var/log/nginx/access.log -stderr /var/log/nginx/error.log nginx
```

### Command-line Options

You can specify multiple template by passing using `-template` multiple times:

```
$ dockerize -template template1.tmpl:file1.cfg -template template2.tmpl:file3
```

You can tail multiple files to `STDOUT` and `STDERR` by passing the options multiple times.

```
$ dockerize -stdout info.log -stdout perf.log
```

If your file uses `{{` and `}}` as part of it's syntax, you can change the template escape characters using the `-delims`.

```
$ dockerize -delims "<%:%>"
```

## Using Templates

Templates use Golang [text/template](http://golang.org/pkg/text/template/). You can access environment
variables within a template with `.Env`.

```
{{ .Env.PATH }} is my path
```

There are a few built in functions as well:

* `default` - Returns a default value for one that does not exist
* `contains` - Returns true if a string is within another string
* `exists` - Determines if a file path exists or not
* `split` - Splits a string into an array using a separator string
* `replace` - Replaces all occurences of a string within another string
* `parseUrl`- Parses a URL into it's protocol, scheme, host, etc. parts.

## License

MIT
94 changes: 94 additions & 0 deletions dockerize.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package main

import (
"flag"
"fmt"
"log"
"os"
"strings"
"sync"
)

type sliceVar []string

type Context struct {
}

func (c *Context) Env() map[string]string {
env := make(map[string]string)
for _, i := range os.Environ() {
sep := strings.Index(i, "=")
env[i[0:sep]] = i[sep+1:]
}
return env
}

var (
buildVersion string
version bool
wg sync.WaitGroup

templatesFlag sliceVar
stdoutTailFlag sliceVar
stderrTailFlag sliceVar
delimsFlag string
delims []string
)

func (s *sliceVar) Set(value string) error {
*s = append(*s, value)
return nil
}

func (s *sliceVar) String() string {
return strings.Join(*s, ",")
}

func main() {

flag.BoolVar(&version, "version", false, "show version")
flag.Var(&templatesFlag, "template", "Template (/template:/dest). Can be passed multiple times")
flag.Var(&stdoutTailFlag, "stdout", "Tails a file to stdout. Can be passed multiple times")
flag.Var(&stderrTailFlag, "stderr", "Tails a file to stderr. Can be passed multiple times")
flag.StringVar(&delimsFlag, "delims", "", `template tag delimiters. default "{{":"}}" `)

flag.Parse()

if version {
fmt.Println(buildVersion)
return
}

if flag.NArg() == 0 {
log.Fatalln("no command specified")
}

if delimsFlag != "" {
delims = strings.Split(delimsFlag, ":")
if len(delims) != 2 {
log.Fatalf("bad delimiters argument: %s. expected \"left:right\"", delimsFlag)
}
}
for _, t := range templatesFlag {
parts := strings.Split(t, ":")
if len(parts) != 2 {
log.Fatalf("bad template argument: %s. expected \"/template:/dest\"", t)
}
generateFile(parts[0], parts[1])
}

wg.Add(1)
go runCmd(flag.Arg(0), flag.Args()[1:]...)

for _, out := range stdoutTailFlag {
wg.Add(1)
go tailFile(out, os.Stdout)
}

for _, err := range stderrTailFlag {
wg.Add(1)
go tailFile(err, os.Stderr)
}

wg.Wait()
}
20 changes: 20 additions & 0 deletions examples/nginx/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM ubuntu:14.04
MAINTAINER Jason Wilder [email protected]

# Install Nginx.
RUN echo "deb http://ppa.launchpad.net/nginx/stable/ubuntu trusty main" > /etc/apt/sources.list.d/nginx-stable-trusty.list
RUN echo "deb-src http://ppa.launchpad.net/nginx/stable/ubuntu trusty main" >> /etc/apt/sources.list.d/nginx-stable-trusty.list
RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C300EE8C
RUN apt-get update
RUN apt-get install -y wget nginx

RUN wget https://github.com/jwilder/dockerize/releases/download/v0.0.1/dockerize-linux-amd64-v0.0.1.tar.gz
RUN tar -C /usr/local/bin -xvzf dockerize-linux-amd64-v0.0.1.tar.gz

RUN echo "daemon off;" >> /etc/nginx/nginx.conf

ADD default.tmpl /etc/nginx/sites-available/default.tmpl

EXPOSE 80

CMD dockerize -template /etc/nginx/sites-available/default.tmpl:/etc/nginx/sites-available/default -stdout /var/log/nginx/access.log -stderr /var/log/nginx/error.log nginx
18 changes: 18 additions & 0 deletions examples/nginx/default.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;

root /usr/share/nginx/html;
index index.html index.htm;

# Make site accessible from http://localhost/
server_name localhost;

location / {
access_log off;
proxy_pass {{ .Env.PROXY_URL }};
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
20 changes: 20 additions & 0 deletions exec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package main

import (
"log"
"os"
"os/exec"
)

func runCmd(cmd string, args ...string) {

//FIXME: forward signals
process := exec.Command(cmd, args...)
process.Stdin = os.Stdin
process.Stdout = os.Stdout
process.Stderr = os.Stderr
err := process.Run()
if err != nil {
log.Fatalf("error running command: %s, %s\n", cmd, err)
}
}
25 changes: 25 additions & 0 deletions tail.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package main

import (
"fmt"
"log"
"os"

"github.com/ActiveState/tail"
)

func tailFile(file string, dest *os.File) {
defer wg.Done()
t, err := tail.TailFile(file, tail.Config{
Follow: true,
ReOpen: true,
//Poll: true,
Logger: tail.DiscardingLogger,
})
if err != nil {
log.Fatalf("unable to tail %s: %s", "foo", err)
}
for line := range t.Lines {
fmt.Fprintln(dest, line.Text)
}
}
Loading

0 comments on commit 482f216

Please sign in to comment.