baha 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/.travis.yml +4 -1
- data/CHANGELOG.md +8 -0
- data/Gemfile +2 -0
- data/README.md +75 -17
- data/baha.gemspec +4 -3
- data/example/example.yml +5 -0
- data/example/memcached/Dockerfile +24 -0
- data/lib/baha/builder.rb +29 -2
- data/lib/baha/cli.rb +53 -0
- data/lib/baha/config.rb +13 -0
- data/lib/baha/container_options/exposed_ports.rb +6 -2
- data/lib/baha/dockerfile.rb +150 -0
- data/lib/baha/image.rb +2 -1
- data/lib/baha/version.rb +1 -1
- data/spec/builder_spec.rb +15 -2
- data/spec/config_spec.rb +17 -1
- data/spec/dockerfile_spec.rb +38 -0
- data/spec/fixtures/Dockerfile +33 -0
- data/spec/fixtures/Dockerfile.invalid +4 -0
- data/spec/fixtures/config_build.yml +4 -1
- data/spec/fixtures/config_build_image.yml +3 -1
- data/spec/fixtures/config_dockerfile.yml +9 -0
- data/spec/fixtures/config_eachimage.yml +7 -1
- data/spec/spec_helper.rb +14 -0
- metadata +20 -5
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YTUxODZmMWUzYTBhNzI5YTIzZWU0NjAwNzUyMjRiMTMzY2VjNjkyOA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
MzU1Nzg5YjQ3Y2RiNzMwYTIzMmU2ZTQ5ZjQ5ZmVmMDMxNWQ0ZDIwZA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NGZiYmFmMTY5ZGRmNDQ1MGJkMzIzMmYyYmIyYjAwZjJkZGY3ZTg5OTExN2Rh
|
10
|
+
NzRkZjY0MDI3NmQ5MTU2NTg1MjUwMmYwN2ZmNjFiY2RlOWEzMDk4NTJlMmI0
|
11
|
+
YjZkMjJlYTJiYTk0ZTk1ODI5M2JmNjMyYzQ3OTczZmQ3YTM5NGM=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
YWYzZmFlNjMzMjZkOTgzNjA2N2JhZjNiNTQ4MjRjZTBhNzg1OTFhMzJjZmEw
|
14
|
+
MTg1NjYzZjVkNWUwZWI2ZjUxZDU4ZjM1Yzk2MmQ1ZmM5NTkzZDk4ZTIyNDI0
|
15
|
+
NDY0ZDE3ZTQyZDRiMzhiYjg2MTcyYWM0YTNkM2Y5YmQ3YmZiYzc=
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -3,12 +3,13 @@ Baha
|
|
3
3
|
|
4
4
|
[![Gem Version](https://badge.fury.io/rb/baha.png)](http://badge.fury.io/rb/baha)
|
5
5
|
[![Build Status](https://travis-ci.org/justenwalker/baha.png?branch=master)](https://travis-ci.org/justenwalker/baha)
|
6
|
+
[![Code Climate](https://codeclimate.com/github/justenwalker/baha/badges/gpa.svg)](https://codeclimate.com/github/justenwalker/baha)
|
6
7
|
|
7
8
|
Introduction
|
8
9
|
------------
|
9
10
|
|
10
11
|
Baha is a command-line utility that assists in the creation of docker images.
|
11
|
-
It addresses some of
|
12
|
+
It addresses some of Dockerfiles shortcomings and encourages smaller, reusable, tagged images.
|
12
13
|
|
13
14
|
Why not Dockerfiles?
|
14
15
|
--------------------
|
@@ -18,9 +19,11 @@ Dockerfiles are simple. They are an easy way to make a docker image without need
|
|
18
19
|
So why would I use Baha?
|
19
20
|
------------------------
|
20
21
|
|
22
|
+
Baha attempts to address the shortcomings of Dockerfiles by factoring out redundancies and providing a modular interface for creating suites of dependent images.
|
23
|
+
|
21
24
|
### 1. Baha forbids more than 1 layer per image
|
22
25
|
|
23
|
-
If you split statements across multiple RUN statements, each of these results in a new layer.
|
26
|
+
If you split statements across multiple `RUN` statements, each of these results in a new layer.
|
24
27
|
The more commands you run, the more layers you create. If you want to *minimize the number of layers* (See [Official Recommendations](https://docs.docker.com/articles/dockerfile_best-practices/))
|
25
28
|
then you must ensure that all statements can be condensed into one line - sacrificing maintainability in the process.
|
26
29
|
|
@@ -30,25 +33,25 @@ Baha encourages using scripts instead of `RUN` statements to ensure that only on
|
|
30
33
|
|
31
34
|
The nature of the way the dockerfiles are processed means that each command you run commits a new image.
|
32
35
|
This means, that if you have a `RUN` statement that downloads packages for installation.
|
33
|
-
This will commit a layer with the installation files. Later on if you clean up these files with further RUN commands, they will still exist in your image lineage - thus having no space savings. Without proper precautions, you'll end up having unnecessarily large images.
|
36
|
+
This will commit a layer with the installation files. Later on if you clean up these files with further `RUN` commands, they will still exist in your image lineage - thus having no space savings. Without proper precautions, you'll end up having unnecessarily large images.
|
34
37
|
|
35
38
|
Baha ensures that all setup tasks happen in a single commit - so you can write cleanup statements and be assured that they will indeed be absent in the resulting images.
|
36
39
|
|
37
40
|
### 3. Baha understands the bigger picture
|
38
41
|
|
39
|
-
Another best practice (2
|
42
|
+
Another best practice ([2](http://crosbymichael.com/dockerfile-best-practices-take-2.html): #7) recommends that you use your own base image.
|
40
43
|
Dockerfiles make it simple to create your own 'base' image, but how about updates?
|
41
44
|
|
42
45
|
If you were to just rebuild all of your Dockerfiles, you would create an entirely new tree - even if nothing changed.
|
43
46
|
|
44
|
-
Baha will rebuild your entire lineage if the base
|
47
|
+
Baha will rebuild your entire lineage if the base image changes, but will not rebuild base images if only the children change.
|
45
48
|
|
46
|
-
**Caveat
|
49
|
+
**Caveat**
|
47
50
|
|
48
51
|
Baha relies on tagging your releases, noticing when the tag has changed, and treating tags as immutable.
|
49
52
|
This is analogous to how **git** treats tags.
|
50
53
|
|
51
|
-
Tagging your images is another best-practice (2
|
54
|
+
Tagging your images is another best-practice ([2](http://crosbymichael.com/dockerfile-best-practices-take-2.html): #5) anyway, so this is encouraged by design.
|
52
55
|
|
53
56
|
Bottom line is: If you change your image, you should change the tag/version.
|
54
57
|
|
@@ -62,8 +65,8 @@ This workspace is made available to the image via a bind mount during build-time
|
|
62
65
|
|
63
66
|
**References**
|
64
67
|
|
65
|
-
1. [Official Dockerfile Best Practices](https://docs.docker.com/articles/dockerfile_best-practices/)
|
66
|
-
2. [Dockerfile Best Practices - take 2, by Michael Crosby](http://crosbymichael.com/dockerfile-best-practices-take-2.html)
|
68
|
+
1. [Official Dockerfile Best Practices](https://docs.docker.com/articles/dockerfile_best-practices/)
|
69
|
+
2. [Dockerfile Best Practices - take 2, by Michael Crosby](http://crosbymichael.com/dockerfile-best-practices-take-2.html)
|
67
70
|
|
68
71
|
Disclaimer
|
69
72
|
----------
|
@@ -83,16 +86,16 @@ Installation
|
|
83
86
|
```
|
84
87
|
$ gem install baha
|
85
88
|
```
|
86
|
-
### gem install baha
|
87
89
|
|
88
90
|
Usage
|
89
91
|
-----
|
90
92
|
|
91
93
|
```
|
92
94
|
Baha Commands:
|
93
|
-
baha build [options] CONFIG
|
94
|
-
baha
|
95
|
-
baha
|
95
|
+
baha build [options] CONFIG # Builds all docker images based on the given config
|
96
|
+
baha convert DOCKERFILE n, --name=NAME # Converts an existing dockerfile to a Baha-compatible image.yml
|
97
|
+
baha help [COMMAND] # Describe available commands or one specific command
|
98
|
+
baha version # Print version and exit
|
96
99
|
```
|
97
100
|
|
98
101
|
**build**
|
@@ -111,7 +114,50 @@ Description:
|
|
111
114
|
Reads the CONFIG file and builds all of the docker images in the order they appear.
|
112
115
|
```
|
113
116
|
|
114
|
-
|
117
|
+
**convert**
|
118
|
+
|
119
|
+
```
|
120
|
+
Usage:
|
121
|
+
baha convert DOCKERFILE n, --name=NAME
|
122
|
+
|
123
|
+
Options:
|
124
|
+
n, --name=NAME # The target image name
|
125
|
+
t, [--tag=TAG] # The target image tag
|
126
|
+
# Default: latest
|
127
|
+
o, [--output=OUTPUT] # Target output file
|
128
|
+
# Default: STDOUT
|
129
|
+
|
130
|
+
Description:
|
131
|
+
Reads the given Dockerfile and outputs a Baha-compatible image.yml
|
132
|
+
which can be included or embedded within a CONFIG
|
133
|
+
```
|
134
|
+
|
135
|
+
Example
|
136
|
+
-------
|
137
|
+
|
138
|
+
Check out the **example** directory for a sample CONFIG.
|
139
|
+
|
140
|
+
To run the example build
|
141
|
+
|
142
|
+
```
|
143
|
+
### 1.
|
144
|
+
### If necessary (using boot2docker) export the correct environment variables
|
145
|
+
### You can find this by running boot2docker up
|
146
|
+
# export DOCKER_HOST=tcp://192.168.59.103:2376
|
147
|
+
# export DOCKER_CERT_PATH=$HOME/.boot2docker/certs/boot2docker-vm
|
148
|
+
# export DOCKER_TLS_VERIFY=1
|
149
|
+
|
150
|
+
### 2.
|
151
|
+
### Checkout baha
|
152
|
+
git clone https://github.com/justenwalker/baha.git
|
153
|
+
cd baha
|
154
|
+
|
155
|
+
### 3. Install bundle
|
156
|
+
bundle install
|
157
|
+
|
158
|
+
### 4. Run the example.yml
|
159
|
+
bundle exec baha build -d example/example.yml
|
160
|
+
```
|
115
161
|
|
116
162
|
How it works
|
117
163
|
------------
|
@@ -127,13 +173,25 @@ It will build each image in the order they appear by doing the following.
|
|
127
173
|
1. Create the workspace directory if it doesn't exist
|
128
174
|
2. Run any `pre_build` tasks to prepare dependencies
|
129
175
|
|
130
|
-
### Run the
|
176
|
+
### Run the command inside the image
|
131
177
|
1. Creates a new container and runs it with the `command` given
|
132
178
|
2. Commits the container with the run options specified in the images `config` section.
|
133
179
|
|
180
|
+
### Tags the resulting image
|
181
|
+
Adds the appropriate tags to the image as defined in the image config for both the remote registry and local repository.
|
182
|
+
|
134
183
|
How to Contribute
|
135
184
|
-----------------
|
136
185
|
|
186
|
+
Contributions are welcome! Documentation, bug-fixes, patches, or new functionality, or comments/criticism.
|
187
|
+
|
188
|
+
### Code Contributions
|
189
|
+
|
190
|
+
Start by [forking](https://github.com/justenwalker/baha/fork) the github project.
|
191
|
+
Work on a topic branch instead of master (`git checkout -b my-feature`) and submit a pull request when you are done.
|
192
|
+
|
193
|
+
Please add specs to cover the change so that we can avoid regressions and help future commits from breaking existing code.
|
194
|
+
|
137
195
|
### Running the tests
|
138
196
|
|
139
197
|
$ bundle
|
@@ -146,11 +204,11 @@ How to Contribute
|
|
146
204
|
|
147
205
|
### Reporting Issues
|
148
206
|
|
149
|
-
Please include a reproducible test case.
|
207
|
+
Please include a reproducible test case if possible. Otherwise, provide as much detail as you can.
|
150
208
|
|
151
209
|
License
|
152
210
|
-------
|
153
211
|
|
154
212
|
Copyright (c) 2014 Justen Walker.
|
155
213
|
|
156
|
-
Released under the terms of the MIT License. For further information, please see the file `LICENSE.md`.
|
214
|
+
Released under the terms of the MIT License. For further information, please see the file `LICENSE.md`.
|
data/baha.gemspec
CHANGED
@@ -8,9 +8,10 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Baha::VERSION
|
9
9
|
spec.authors = ["Justen Walker"]
|
10
10
|
spec.email = ["justen.walker+github@gmail.com"]
|
11
|
-
spec.summary = %q{Baha -
|
12
|
-
spec.description =
|
13
|
-
|
11
|
+
spec.summary = %q{Baha is a command-line utility that assists in the creation of docker images.}
|
12
|
+
spec.description = spec.summary + "\n" +
|
13
|
+
%q{It addresses some of Dockerfiles shortcomings and encourages smaller, reusable, tagged images.}
|
14
|
+
spec.homepage = "https://github.com/justenwalker/baha"
|
14
15
|
spec.license = "MIT"
|
15
16
|
|
16
17
|
spec.files = `git ls-files -z`.split("\x0")
|
data/example/example.yml
CHANGED
@@ -52,3 +52,8 @@ images:
|
|
52
52
|
### include them from another file relative to this config.
|
53
53
|
### Keep things modular and maintainable.
|
54
54
|
- include: rvm/image.yml
|
55
|
+
### You can also build an image from a Dockerfile
|
56
|
+
### Be sure to name and tag it
|
57
|
+
- dockerfile: memcached/Dockerfile
|
58
|
+
name: memcached
|
59
|
+
tag: 2.2
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Memcached
|
2
|
+
#
|
3
|
+
# VERSION 2.2
|
4
|
+
|
5
|
+
# use the ubuntu base image provided by dotCloud
|
6
|
+
FROM ubuntu
|
7
|
+
|
8
|
+
MAINTAINER Victor Coisne victor.coisne@dotcloud.com
|
9
|
+
|
10
|
+
# make sure the package repository is up to date
|
11
|
+
RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
|
12
|
+
RUN apt-get update
|
13
|
+
|
14
|
+
# install memcached
|
15
|
+
RUN apt-get install -y memcached
|
16
|
+
|
17
|
+
# Launch memcached when launching the container
|
18
|
+
ENTRYPOINT ["memcached"]
|
19
|
+
|
20
|
+
# run memcached as the daemon user
|
21
|
+
USER daemon
|
22
|
+
|
23
|
+
# expose memcached port
|
24
|
+
EXPOSE 11211
|
data/lib/baha/builder.rb
CHANGED
@@ -3,6 +3,8 @@ require 'baha/image'
|
|
3
3
|
require 'baha/log'
|
4
4
|
require 'baha/pre_build'
|
5
5
|
require 'fileutils'
|
6
|
+
require 'tempfile'
|
7
|
+
require 'shellwords'
|
6
8
|
|
7
9
|
class Baha::Builder
|
8
10
|
LOG = Baha::Log.for_name("Builder")
|
@@ -47,6 +49,27 @@ class Baha::Builder
|
|
47
49
|
FileUtils.mkdir_p workspace.to_s
|
48
50
|
end
|
49
51
|
|
52
|
+
run = false
|
53
|
+
## Image Run Config
|
54
|
+
if image.run
|
55
|
+
build_log.debug { "Image has RUN commands"}
|
56
|
+
run = '.init.sh'
|
57
|
+
File.open(workspace + run,'w') do |f|
|
58
|
+
f.write("#!/bin/sh\n")
|
59
|
+
f.write("set -xe\n")
|
60
|
+
image.run.each do |cmd|
|
61
|
+
build_log.debug { "Writing command: #{cmd}"}
|
62
|
+
case cmd
|
63
|
+
when String
|
64
|
+
f.write("#{cmd}\n")
|
65
|
+
when Array
|
66
|
+
safe = Shellwords.shelljoin(cmd)
|
67
|
+
f.write("#{safe}\n")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
50
73
|
## Pre-Build tasks
|
51
74
|
build_log.info { "Building #{image.name}" }
|
52
75
|
if image.pre_build
|
@@ -58,16 +81,20 @@ class Baha::Builder
|
|
58
81
|
end
|
59
82
|
|
60
83
|
## Build Image
|
84
|
+
command = image.command
|
85
|
+
if run
|
86
|
+
command = ["/bin/sh", "#{image.bind}/#{run}"]
|
87
|
+
end
|
61
88
|
container_config = {
|
62
89
|
'Image' => image.parent_id,
|
63
|
-
'Cmd' =>
|
90
|
+
'Cmd' => command,
|
64
91
|
'Workingdir' => image.bind
|
65
92
|
}
|
66
93
|
build_log.debug { "Creating container for #{image.name} => #{container_config.inspect}" }
|
67
94
|
container = Docker::Container.create(container_config)
|
68
95
|
build_log.debug { "Created container #{container.id} "}
|
69
96
|
|
70
|
-
build_log.debug { "Running container for #{image.name}" }
|
97
|
+
build_log.debug { "Running container for #{image.name}: #{command}" }
|
71
98
|
container.start({
|
72
99
|
'Binds' => "#{image.workspace.expand_path}:#{image.bind}"
|
73
100
|
})
|
data/lib/baha/cli.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'thor'
|
2
2
|
require 'baha/builder'
|
3
3
|
require 'baha/log'
|
4
|
+
require 'baha/dockerfile'
|
4
5
|
|
5
6
|
class Baha::CLI < Thor
|
6
7
|
desc "build [options] CONFIG", "Builds all docker images based on the given config"
|
@@ -62,6 +63,58 @@ class Baha::CLI < Thor
|
|
62
63
|
Baha::Log.close!
|
63
64
|
end
|
64
65
|
end
|
66
|
+
desc "convert DOCKERFILE", "Converts an existing dockerfile to a Baha-compatible image.yml"
|
67
|
+
long_desc <<-LONGDESC
|
68
|
+
Reads the given Dockerfile and outputs a Baha-compatible image.yml which can be included or embedded within a CONFIG
|
69
|
+
LONGDESC
|
70
|
+
option :name, {
|
71
|
+
:aliases => :n,
|
72
|
+
:required => true,
|
73
|
+
:type => :string,
|
74
|
+
:desc => "The target image name"
|
75
|
+
}
|
76
|
+
option :tag, {
|
77
|
+
:aliases => :t,
|
78
|
+
:required => false,
|
79
|
+
:type => :string,
|
80
|
+
:default => 'latest',
|
81
|
+
:desc => 'The target image tag'
|
82
|
+
}
|
83
|
+
option :output, {
|
84
|
+
:aliases => :o,
|
85
|
+
:required => false,
|
86
|
+
:type => :string,
|
87
|
+
:default => 'STDOUT',
|
88
|
+
:desc => 'Target output file'
|
89
|
+
}
|
90
|
+
def convert(dockerfile)
|
91
|
+
file = Pathname.new(dockerfile)
|
92
|
+
out = options[:output]
|
93
|
+
name = options[:name]
|
94
|
+
tag = options[:tag]
|
95
|
+
Baha::Log.logfile = STDERR
|
96
|
+
Baha::Log.level = :info
|
97
|
+
log = Baha::Log.for_name("CLI")
|
98
|
+
begin
|
99
|
+
if file.exist?
|
100
|
+
yaml = Baha::Dockerfile.parse(dockerfile)
|
101
|
+
yaml = { 'name' => name, 'tag' => tag }.merge(yaml)
|
102
|
+
if out == 'STDOUT'
|
103
|
+
puts yaml.to_yaml
|
104
|
+
else
|
105
|
+
File.open(out,'w') do |f|
|
106
|
+
f.write yaml.to_yaml
|
107
|
+
end
|
108
|
+
end
|
109
|
+
else
|
110
|
+
log.fatal { "DOCKERFILE #{dockerfile} not found" }
|
111
|
+
exit 1
|
112
|
+
end
|
113
|
+
rescue Exception => e
|
114
|
+
log.fatal("Error encountered while building images")
|
115
|
+
log.fatal(e)
|
116
|
+
end
|
117
|
+
end
|
65
118
|
desc "version", "Print version and exit"
|
66
119
|
def version
|
67
120
|
puts "baha " + Baha::VERSION
|
data/lib/baha/config.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'pathname'
|
3
3
|
require 'baha/log'
|
4
|
+
require 'baha/dockerfile'
|
4
5
|
|
5
6
|
class Baha::Config
|
6
7
|
DEFAULTS = {
|
@@ -84,6 +85,18 @@ class Baha::Config
|
|
84
85
|
LOG.error { "Unable to find image include: #{path}"}
|
85
86
|
next
|
86
87
|
end
|
88
|
+
elsif image.has_key?('dockerfile')
|
89
|
+
path = Pathname.new(image['dockerfile'])
|
90
|
+
file = resolve_file(path)
|
91
|
+
if file
|
92
|
+
dockerfile = Baha::Dockerfile.parse(file)
|
93
|
+
dockerfile['name'] = image['name']
|
94
|
+
dockerfile['tag'] = image['tag']
|
95
|
+
yield Baha::Image.new(self,dockerfile)
|
96
|
+
else
|
97
|
+
LOG.error { "Unable to find dockerfile: #{path}"}
|
98
|
+
next
|
99
|
+
end
|
87
100
|
else
|
88
101
|
yield Baha::Image.new(self,image)
|
89
102
|
end
|
@@ -15,7 +15,11 @@ module Baha
|
|
15
15
|
when Fixnum
|
16
16
|
config['ExposedPorts']["#{port}/tcp"] = {}
|
17
17
|
when String
|
18
|
-
|
18
|
+
if port.match(/^\d+$/)
|
19
|
+
config['ExposedPorts']["#{port}/tcp"] = {}
|
20
|
+
else
|
21
|
+
config['ExposedPorts'][port] = {}
|
22
|
+
end
|
19
23
|
end
|
20
24
|
end
|
21
25
|
end
|
@@ -24,7 +28,7 @@ module Baha
|
|
24
28
|
raise ERROR("should be an array") unless @value.kind_of?(Array)
|
25
29
|
@value.each_with_index do |item,index|
|
26
30
|
if item.kind_of?(String)
|
27
|
-
unless /(\d+)\/(tcp|udp)
|
31
|
+
unless /(\d+)(\/(tcp|udp))?/ =~ item
|
28
32
|
raise ERROR("#{index}: '#{item}' should be in the form 8080/tcp")
|
29
33
|
end
|
30
34
|
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'docker'
|
2
|
+
require 'baha/container_options'
|
3
|
+
require 'baha/log'
|
4
|
+
require 'set'
|
5
|
+
require 'json'
|
6
|
+
require 'csv'
|
7
|
+
require 'yaml'
|
8
|
+
|
9
|
+
module Baha
|
10
|
+
class DockerfileParseError < RuntimeError
|
11
|
+
attr_reader :file, :line, :text
|
12
|
+
def initialize(file,line, text)
|
13
|
+
super("Unable to parse Dockerfile #{file}:#{line}\n#{text}")
|
14
|
+
@file = file
|
15
|
+
@line = line
|
16
|
+
@text = text
|
17
|
+
end
|
18
|
+
end
|
19
|
+
class Dockerfile
|
20
|
+
LOG = Baha::Log.for_name("Dockerfile")
|
21
|
+
class << self
|
22
|
+
def parse(file)
|
23
|
+
LOG.info { "Parsing Dockerfile: #{file}" }
|
24
|
+
linenum = 0
|
25
|
+
image = {
|
26
|
+
'run' => [],
|
27
|
+
'pre_build' => [],
|
28
|
+
'config' => {}
|
29
|
+
}
|
30
|
+
multiline = []
|
31
|
+
f = File.open(file, "r")
|
32
|
+
begin
|
33
|
+
f.each_line do |rawline|
|
34
|
+
linenum = linenum + 1
|
35
|
+
line = rawline.chomp.gsub(/^\s*/,'')
|
36
|
+
# Skip empty lines
|
37
|
+
next if line.empty?
|
38
|
+
# Skip comments
|
39
|
+
next if line.match(/^#/)
|
40
|
+
|
41
|
+
# Buffer multi-lines
|
42
|
+
if line.match(/\\$/)
|
43
|
+
multiline << line.chop
|
44
|
+
next
|
45
|
+
end
|
46
|
+
multiline << line
|
47
|
+
|
48
|
+
|
49
|
+
line_to_parse = multiline.join("\n")
|
50
|
+
LOG.debug { "Parsing #{linenum}: #{line_to_parse}" }
|
51
|
+
unless parse_line(image,line_to_parse)
|
52
|
+
raise DockerfileParseError.new(file,linenum, line_to_parse)
|
53
|
+
end
|
54
|
+
multiline = []
|
55
|
+
end
|
56
|
+
ensure
|
57
|
+
f.close
|
58
|
+
end
|
59
|
+
image
|
60
|
+
end
|
61
|
+
|
62
|
+
def as_array(args)
|
63
|
+
begin
|
64
|
+
return JSON.parse(args)
|
65
|
+
rescue
|
66
|
+
return CSV.parse_line(args,{:col_sep => " ", })
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def as_cmd(cmd)
|
71
|
+
begin
|
72
|
+
return JSON.parse(cmd)
|
73
|
+
rescue
|
74
|
+
cmd
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def append_attr(image,key,value)
|
79
|
+
image[key] = [] unless image.has_key?(key)
|
80
|
+
case value
|
81
|
+
when Array
|
82
|
+
image[key].concat(value)
|
83
|
+
else
|
84
|
+
image[key].push(value)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Resolve environment variables in the string
|
89
|
+
def resolve_env(image,value)
|
90
|
+
if image['config'].has_key?('env')
|
91
|
+
env = image['config']['env']
|
92
|
+
env.keys.reduce(value) do |v,k|
|
93
|
+
r = Regexp.new("(?<!\\\\)[$](?:\\{#{k}\\}|#{k})")
|
94
|
+
v.gsub(r,env[k])
|
95
|
+
end
|
96
|
+
else
|
97
|
+
value
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Sets the workdir on the image config
|
102
|
+
def set_workdir(image,value)
|
103
|
+
wd = image['config']['workingdir']
|
104
|
+
if wd
|
105
|
+
wd = (Pathname.new(wd) + value).to_s
|
106
|
+
else
|
107
|
+
wd = value
|
108
|
+
end
|
109
|
+
image['config']['workingdir'] = resolve_env(image,wd)
|
110
|
+
end
|
111
|
+
|
112
|
+
def set_env(image,value)
|
113
|
+
k,v = value.split(/\s+/,2)
|
114
|
+
image['config']['env'] ||= {}
|
115
|
+
image['config']['env'][k] = v
|
116
|
+
end
|
117
|
+
|
118
|
+
# Parse a line and configure the image
|
119
|
+
def parse_line(image,line)
|
120
|
+
cmd, args = line.split(/\s+/,2)
|
121
|
+
cmd = cmd.downcase.to_sym
|
122
|
+
case cmd
|
123
|
+
when :from
|
124
|
+
image['parent'] = args
|
125
|
+
when :maintainer
|
126
|
+
image['maintainer'] = args
|
127
|
+
when :expose
|
128
|
+
append_attr(image['config'],'exposedports',resolve_env(image,args))
|
129
|
+
when :volume
|
130
|
+
append_attr(image['config'],'volumes',as_array(resolve_env(image,args)))
|
131
|
+
when :entrypoint
|
132
|
+
image['config']['entrypoint'] = as_cmd(args)
|
133
|
+
when :cmd
|
134
|
+
image['config']['cmd'] = as_cmd(args)
|
135
|
+
when :run
|
136
|
+
image['run'] << as_cmd(args)
|
137
|
+
when :user
|
138
|
+
image['config']['user'] = resolve_env(image,args)
|
139
|
+
when :workdir
|
140
|
+
set_workdir(image,args)
|
141
|
+
when :env
|
142
|
+
set_env(image,resolve_env(image,args))
|
143
|
+
else
|
144
|
+
return false
|
145
|
+
end
|
146
|
+
true
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
data/lib/baha/image.rb
CHANGED
@@ -65,7 +65,7 @@ module Baha
|
|
65
65
|
end
|
66
66
|
|
67
67
|
end
|
68
|
-
attr_reader :parent, :image, :maintainer, :options, :pre_build, :bind, :command, :timeout, :workspace, :name, :tags
|
68
|
+
attr_reader :parent, :image, :maintainer, :options, :pre_build, :bind, :command, :timeout, :workspace, :name, :tags, :run
|
69
69
|
|
70
70
|
def initialize(config,image)
|
71
71
|
@parent = Baha::Image.parse_with_default(image['parent'] || config.defaults[:parent], config.defaults[:repository])
|
@@ -76,6 +76,7 @@ module Baha
|
|
76
76
|
@pre_build = image['pre_build']
|
77
77
|
@bind = image['bind'] || config.defaults[:bind]
|
78
78
|
@command = image['command'] || config.defaults[:command]
|
79
|
+
@run = image['run']
|
79
80
|
@timeout = image['timeout'] || config.defaults[:timeout]
|
80
81
|
@workspace = config.workspace + (image['workspace'] || @image[:name])
|
81
82
|
@name = @image[:name]
|
data/lib/baha/version.rb
CHANGED
data/spec/builder_spec.rb
CHANGED
@@ -44,10 +44,15 @@ describe Baha::Builder do
|
|
44
44
|
let(:image2) {
|
45
45
|
double('image2')
|
46
46
|
}
|
47
|
+
let (:init) {
|
48
|
+
double('init.sh')
|
49
|
+
}
|
47
50
|
before do
|
48
51
|
allow_any_instance_of(Baha::Image).to receive(:needs_update?).and_return(true)
|
49
52
|
allow_any_instance_of(Baha::Image).to receive(:parent_id).and_return('AAAA')
|
50
53
|
allow(Docker::Container).to receive(:create).and_return(container)
|
54
|
+
allow(File).to receive(:open).and_call_original
|
55
|
+
allow(File).to receive(:open).with(pathname_matching(/init.sh$/),'w').and_yield(init)
|
51
56
|
allow(container).to receive(:start)
|
52
57
|
allow(container).to receive(:stop)
|
53
58
|
allow(container).to receive(:streaming_logs).with({"stdout"=>true, "stderr"=>true, "follow"=>true, "timestamps"=>false}).and_yield(:stdout,"console message").and_yield(:stderr,"error line")
|
@@ -57,22 +62,30 @@ describe Baha::Builder do
|
|
57
62
|
allow(Docker::Image).to receive(:get).with('BBBB').and_return(image)
|
58
63
|
allow(image).to receive(:tag)
|
59
64
|
allow(container).to receive(:remove)
|
65
|
+
allow(init).to receive(:write)
|
60
66
|
end
|
61
67
|
it 'executes pre_build step' do
|
62
68
|
subject.build!
|
63
69
|
expect(Baha::PreBuild::Module).to have_received(:execute).twice.with(hash_including('download' => "http://www.google.com"))
|
64
70
|
end
|
71
|
+
it 'write run script' do
|
72
|
+
subject.build!
|
73
|
+
expect(init).to have_received(:write).with("#!/bin/sh\n").ordered
|
74
|
+
expect(init).to have_received(:write).with("set -xe\n").ordered
|
75
|
+
expect(init).to have_received(:write).with("echo \"Hello\"\n").ordered
|
76
|
+
expect(init).to have_received(:write).with("/bin/echo World\n").ordered
|
77
|
+
end
|
65
78
|
it 'creates containers' do
|
66
79
|
subject.build!
|
67
80
|
expect(Docker::Container).to have_received(:create).twice.with({"Image"=>"AAAA", "Cmd"=>["/bin/bash", "./init.sh"], "Workingdir"=>"/.baha"})
|
68
81
|
end
|
69
82
|
it 'starts containers' do
|
70
83
|
subject.build!
|
71
|
-
expect(container).to have_received(:start).
|
84
|
+
expect(container).to have_received(:start).exactly(3).times
|
72
85
|
end
|
73
86
|
it 'commits containers' do
|
74
87
|
subject.build!
|
75
|
-
expect(container).to have_received(:commit).with({"run"=>{"ExposedPorts"=>{"8080/tcp"=>{}}}}).ordered
|
88
|
+
expect(container).to have_received(:commit).with({"run"=>{"ExposedPorts"=>{"8080/tcp"=>{}, "8081/tcp"=>{}, "8009/tcp"=>{}}}}).ordered
|
76
89
|
expect(container).to have_received(:commit).with({'run' => {}}).ordered
|
77
90
|
end
|
78
91
|
it 'tags image' do
|
data/spec/config_spec.rb
CHANGED
@@ -39,6 +39,22 @@ describe Baha::Config do
|
|
39
39
|
its(:configdir) { should eq(fixture_path) }
|
40
40
|
its(:workspace) { should eq(fixture_path + 'workspace') }
|
41
41
|
end
|
42
|
+
context "with dockerfile" do
|
43
|
+
subject { Baha::Config.load(fixture('config_dockerfile.yml')) }
|
44
|
+
its(:defaults) { should eq(
|
45
|
+
{
|
46
|
+
:parent=>"ubuntu:14.04.1",
|
47
|
+
:bind=>"/.baha",
|
48
|
+
:repository=>'docker.example.com/baha',
|
49
|
+
:maintainer => "Ishmael <ishmael@example.com>",
|
50
|
+
:command => ['/bin/bash','./init.sh'],
|
51
|
+
:timeout => 1200
|
52
|
+
})
|
53
|
+
}
|
54
|
+
its(:options) { should eq({}) }
|
55
|
+
its(:configdir) { should eq(fixture_path) }
|
56
|
+
its(:workspace) { should eq(fixture_path + 'workspace') }
|
57
|
+
end
|
42
58
|
context "with DOCKER_CERT_PATH set" do
|
43
59
|
subject { Baha::Config.load(fixture('config_embedded.yml')) }
|
44
60
|
before do
|
@@ -87,7 +103,7 @@ describe Baha::Config do
|
|
87
103
|
subject.each_image do |image|
|
88
104
|
images << image
|
89
105
|
end
|
90
|
-
expect(images.size).to eq(
|
106
|
+
expect(images.size).to eq(3)
|
91
107
|
end
|
92
108
|
end
|
93
109
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'baha/config'
|
3
|
+
|
4
|
+
describe Baha::Config do
|
5
|
+
before do
|
6
|
+
ENV['DOCKER_CERT_PATH'] = nil
|
7
|
+
ENV['DOCKER_TLS_VERIFY'] = nil
|
8
|
+
end
|
9
|
+
describe "#parse" do
|
10
|
+
context "with valid Dockerfile" do
|
11
|
+
subject { Baha::Dockerfile.parse(fixture('Dockerfile')) }
|
12
|
+
its(['parent']) { should eq('ubuntu:14.04.1')}
|
13
|
+
its(['run']) { should eq([
|
14
|
+
"echo \"Hello\"",
|
15
|
+
["/bin/echo", "World"],
|
16
|
+
"echo Hello \nworld \nmultiline"])}
|
17
|
+
its(['config']) { should eq({
|
18
|
+
"entrypoint"=>["/bin/bash"],
|
19
|
+
"exposedports"=>["8080", "8081", "8009"],
|
20
|
+
"env"=>{
|
21
|
+
"HOME"=>"/home/user",
|
22
|
+
"BIN_DIR"=>"bin",
|
23
|
+
"HOME2"=>"/home/user/two",
|
24
|
+
"USER"=>"daemon"},
|
25
|
+
"workingdir"=>"/home/user/bin/b/c",
|
26
|
+
"cmd"=>"-l",
|
27
|
+
"user"=>"daemon",
|
28
|
+
"volumes"=>[
|
29
|
+
"/home/user",
|
30
|
+
"/logs",
|
31
|
+
"/home/user/logs",
|
32
|
+
"/data"]})}
|
33
|
+
end
|
34
|
+
context "with invalid Dockerfile" do
|
35
|
+
it { expect { Baha::Dockerfile.parse(fixture('Dockerfile.invalid')) }.to raise_error(Baha::DockerfileParseError) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Comment
|
2
|
+
FROM ubuntu:14.04.1
|
3
|
+
|
4
|
+
MAINTAINER "Captain Ahab" <ahab@example.com>
|
5
|
+
|
6
|
+
RUN echo "Hello"
|
7
|
+
RUN ["/bin/echo", "World"]
|
8
|
+
RUN echo Hello \
|
9
|
+
world \
|
10
|
+
multiline
|
11
|
+
|
12
|
+
ENTRYPOINT ["/bin/bash"]
|
13
|
+
|
14
|
+
EXPOSE 8080
|
15
|
+
EXPOSE 8081
|
16
|
+
EXPOSE 8009
|
17
|
+
|
18
|
+
ENV HOME /home/user
|
19
|
+
ENV BIN_DIR bin
|
20
|
+
ENV HOME2 $HOME/two
|
21
|
+
ENV USER daemon
|
22
|
+
|
23
|
+
WORKDIR ${HOME}
|
24
|
+
WORKDIR $BIN_DIR
|
25
|
+
WORKDIR b
|
26
|
+
WORKDIR c
|
27
|
+
|
28
|
+
CMD -l
|
29
|
+
|
30
|
+
USER ${USER}
|
31
|
+
|
32
|
+
VOLUME $HOME /logs
|
33
|
+
VOLUME ["$HOME/logs", "/data"]
|
@@ -9,4 +9,10 @@ images:
|
|
9
9
|
- parent: 'ubuntu:14.04.1'
|
10
10
|
name: base
|
11
11
|
tag: 1.0.0
|
12
|
-
maintainer: '"Captain Ahab" <ahab@example.com>'
|
12
|
+
maintainer: '"Captain Ahab" <ahab@example.com>'
|
13
|
+
- dockerfile: Dockerfile
|
14
|
+
name: dockerfile
|
15
|
+
tag: 1.0.0
|
16
|
+
- dockerfile: no such dockerfile
|
17
|
+
name: invalid
|
18
|
+
tag: 1.0.0
|
data/spec/spec_helper.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require "codeclimate-test-reporter"
|
2
|
+
CodeClimate::TestReporter.start
|
3
|
+
|
1
4
|
require 'simplecov'
|
2
5
|
SimpleCov.start do
|
3
6
|
add_filter '/spec/'
|
@@ -36,4 +39,15 @@ RSpec.configure do |config|
|
|
36
39
|
config.before(:each) do
|
37
40
|
Baha::Log.logfile = StringIO.new
|
38
41
|
end
|
42
|
+
end
|
43
|
+
|
44
|
+
RSpec::Matchers.define :pathname_matching do |expected|
|
45
|
+
match do |actual|
|
46
|
+
case expected
|
47
|
+
when Regexp
|
48
|
+
expected.match(actual.to_s) != nil
|
49
|
+
else
|
50
|
+
expected == actual
|
51
|
+
end
|
52
|
+
end
|
39
53
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: baha
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justen Walker
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-12-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -136,7 +136,11 @@ dependencies:
|
|
136
136
|
- - ~>
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: 0.9.1
|
139
|
-
description: Baha -
|
139
|
+
description: ! 'Baha is a command-line utility that assists in the creation of docker
|
140
|
+
images.
|
141
|
+
|
142
|
+
It addresses some of Dockerfiles shortcomings and encourages smaller, reusable,
|
143
|
+
tagged images.'
|
140
144
|
email:
|
141
145
|
- justen.walker+github@gmail.com
|
142
146
|
executables:
|
@@ -146,6 +150,7 @@ extra_rdoc_files: []
|
|
146
150
|
files:
|
147
151
|
- .gitignore
|
148
152
|
- .travis.yml
|
153
|
+
- CHANGELOG.md
|
149
154
|
- Gemfile
|
150
155
|
- LICENSE.txt
|
151
156
|
- README.md
|
@@ -156,6 +161,7 @@ files:
|
|
156
161
|
- example/base/init.sh.erb
|
157
162
|
- example/base/test-template.erb
|
158
163
|
- example/example.yml
|
164
|
+
- example/memcached/Dockerfile
|
159
165
|
- example/rvm/image.yml
|
160
166
|
- example/rvm/init.sh.erb
|
161
167
|
- lib/baha.rb
|
@@ -170,6 +176,7 @@ files:
|
|
170
176
|
- lib/baha/container_options/invalid_option_error.rb
|
171
177
|
- lib/baha/container_options/option.rb
|
172
178
|
- lib/baha/container_options/volumes.rb
|
179
|
+
- lib/baha/dockerfile.rb
|
173
180
|
- lib/baha/image.rb
|
174
181
|
- lib/baha/log.rb
|
175
182
|
- lib/baha/pre_build.rb
|
@@ -186,9 +193,13 @@ files:
|
|
186
193
|
- spec/container_options/exposed_ports_spec.rb
|
187
194
|
- spec/container_options/option_spec.rb
|
188
195
|
- spec/container_options/volumes_spec.rb
|
196
|
+
- spec/dockerfile_spec.rb
|
197
|
+
- spec/fixtures/Dockerfile
|
198
|
+
- spec/fixtures/Dockerfile.invalid
|
189
199
|
- spec/fixtures/base_image.yml
|
190
200
|
- spec/fixtures/config_build.yml
|
191
201
|
- spec/fixtures/config_build_image.yml
|
202
|
+
- spec/fixtures/config_dockerfile.yml
|
192
203
|
- spec/fixtures/config_eachimage.yml
|
193
204
|
- spec/fixtures/config_embedded.yml
|
194
205
|
- spec/fixtures/config_include.yml
|
@@ -203,7 +214,7 @@ files:
|
|
203
214
|
- spec/pre_build/template_spec.rb
|
204
215
|
- spec/pre_build_spec.rb
|
205
216
|
- spec/spec_helper.rb
|
206
|
-
homepage:
|
217
|
+
homepage: https://github.com/justenwalker/baha
|
207
218
|
licenses:
|
208
219
|
- MIT
|
209
220
|
metadata: {}
|
@@ -226,7 +237,7 @@ rubyforge_project:
|
|
226
237
|
rubygems_version: 2.1.11
|
227
238
|
signing_key:
|
228
239
|
specification_version: 4
|
229
|
-
summary: Baha -
|
240
|
+
summary: Baha is a command-line utility that assists in the creation of docker images.
|
230
241
|
test_files:
|
231
242
|
- spec/builder_spec.rb
|
232
243
|
- spec/config_spec.rb
|
@@ -236,9 +247,13 @@ test_files:
|
|
236
247
|
- spec/container_options/exposed_ports_spec.rb
|
237
248
|
- spec/container_options/option_spec.rb
|
238
249
|
- spec/container_options/volumes_spec.rb
|
250
|
+
- spec/dockerfile_spec.rb
|
251
|
+
- spec/fixtures/Dockerfile
|
252
|
+
- spec/fixtures/Dockerfile.invalid
|
239
253
|
- spec/fixtures/base_image.yml
|
240
254
|
- spec/fixtures/config_build.yml
|
241
255
|
- spec/fixtures/config_build_image.yml
|
256
|
+
- spec/fixtures/config_dockerfile.yml
|
242
257
|
- spec/fixtures/config_eachimage.yml
|
243
258
|
- spec/fixtures/config_embedded.yml
|
244
259
|
- spec/fixtures/config_include.yml
|