dockerchain 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +44 -0
- data/Rakefile +1 -0
- data/bin/dockerchain +21 -0
- data/dockerchain.gemspec +25 -0
- data/lib/dockerchain.rb +26 -0
- data/lib/dockerchain/cli.rb +6 -0
- data/lib/dockerchain/cli/option_parser.rb +77 -0
- data/lib/dockerchain/runner.rb +101 -0
- data/lib/dockerchain/version.rb +3 -0
- data/sshable.yml +4 -0
- metadata +112 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Cloudspace
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# Dockerchain
|
2
|
+
|
3
|
+
Build a series of docker images from a set of dockerfiles, chaining the output of one into the next.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
git clone git@github.com:jeremiahishere/dockerchain.git
|
8
|
+
cd dockerchain
|
9
|
+
bundle install
|
10
|
+
|
11
|
+
If necessary, clone git@github.com:jeremiahishere/dockerfile_ast.git and
|
12
|
+
manually install it.
|
13
|
+
|
14
|
+
## Usage
|
15
|
+
|
16
|
+
write a yml file similar to sshable.yml. The script will automatically prepend the image_name field with 'dockerchain/'. The repos should have a Dockerfile in their root directory and must be available to the current user.
|
17
|
+
|
18
|
+
run `bundle exec dockerchain path/to/my_config_file.yml`
|
19
|
+
|
20
|
+
## How it works
|
21
|
+
|
22
|
+
1. Write a yaml file if records that look like this
|
23
|
+
|
24
|
+
- image_name: sshable
|
25
|
+
repo_url: https://github.com/imightbeinatree/docker-sshable.git
|
26
|
+
|
27
|
+
2. Install the `dockerfile_ast` gem located at http://github.com/jeremiahishere/dockerfile_ast
|
28
|
+
|
29
|
+
3. Parse the yaml file and loop through the repos
|
30
|
+
|
31
|
+
1. clone the first repo into the repos directory
|
32
|
+
2. run `docker build -t dockerchain/<image_name>` on the dockerfile in the directory
|
33
|
+
3. go to the next directory
|
34
|
+
4. run `ruby replace_dockerfile_from.rb /path/to_dockerfile previous_build_name` on the Dockerfile in the repo
|
35
|
+
5. run `docker build -e dockerchain/<second_image_name>` on the new dockerfile
|
36
|
+
6. repeat 3.1 through 3.5 on all of the repos in the yaml file, changing the image name for each one
|
37
|
+
|
38
|
+
## Contributing
|
39
|
+
|
40
|
+
1. Fork it ( http://github.com/<my-github-username>/dockerchain/fork )
|
41
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
42
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
43
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
44
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/dockerchain
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'dockerchain'
|
5
|
+
require 'dockerchain/cli'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'dockerchain'
|
9
|
+
require 'dockerchain/cli'
|
10
|
+
end
|
11
|
+
|
12
|
+
option_parser = Dockerchain::CLI::OptionParser.new
|
13
|
+
options = option_parser.parse(ARGV)
|
14
|
+
chain_file = ARGV.pop
|
15
|
+
unless chain_file
|
16
|
+
puts option_parser.help
|
17
|
+
exit(1)
|
18
|
+
end
|
19
|
+
|
20
|
+
runner = Dockerchain::Runner.new(options)
|
21
|
+
runner.run(chain_file)
|
data/dockerchain.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'dockerchain/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'dockerchain'
|
8
|
+
spec.version = Dockerchain::VERSION
|
9
|
+
spec.authors = ['Adam Dunson', 'Jeremiah Hemphill']
|
10
|
+
spec.email = ['adam@cloudspace.com', 'jeremiah@cloudspace.com']
|
11
|
+
spec.summary = %q{Build a series of docker images from a set of dockerfiles, chaining the output of one into the next.}
|
12
|
+
spec.description = %q{Build a series of docker images from a set of dockerfiles, chaining the output of one into the next.}
|
13
|
+
spec.homepage = ''
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.5'
|
22
|
+
spec.add_development_dependency 'rake'
|
23
|
+
|
24
|
+
spec.add_dependency 'dockerfile_ast'
|
25
|
+
end
|
data/lib/dockerchain.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'dockerchain/version'
|
2
|
+
require 'dockerchain/runner'
|
3
|
+
|
4
|
+
require 'yaml'
|
5
|
+
require 'logger'
|
6
|
+
require 'fileutils'
|
7
|
+
require 'dockerfile_ast'
|
8
|
+
|
9
|
+
# This is a global. Deal with it.
|
10
|
+
def blank?(var)
|
11
|
+
var.nil? || var.empty?
|
12
|
+
end
|
13
|
+
|
14
|
+
module Dockerchain
|
15
|
+
def self.src_path
|
16
|
+
'repos'
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.build_path
|
20
|
+
'dockerchain'
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.logger
|
24
|
+
@@logger ||= Logger.new(STDOUT)
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'optparse/time'
|
3
|
+
require 'ostruct'
|
4
|
+
|
5
|
+
module Dockerchain
|
6
|
+
module CLI
|
7
|
+
class OptionParser
|
8
|
+
attr_accessor :options, :opt_parser
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
# The options specified on the command line will be collected in *options*.
|
12
|
+
# We set default values here.
|
13
|
+
@options = ::OpenStruct.new
|
14
|
+
options.chain_file = ''
|
15
|
+
options.log_file = ''
|
16
|
+
options.src_path = ''
|
17
|
+
options.build_path = ''
|
18
|
+
|
19
|
+
@opt_parser = ::OptionParser.new do |opts|
|
20
|
+
opts.banner = 'Usage: dockerchain CHAINFILE [options]'
|
21
|
+
|
22
|
+
opts.separator ''
|
23
|
+
opts.separator 'Specific options:'
|
24
|
+
|
25
|
+
opts.on('-l', '--logfile LOGFILE',
|
26
|
+
'Specify the LOGFILE to log output to') do |log_file|
|
27
|
+
options.log_file = log_file
|
28
|
+
end
|
29
|
+
|
30
|
+
opts.on('-s', '--srcpath SRCPATH',
|
31
|
+
'Specify the SRCPATH for where to download repositories') do |src_path|
|
32
|
+
options.src_path = src_path
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on('-b', '--buildpath BUILDPATH',
|
36
|
+
'Specify the BUILDPATH for where to do work') do |build_path|
|
37
|
+
options.build_path = build_path
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.separator ''
|
41
|
+
opts.separator 'Common options:'
|
42
|
+
|
43
|
+
# No argument, shows at tail. This will print an options summary.
|
44
|
+
# Try it and see!
|
45
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
46
|
+
puts opts
|
47
|
+
exit
|
48
|
+
end
|
49
|
+
|
50
|
+
# Another typical switch to print the version.
|
51
|
+
opts.on_tail('-v', '--version', 'Show version') do
|
52
|
+
puts Dockerchain::VERSION
|
53
|
+
exit
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
def help
|
60
|
+
opt_parser.help
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# Return a structure describing the options.
|
65
|
+
#
|
66
|
+
def parse(args)
|
67
|
+
begin
|
68
|
+
opt_parser.parse!(args)
|
69
|
+
rescue ::OptionParser::InvalidOption => e
|
70
|
+
puts help
|
71
|
+
exit(1)
|
72
|
+
end
|
73
|
+
options
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Dockerchain
|
2
|
+
class Runner
|
3
|
+
def initialize(options)
|
4
|
+
@logger = Logger.new(options.log_file) unless options.log_file.blank?
|
5
|
+
@src_path = Logger.new(options.src_path) unless options.src_path.blank?
|
6
|
+
@build_path = Logger.new(options.build_path) unless options.build_path.blank?
|
7
|
+
end
|
8
|
+
|
9
|
+
def run(config_file = 'dockerchain.yml')
|
10
|
+
logger.info "Removing repos from previous run"
|
11
|
+
runcmd "rm -rf '#{src_path}'"
|
12
|
+
|
13
|
+
logger.info "Creating #{src_path}..."
|
14
|
+
FileUtils.mkpath(src_path)
|
15
|
+
|
16
|
+
images = parse(config_file)
|
17
|
+
|
18
|
+
image_name = ''
|
19
|
+
|
20
|
+
images.each do |repo|
|
21
|
+
previous_image_name = image_name
|
22
|
+
image_name = repo['image_name'] ? File.join(build_path, repo['image_name']) : ""
|
23
|
+
repo_url = repo['repo_url'] || ""
|
24
|
+
|
25
|
+
command = "git clone '#{repo_url}' '#{File.join(src_path, image_name)}'"
|
26
|
+
logger.info command
|
27
|
+
runcmd command
|
28
|
+
|
29
|
+
logger.info "Rewriting the dockerfile for #{image_name} to point at #{previous_image_name}"
|
30
|
+
replace_dockerfile_from(File.join(src_path, image_name, 'Dockerfile'), previous_image_name)
|
31
|
+
|
32
|
+
command = "docker build -t #{image_name} #{File.join(src_path, image_name)}"
|
33
|
+
logger.info command
|
34
|
+
runcmd command
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def logger
|
41
|
+
@logger ||= Dockerchain.logger
|
42
|
+
end
|
43
|
+
|
44
|
+
def src_path
|
45
|
+
@src_path ||= Dockerchain.src_path
|
46
|
+
end
|
47
|
+
|
48
|
+
def build_path
|
49
|
+
@build_path ||= Dockerchain.build_path
|
50
|
+
end
|
51
|
+
|
52
|
+
def parse(config_file)
|
53
|
+
YAML.load(File.read(config_file))
|
54
|
+
end
|
55
|
+
|
56
|
+
def runcmd(command = '')
|
57
|
+
`#{command}`
|
58
|
+
|
59
|
+
if $? != 0
|
60
|
+
logger.error "Command returned #{$?}. Exiting..."
|
61
|
+
Kernel.exit
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def update_from_node!(tree, previous_build)
|
66
|
+
if tree.elements.nil?
|
67
|
+
return
|
68
|
+
else
|
69
|
+
tree.elements.each do |element|
|
70
|
+
if element.respond_to?(:title) && element.title == :from
|
71
|
+
logger.info "Found the FROM instruction, updating #{element.inspect} to #{previous_build}"
|
72
|
+
# Not the right way to do this
|
73
|
+
string_literal = element.elements[0]
|
74
|
+
string_literal.instance_variable_set(:@input, previous_build)
|
75
|
+
string_literal.instance_variable_set(:@interval, 0..previous_build.size)
|
76
|
+
else
|
77
|
+
update_from_node!(element, previous_build)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def replace_dockerfile_from(filename, previous_build = '')
|
84
|
+
dockerfile = File.read(filename)
|
85
|
+
|
86
|
+
if !previous_build.blank?
|
87
|
+
parser = DockerfileAst::Parser.new
|
88
|
+
ast = parser.parse(dockerfile)
|
89
|
+
update_from_node!(ast, previous_build)
|
90
|
+
writer = DockerfileAst::Writer.new(ast)
|
91
|
+
|
92
|
+
File.open(filename, 'w') do |file|
|
93
|
+
logger.info "Rewriting the dockerfile"
|
94
|
+
file.write writer.write
|
95
|
+
end
|
96
|
+
else
|
97
|
+
logger.info "Not changing the Dockerfile because no previous build was found"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/sshable.yml
ADDED
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dockerchain
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Adam Dunson
|
9
|
+
- Jeremiah Hemphill
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2014-05-15 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: bundler
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '1.5'
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ~>
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '1.5'
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: rake
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ! '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
type: :development
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: dockerfile_ast
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
description: Build a series of docker images from a set of dockerfiles, chaining the
|
64
|
+
output of one into the next.
|
65
|
+
email:
|
66
|
+
- adam@cloudspace.com
|
67
|
+
- jeremiah@cloudspace.com
|
68
|
+
executables:
|
69
|
+
- dockerchain
|
70
|
+
extensions: []
|
71
|
+
extra_rdoc_files: []
|
72
|
+
files:
|
73
|
+
- .gitignore
|
74
|
+
- Gemfile
|
75
|
+
- LICENSE.txt
|
76
|
+
- README.md
|
77
|
+
- Rakefile
|
78
|
+
- bin/dockerchain
|
79
|
+
- dockerchain.gemspec
|
80
|
+
- lib/dockerchain.rb
|
81
|
+
- lib/dockerchain/cli.rb
|
82
|
+
- lib/dockerchain/cli/option_parser.rb
|
83
|
+
- lib/dockerchain/runner.rb
|
84
|
+
- lib/dockerchain/version.rb
|
85
|
+
- sshable.yml
|
86
|
+
homepage: ''
|
87
|
+
licenses:
|
88
|
+
- MIT
|
89
|
+
post_install_message:
|
90
|
+
rdoc_options: []
|
91
|
+
require_paths:
|
92
|
+
- lib
|
93
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ! '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
|
+
none: false
|
101
|
+
requirements:
|
102
|
+
- - ! '>='
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
requirements: []
|
106
|
+
rubyforge_project:
|
107
|
+
rubygems_version: 1.8.25
|
108
|
+
signing_key:
|
109
|
+
specification_version: 3
|
110
|
+
summary: Build a series of docker images from a set of dockerfiles, chaining the output
|
111
|
+
of one into the next.
|
112
|
+
test_files: []
|