baha 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|