baha 0.0.1
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.
- checksums.yaml +15 -0
- data/.gitignore +15 -0
- data/.travis.yml +11 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +156 -0
- data/Rakefile +21 -0
- data/baha.gemspec +31 -0
- data/bin/baha +5 -0
- data/example/.gitignore +1 -0
- data/example/base/init.sh.erb +11 -0
- data/example/base/test-template.erb +22 -0
- data/example/example.yml +54 -0
- data/example/rvm/image.yml +33 -0
- data/example/rvm/init.sh.erb +12 -0
- data/lib/baha/builder.rb +130 -0
- data/lib/baha/cli.rb +69 -0
- data/lib/baha/config.rb +142 -0
- data/lib/baha/container_options/cmd.rb +33 -0
- data/lib/baha/container_options/entrypoint.rb +10 -0
- data/lib/baha/container_options/env.rb +21 -0
- data/lib/baha/container_options/exposed_ports.rb +35 -0
- data/lib/baha/container_options/invalid_option_error.rb +15 -0
- data/lib/baha/container_options/option.rb +59 -0
- data/lib/baha/container_options/volumes.rb +24 -0
- data/lib/baha/container_options.rb +38 -0
- data/lib/baha/image.rb +154 -0
- data/lib/baha/log.rb +80 -0
- data/lib/baha/pre_build/command.rb +51 -0
- data/lib/baha/pre_build/download.rb +28 -0
- data/lib/baha/pre_build/template.rb +48 -0
- data/lib/baha/pre_build.rb +47 -0
- data/lib/baha/version.rb +3 -0
- data/lib/baha/workspace.rb +13 -0
- data/lib/baha.rb +5 -0
- data/spec/builder_spec.rb +103 -0
- data/spec/config_spec.rb +93 -0
- data/spec/container_options/cmd_spec.rb +46 -0
- data/spec/container_options/entrypoint_spec.rb +32 -0
- data/spec/container_options/env_spec.rb +26 -0
- data/spec/container_options/exposed_ports_spec.rb +32 -0
- data/spec/container_options/option_spec.rb +43 -0
- data/spec/container_options/volumes_spec.rb +25 -0
- data/spec/fixtures/base_image.yml +5 -0
- data/spec/fixtures/config_build.yml +12 -0
- data/spec/fixtures/config_build_image.yml +10 -0
- data/spec/fixtures/config_eachimage.yml +12 -0
- data/spec/fixtures/config_embedded.yml +11 -0
- data/spec/fixtures/config_include.yml +7 -0
- data/spec/fixtures/config_ssl.yml +13 -0
- data/spec/fixtures/config_sslpath.yml +11 -0
- data/spec/helpers/docker_helpers.rb +31 -0
- data/spec/image_spec.rb +167 -0
- data/spec/log_spec.rb +89 -0
- data/spec/options_spec.rb +52 -0
- data/spec/pre_build/command_spec.rb +69 -0
- data/spec/pre_build/download_spec.rb +43 -0
- data/spec/pre_build/template_spec.rb +55 -0
- data/spec/pre_build_spec.rb +29 -0
- data/spec/spec_helper.rb +39 -0
- metadata +255 -0
data/lib/baha/config.rb
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'pathname'
|
3
|
+
require 'baha/log'
|
4
|
+
|
5
|
+
class Baha::Config
|
6
|
+
DEFAULTS = {
|
7
|
+
:parent => 'ubuntu:14.04.1',
|
8
|
+
:bind => '/.baha',
|
9
|
+
:command => ['/bin/bash','./init.sh'],
|
10
|
+
:repository => nil,
|
11
|
+
:maintainer => nil,
|
12
|
+
:timeout => 1200
|
13
|
+
}
|
14
|
+
LOG = Baha::Log.for_name("Config")
|
15
|
+
class << self
|
16
|
+
def load(file)
|
17
|
+
LOG.debug { "Loading file #{file}"}
|
18
|
+
filepath = Pathname.new(file)
|
19
|
+
raise ArgumentError.new("Cannot read config file #{file}") unless filepath.readable?
|
20
|
+
config = YAML.load_file(filepath)
|
21
|
+
config['configdir'] ||= filepath.dirname
|
22
|
+
Baha::Config.new(config)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_reader :configdir, :workspace, :secure, :options
|
27
|
+
attr_reader :defaults
|
28
|
+
|
29
|
+
def initialize(config)
|
30
|
+
@config = config
|
31
|
+
|
32
|
+
# Defaults
|
33
|
+
defaults = config['defaults'] || {}
|
34
|
+
raise ArgumentError.new("Expected Hash for defaults") unless defaults.is_a?(Hash)
|
35
|
+
@defaults = {}
|
36
|
+
DEFAULTS.keys.each do |k|
|
37
|
+
@defaults[k] = defaults[k] || defaults[k.to_s] || DEFAULTS[k]
|
38
|
+
end
|
39
|
+
|
40
|
+
@configdir = Pathname.new(config['configdir'] || Pathname.pwd)
|
41
|
+
@workspace = Pathname.new(config['workspace'] || @configdir + 'workspace')
|
42
|
+
@secure = false
|
43
|
+
@options = {}
|
44
|
+
init_security if ENV.has_key?('DOCKER_CERT_PATH') || config.has_key?('ssl')
|
45
|
+
end
|
46
|
+
|
47
|
+
def init_security
|
48
|
+
@secure = true
|
49
|
+
cert_path = ''
|
50
|
+
ssl_options = { }
|
51
|
+
if ENV['DOCKER_CERT_PATH']
|
52
|
+
cert_path = Pathname.new(ENV['DOCKER_CERT_PATH'])
|
53
|
+
ssl_options[:ssl_verify_peer] = (ENV['DOCKER_TLS_VERIFY'] == '1')
|
54
|
+
ssl_options[:client_cert] = (cert_path + 'cert.pem').expand_path.to_s
|
55
|
+
ssl_options[:client_key] = (cert_path + 'key.pem').expand_path.to_s
|
56
|
+
ssl_options[:ssl_ca_file] = (cert_path + 'ca.pem').expand_path.to_s
|
57
|
+
elsif @config.has_key?('ssl')
|
58
|
+
ssl = @config['ssl']
|
59
|
+
ssl_options[:ssl_verify_peer] = ssl['verify'] || false
|
60
|
+
if ssl.has_key?('cert_path')
|
61
|
+
cert_path = Pathname.new(ssl['cert_path'])
|
62
|
+
ssl_options[:client_cert] = (cert_path + 'cert.pem').expand_path.to_s
|
63
|
+
ssl_options[:client_key] = (cert_path + 'key.pem').expand_path.to_s
|
64
|
+
ssl_options[:ssl_ca_file] = (cert_path + 'ca.pem').expand_path.to_s
|
65
|
+
else
|
66
|
+
ssl_options[:client_cert] = ssl['cert']
|
67
|
+
ssl_options[:client_key] = ssl['key']
|
68
|
+
ssl_options[:ssl_ca_file] = ssl['ca']
|
69
|
+
end
|
70
|
+
end
|
71
|
+
@options.merge!(ssl_options)
|
72
|
+
end
|
73
|
+
|
74
|
+
def each_image
|
75
|
+
return unless @config.has_key?('images')
|
76
|
+
@config['images'].each do |image|
|
77
|
+
if image.has_key?('include')
|
78
|
+
path = Pathname.new(image['include'])
|
79
|
+
file = resolve_file(path)
|
80
|
+
if file
|
81
|
+
yml = YAML.load_file(file)
|
82
|
+
yield Baha::Image.new(self,yml)
|
83
|
+
else
|
84
|
+
LOG.error { "Unable to find image include: #{path}"}
|
85
|
+
next
|
86
|
+
end
|
87
|
+
else
|
88
|
+
yield Baha::Image.new(self,image)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def resolve_file(file)
|
94
|
+
filepath = Pathname.new(file)
|
95
|
+
LOG.debug { "resolve_file(#{file})" }
|
96
|
+
paths = [
|
97
|
+
filepath, # 0. Absolute path
|
98
|
+
@workspace + file, # 1. Workspace
|
99
|
+
@configdir + file, # 2. Config
|
100
|
+
Pathname.pwd + file # 3. Current directory
|
101
|
+
]
|
102
|
+
paths.reduce(nil) do |result,path|
|
103
|
+
if result.nil?
|
104
|
+
if path.exist?
|
105
|
+
result = path
|
106
|
+
LOG.debug("found file at: #{path}")
|
107
|
+
else
|
108
|
+
LOG.debug("did not find file at: #{path}")
|
109
|
+
end
|
110
|
+
end
|
111
|
+
result
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Initialize Docker Client
|
116
|
+
def init_docker!
|
117
|
+
Docker.options = @options
|
118
|
+
if @config.has_key?('docker_url')
|
119
|
+
url = @config['docker_url']
|
120
|
+
Docker.url = url
|
121
|
+
end
|
122
|
+
if @secure
|
123
|
+
Docker.url = Docker.url.gsub(/^tcp:/,'https:')
|
124
|
+
end
|
125
|
+
LOG.debug { "Docker URL: #{Docker.url}"}
|
126
|
+
LOG.debug { "Docker Options: #{Docker.options.inspect}"}
|
127
|
+
Docker.validate_version!
|
128
|
+
end
|
129
|
+
|
130
|
+
def inspect
|
131
|
+
<<-eos.gsub(/\n?\s{2,}/,'')
|
132
|
+
#{self.class.name}<
|
133
|
+
@config=#{@config.inspect},
|
134
|
+
@configdir=#{@configdir},
|
135
|
+
@workspace=#{@workspace},
|
136
|
+
@defaults=#{@defaults.inspect},
|
137
|
+
@secure=#{@secure},
|
138
|
+
@options=#{@options.inspect}
|
139
|
+
>
|
140
|
+
eos
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'baha/container_options/invalid_option_error'
|
2
|
+
require 'baha/container_options/option'
|
3
|
+
module Baha
|
4
|
+
module ContainerOptions
|
5
|
+
class Cmd < Option
|
6
|
+
|
7
|
+
def self.split_command(cmd)
|
8
|
+
require 'csv'
|
9
|
+
CSV.parse_line(cmd,{:col_sep => ' ', :skip_blanks => true, :quote_char => '"'})
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(*args)
|
13
|
+
if args.length < 2 then
|
14
|
+
@conf = 'Cmd'
|
15
|
+
super('Cmd',*args)
|
16
|
+
else
|
17
|
+
@conf = args[0]
|
18
|
+
super(*args)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
def apply(config)
|
22
|
+
if @value.kind_of?(Array)
|
23
|
+
config[@conf] = @value
|
24
|
+
else
|
25
|
+
config[@conf] = Cmd::split_command(@value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
def validate!
|
29
|
+
raise ERROR("should be an array or string") unless @value.kind_of?(Array) or @value.kind_of?(String)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'baha/container_options/option'
|
2
|
+
module Baha
|
3
|
+
module ContainerOptions
|
4
|
+
class Env < Option
|
5
|
+
def initialize(*args)
|
6
|
+
super(:env,*args)
|
7
|
+
end
|
8
|
+
def apply(config)
|
9
|
+
unless config.has_key?('Env')
|
10
|
+
config['Env'] = []
|
11
|
+
end
|
12
|
+
@value.each_pair do |k,v|
|
13
|
+
config['Env'] << "#{k}=#{v}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
def validate!
|
17
|
+
raise ERROR("should be a hash") unless @value.kind_of?(Hash)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'baha/container_options/invalid_option_error'
|
2
|
+
require 'baha/container_options/option'
|
3
|
+
module Baha
|
4
|
+
module ContainerOptions
|
5
|
+
class ExposedPorts < Option
|
6
|
+
def initialize(*args)
|
7
|
+
super(:exposedports,*args)
|
8
|
+
end
|
9
|
+
def apply(config)
|
10
|
+
unless config.has_key?('ExposedPorts')
|
11
|
+
config['ExposedPorts'] = {}
|
12
|
+
end
|
13
|
+
@value.each do |port|
|
14
|
+
case port
|
15
|
+
when Fixnum
|
16
|
+
config['ExposedPorts']["#{port}/tcp"] = {}
|
17
|
+
when String
|
18
|
+
config['ExposedPorts'][port] = {}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def validate!
|
24
|
+
raise ERROR("should be an array") unless @value.kind_of?(Array)
|
25
|
+
@value.each_with_index do |item,index|
|
26
|
+
if item.kind_of?(String)
|
27
|
+
unless /(\d+)\/(tcp|udp)/ =~ item
|
28
|
+
raise ERROR("#{index}: '#{item}' should be in the form 8080/tcp")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Baha
|
2
|
+
module ContainerOptions
|
3
|
+
class InvalidOptionError < RuntimeError
|
4
|
+
attr_reader :key
|
5
|
+
attr_reader :value
|
6
|
+
attr_reader :reason
|
7
|
+
def initialize(key,value,reason)
|
8
|
+
super("Unable to validate option: #{key}. '#{value}' #{reason}")
|
9
|
+
@key = key
|
10
|
+
@value = value
|
11
|
+
@reason = reason
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'baha/container_options/invalid_option_error'
|
2
|
+
module Baha
|
3
|
+
module ContainerOptions
|
4
|
+
class Option
|
5
|
+
KEYS = {
|
6
|
+
:cmd => 'Cmd',
|
7
|
+
:cpushares => 'CpuShares',
|
8
|
+
:cpuset => 'Cpuset',
|
9
|
+
:domainname => 'Domainname',
|
10
|
+
:entrypoint => 'Entrypoint',
|
11
|
+
:env => 'Env',
|
12
|
+
:exposedports => 'ExposedPorts',
|
13
|
+
:hostname => 'Hostname',
|
14
|
+
:image => 'Image',
|
15
|
+
:memory => 'Memory',
|
16
|
+
:memoryswap => 'MemorySwap',
|
17
|
+
:networkdisabled => 'NetworkDisabled',
|
18
|
+
:user => 'User',
|
19
|
+
:volumes => 'Volumes',
|
20
|
+
:workingdir => 'WorkingDir',
|
21
|
+
}
|
22
|
+
|
23
|
+
attr_reader :key
|
24
|
+
attr_reader :config_key
|
25
|
+
attr_reader :value
|
26
|
+
|
27
|
+
def initialize(*args)
|
28
|
+
k,@value = args
|
29
|
+
raise ArgumentError, "Cannot understand option key '#{k}'" unless k.respond_to?(:to_sym)
|
30
|
+
@key = k.to_sym.downcase
|
31
|
+
raise ERROR("Option with key '#{@key}' is not found. Expecting #{KEYS.keys.inspect}") unless KEYS.has_key?(@key)
|
32
|
+
@config_key = KEYS[@key]
|
33
|
+
end
|
34
|
+
|
35
|
+
def eql?(other)
|
36
|
+
@key == other.key and @value == other.value
|
37
|
+
end
|
38
|
+
|
39
|
+
# Apply this option to the container's config hash
|
40
|
+
def apply(config)
|
41
|
+
config[@config_key] = @value
|
42
|
+
end
|
43
|
+
|
44
|
+
# Validate the option's value
|
45
|
+
def validate!
|
46
|
+
KEYS.has_key?(@key)
|
47
|
+
end
|
48
|
+
|
49
|
+
def inspect
|
50
|
+
"#{self.class.name}<@key=#{@key.inspect},@value=#{@value.inspect}>"
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
def ERROR(reason)
|
55
|
+
InvalidOptionError.new(@key,@value,reason)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'baha/container_options/option'
|
2
|
+
module Baha
|
3
|
+
module ContainerOptions
|
4
|
+
class Volumes < Option
|
5
|
+
def initialize(*args)
|
6
|
+
super(:volumes,*args)
|
7
|
+
end
|
8
|
+
def apply(config)
|
9
|
+
unless config.has_key?('Volumes')
|
10
|
+
config['Volumes'] = {}
|
11
|
+
end
|
12
|
+
@value.each do |mount|
|
13
|
+
config['Volumes'][mount] = {}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
def validate!
|
17
|
+
raise ERROR("should be an array") unless @value.kind_of?(Array)
|
18
|
+
@value.each_with_index do |item,index|
|
19
|
+
raise ERROR("#{index}: '#{item}' should be a string") unless item.kind_of?(String)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'baha/container_options/cmd'
|
2
|
+
require 'baha/container_options/entrypoint'
|
3
|
+
require 'baha/container_options/env'
|
4
|
+
require 'baha/container_options/exposed_ports'
|
5
|
+
require 'baha/container_options/invalid_option_error'
|
6
|
+
require 'baha/container_options/option'
|
7
|
+
require 'baha/container_options/volumes'
|
8
|
+
module Baha
|
9
|
+
module ContainerOptions
|
10
|
+
def self.parse_options(options)
|
11
|
+
if options
|
12
|
+
Hash[options.map { |k,v| opt = self.parse_option(k,v)
|
13
|
+
[opt.key,opt] }]
|
14
|
+
else
|
15
|
+
{}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
def self.parse_option(key,value)
|
19
|
+
k = key.to_sym.downcase
|
20
|
+
option = case k
|
21
|
+
when :volumes
|
22
|
+
Volumes.new(value)
|
23
|
+
when :env
|
24
|
+
Env.new(value)
|
25
|
+
when :cmd
|
26
|
+
Cmd.new(value)
|
27
|
+
when :entrypoint
|
28
|
+
Entrypoint.new(value)
|
29
|
+
when :exposedports
|
30
|
+
ExposedPorts.new(value)
|
31
|
+
else
|
32
|
+
Option.new(key,value)
|
33
|
+
end
|
34
|
+
option.validate!
|
35
|
+
option
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/baha/image.rb
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'docker'
|
2
|
+
require 'baha/container_options'
|
3
|
+
require 'baha/log'
|
4
|
+
require 'set'
|
5
|
+
|
6
|
+
module Baha
|
7
|
+
class ImageNotFoundError < RuntimeError
|
8
|
+
attr_reader :image
|
9
|
+
def initialize(image)
|
10
|
+
super("Unable to locate image : #{image[:name]}:#{image[:tag]}")
|
11
|
+
@image = image
|
12
|
+
end
|
13
|
+
end
|
14
|
+
class Image
|
15
|
+
LOG = Baha::Log.for_name("Image")
|
16
|
+
class << self
|
17
|
+
# Parses an image name
|
18
|
+
def parse_name(image)
|
19
|
+
m = /(?:([a-z0-9\-._\/]+))(?::([a-zA-Z0-9\-._]+))?/.match(image)
|
20
|
+
unless m
|
21
|
+
raise ArgumentError.new("Unable to parse image name #{image}")
|
22
|
+
end
|
23
|
+
tag = m.captures[1]
|
24
|
+
tag ||= 'latest'
|
25
|
+
imagename = m.captures[0]
|
26
|
+
m2 = /(?:(.+)\/)?(.+)/.match(imagename)
|
27
|
+
ns = m2.captures[0]
|
28
|
+
name = m2.captures[1]
|
29
|
+
{
|
30
|
+
:ns => ns,
|
31
|
+
:name => name,
|
32
|
+
:tag => tag
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse_with_default(image,repository)
|
37
|
+
i = parse_name(image)
|
38
|
+
unless i[:ns]
|
39
|
+
i[:ns] = repository
|
40
|
+
end
|
41
|
+
i
|
42
|
+
end
|
43
|
+
|
44
|
+
def get_image!(image)
|
45
|
+
LOG.debug { "get_image!(#{image.inspect})" }
|
46
|
+
tag = image[:tag] || 'latest'
|
47
|
+
repo = "#{image[:ns]}/#{image[:name]}"
|
48
|
+
img = [
|
49
|
+
lambda { Docker::Image.get("#{image[:name]}:#{image[:tag]}") },
|
50
|
+
lambda { Docker::Image.create('fromImage'=> image[:name], 'tag' => tag) },
|
51
|
+
lambda { Docker::Image.create('fromImage' => repo, 'tag' => tag) }
|
52
|
+
].reduce(nil) do |result,block|
|
53
|
+
unless result
|
54
|
+
begin
|
55
|
+
result = block.call
|
56
|
+
result = Docker::Image.get(result.id)
|
57
|
+
rescue
|
58
|
+
result = nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
result
|
62
|
+
end
|
63
|
+
raise Baha::ImageNotFoundError.new(image) unless img
|
64
|
+
img
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
attr_reader :parent, :image, :maintainer, :options, :pre_build, :bind, :command, :timeout, :workspace, :name, :tags
|
69
|
+
|
70
|
+
def initialize(config,image)
|
71
|
+
@parent = Baha::Image.parse_with_default(image['parent'] || config.defaults[:parent], config.defaults[:repository])
|
72
|
+
@image = Baha::Image.parse_with_default(image['name'], config.defaults[:repository])
|
73
|
+
@image[:tag] = image['tag'] if image.has_key?('tag')
|
74
|
+
@maintainer = image['maintainer'] || config.defaults[:maintainer]
|
75
|
+
@options = Baha::ContainerOptions::parse_options(image['config'])
|
76
|
+
@pre_build = image['pre_build']
|
77
|
+
@bind = image['bind'] || config.defaults[:bind]
|
78
|
+
@command = image['command'] || config.defaults[:command]
|
79
|
+
@timeout = image['timeout'] || config.defaults[:timeout]
|
80
|
+
@workspace = config.workspace + (image['workspace'] || @image[:name])
|
81
|
+
@name = @image[:name]
|
82
|
+
@tags = Set.new [
|
83
|
+
"#{@image[:name]}:#{@image[:tag]}",
|
84
|
+
"#{@image[:name]}:latest"
|
85
|
+
]
|
86
|
+
if @image[:ns]
|
87
|
+
@tags << "#{@image[:ns]}/#{@image[:name]}:#{@image[:tag]}"
|
88
|
+
@tags << "#{@image[:ns]}/#{@image[:name]}:latest"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def env
|
93
|
+
{
|
94
|
+
:parent => @parent,
|
95
|
+
:maintainer => @maintainer,
|
96
|
+
:bind => @bind,
|
97
|
+
:name => @image[:name],
|
98
|
+
:tag => @image[:tag],
|
99
|
+
:workspace => @workspace.expand_path.to_s
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
def parent_id
|
104
|
+
parent = Baha::Image.get_image!(@parent)
|
105
|
+
parent.id
|
106
|
+
end
|
107
|
+
|
108
|
+
def commit_config
|
109
|
+
@options.values.reduce({}) do |memo,option|
|
110
|
+
option.apply(memo)
|
111
|
+
memo
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Checks if the image needs updating
|
116
|
+
# 1. If it's parent image has changed
|
117
|
+
# 2. If the desired tag is not found
|
118
|
+
# Will raise Baha::ImageNotFoundError if the parent image can not be found
|
119
|
+
def needs_update?
|
120
|
+
LOG.debug { "needs_update?(#{@image.inspect})" }
|
121
|
+
parent = Baha::Image.get_image!(@parent)
|
122
|
+
LOG.debug { "got parent: #{parent.inspect}" }
|
123
|
+
begin
|
124
|
+
image = Baha::Image.get_image!(@image)
|
125
|
+
LOG.debug { "got image: #{image.inspect}" }
|
126
|
+
parent_id = image.info['Parent']
|
127
|
+
this_tags = image.history[0]["Tags"]
|
128
|
+
local_tag = "#{@image[:name]}:#{@image[:tag]}"
|
129
|
+
remote_tag = "#{@image[:ns]}/#{local_tag}"
|
130
|
+
LOG.debug { "current parent id = #{parent_id}" }
|
131
|
+
LOG.debug { "current image tags = #{parent_id}" }
|
132
|
+
LOG.debug { "current parent id = #{parent_id}" }
|
133
|
+
parent_id != parent.id or not ( this_tags.include?(local_tag) or this_tags.include?(remote_tag) )
|
134
|
+
rescue
|
135
|
+
true
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def inspect
|
140
|
+
<<-eos.gsub(/\n?\s{2,}/,'')
|
141
|
+
#{self.class.name}<
|
142
|
+
@parent=#{@parent.inspect},
|
143
|
+
@image=#{@image.inspect},
|
144
|
+
@maintainer=#{@maintainer},
|
145
|
+
@options=#{@options.inspect},
|
146
|
+
@pre_build=#{@pre_build.inspect},
|
147
|
+
@bind=#{@bind},
|
148
|
+
@command=#{@command.inspect},
|
149
|
+
@timeout=#{@timeout}
|
150
|
+
>
|
151
|
+
eos
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
data/lib/baha/log.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
class Baha::Log
|
2
|
+
require 'logger'
|
3
|
+
LEVELS = {
|
4
|
+
:debug => Logger::DEBUG,
|
5
|
+
:info => Logger::INFO,
|
6
|
+
:warn => Logger::WARN,
|
7
|
+
:error => Logger::ERROR,
|
8
|
+
:fatal => Logger::FATAL
|
9
|
+
}
|
10
|
+
class Formatter
|
11
|
+
Format = "%s [%5s] %s -- %s\n"
|
12
|
+
|
13
|
+
def call(severity, time, progname, msg)
|
14
|
+
Format % [time.strftime('%Y-%m-%d %H:%M:%S.%L'), severity, progname, msg2str(msg)]
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def msg2str(msg)
|
20
|
+
case msg
|
21
|
+
when ::String
|
22
|
+
msg
|
23
|
+
when ::Exception
|
24
|
+
"#{ msg.message } (#{ msg.class })\n\t" <<
|
25
|
+
(msg.backtrace || []).join("\n\t")
|
26
|
+
else
|
27
|
+
msg.inspect
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
class << self
|
32
|
+
attr_reader :level, :logfile, :io
|
33
|
+
def logfile=(io)
|
34
|
+
@io = io
|
35
|
+
@logfile = Logger.new(io)
|
36
|
+
@logfile.formatter = Baha::Log::Formatter.new()
|
37
|
+
self.level = :error
|
38
|
+
end
|
39
|
+
def level=(level)
|
40
|
+
key = case level
|
41
|
+
when String
|
42
|
+
@level = level.downcase.to_sym
|
43
|
+
when Symbol
|
44
|
+
@level = level.downcase
|
45
|
+
else
|
46
|
+
raise ArgumentError.new("level must be a string or symbol")
|
47
|
+
end
|
48
|
+
raise ArgumentError.new("level must be in #{LEVELS.keys}") unless LEVELS.has_key?(key)
|
49
|
+
@level = key
|
50
|
+
self.logfile.sev_threshold = LEVELS[@level]
|
51
|
+
end
|
52
|
+
def close!
|
53
|
+
if @logfile
|
54
|
+
@logfile.close
|
55
|
+
@logfile = nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
def for_name(progname)
|
59
|
+
Baha::Log.new(progname)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
attr_reader :progname
|
64
|
+
|
65
|
+
def initialize(progname)
|
66
|
+
@progname = progname
|
67
|
+
end
|
68
|
+
|
69
|
+
LEVELS.keys.each do |level|
|
70
|
+
define_method(level) do |*message, &block|
|
71
|
+
if Baha::Log.logfile
|
72
|
+
if block
|
73
|
+
Baha::Log.logfile.send(level, @progname, &block)
|
74
|
+
else
|
75
|
+
Baha::Log.logfile.send(level, @progname) { message[0] }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'baha/pre_build'
|
2
|
+
require 'open3'
|
3
|
+
|
4
|
+
class Baha::PreBuild::Module::Command
|
5
|
+
LOG = Baha::Log.for_name("Module::Command")
|
6
|
+
|
7
|
+
def self.execute(mod)
|
8
|
+
LOG.debug("execute(#{mod.args.inspect})")
|
9
|
+
command = mod.args['command']
|
10
|
+
creates = mod.args['creates']
|
11
|
+
onlyif = mod.args['only_if']
|
12
|
+
|
13
|
+
cwd = mod.image.workspace.expand_path
|
14
|
+
|
15
|
+
if creates
|
16
|
+
filepath = cwd + creates
|
17
|
+
LOG.debug { "Checking if file exists #{filepath}"}
|
18
|
+
if filepath.exist?
|
19
|
+
LOG.info("#{creates} exists - skipping command")
|
20
|
+
return
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
if onlyif
|
25
|
+
exit_status = 0
|
26
|
+
LOG.info { "Running test [onlyif] #{onlyif.inspect}" }
|
27
|
+
Open3.popen2e(onlyif,:chdir=>cwd.to_s) do |stdin, oe, wait_thr|
|
28
|
+
oe.each do |line|
|
29
|
+
LOG.debug { "++ " + line }
|
30
|
+
end
|
31
|
+
exit_status = wait_thr.value # Process::Status object returned.
|
32
|
+
end
|
33
|
+
unless exit_status.success?
|
34
|
+
LOG.debug { "onlyif did not exist successfully - skipping command" }
|
35
|
+
return
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
LOG.info { "Running command #{command.inspect}" }
|
40
|
+
Open3.popen2e(command,:chdir=>cwd.to_s) do |stdin, oe, wait_thr|
|
41
|
+
oe.each do |line|
|
42
|
+
LOG.debug { "++ " + line.chomp }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
Baha::PreBuild::Module.register(:command) do |mod|
|
50
|
+
Baha::PreBuild::Module::Command.execute(mod)
|
51
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'baha/pre_build'
|
2
|
+
require 'open-uri'
|
3
|
+
|
4
|
+
class Baha::PreBuild::Module::Download
|
5
|
+
LOG = Baha::Log.for_name("Module::Download")
|
6
|
+
|
7
|
+
def self.execute(mod)
|
8
|
+
LOG.debug("execute(#{mod.args.inspect})")
|
9
|
+
|
10
|
+
filename = mod.image.workspace + mod.args['file']
|
11
|
+
url = mod.args['download']
|
12
|
+
overwrite = mod.args['overwrite'] || false
|
13
|
+
if Pathname.new(filename).exist? and not overwrite
|
14
|
+
LOG.info("#{filename} already exists")
|
15
|
+
else
|
16
|
+
LOG.info("Download #{url} -> #{filename}")
|
17
|
+
File.open(filename, "w") do |saved_file|
|
18
|
+
open(url, "rb") do |read_file|
|
19
|
+
saved_file.write(read_file.read)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
Baha::PreBuild::Module.register(:download) do |mod|
|
27
|
+
Baha::PreBuild::Module::Download.execute(mod)
|
28
|
+
end
|