construi 0.39.0 → 0.40.0
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/bin/construi +1 -0
- data/construi.gemspec +3 -1
- data/lib/construi.rb +0 -2
- data/lib/construi/config.rb +82 -37
- data/lib/construi/console.rb +27 -0
- data/lib/construi/container.rb +51 -24
- data/lib/construi/image.rb +14 -8
- data/lib/construi/runner.rb +3 -2
- data/lib/construi/target.rb +26 -9
- data/lib/construi/version.rb +1 -1
- data/site/index.md +27 -0
- data/spec/lib/construi/config_spec.rb +110 -2
- data/spec/lib/construi/container_spec.rb +13 -22
- data/spec/lib/construi/image_spec.rb +7 -7
- data/spec/lib/construi/runner_spec.rb +1 -1
- metadata +6 -5
data/bin/construi
CHANGED
data/construi.gemspec
CHANGED
|
@@ -8,8 +8,10 @@ Gem::Specification.new do |spec|
|
|
|
8
8
|
spec.version = Construi::VERSION
|
|
9
9
|
spec.authors = ['Levi Stephen']
|
|
10
10
|
spec.email = ['levi.stephen@gmail.com']
|
|
11
|
+
|
|
11
12
|
spec.summary = 'Build tool using Docker to specify build environment'
|
|
12
13
|
spec.description = 'Build tool using Docker to specify build environment'
|
|
14
|
+
|
|
13
15
|
spec.homepage = 'https://github.com/lstephen/construi'
|
|
14
16
|
spec.license = 'MIT'
|
|
15
17
|
|
|
@@ -20,7 +22,7 @@ Gem::Specification.new do |spec|
|
|
|
20
22
|
|
|
21
23
|
spec.required_ruby_version = '>= 1.9'
|
|
22
24
|
|
|
23
|
-
spec.add_dependency 'docker-api', '=1.
|
|
25
|
+
spec.add_dependency 'docker-api', '=1.22.0'
|
|
24
26
|
spec.add_dependency 'colorize', '=0.7.7'
|
|
25
27
|
|
|
26
28
|
spec.add_development_dependency 'bundler', '~> 1.9'
|
data/lib/construi.rb
CHANGED
data/lib/construi/config.rb
CHANGED
|
@@ -1,6 +1,25 @@
|
|
|
1
|
+
require 'yaml'
|
|
1
2
|
|
|
2
3
|
module Construi
|
|
3
4
|
module Config
|
|
5
|
+
module WrappedYaml
|
|
6
|
+
def parent
|
|
7
|
+
nil
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def key?(key)
|
|
11
|
+
yaml.is_a?(Hash) && yaml.key?(key.to_s)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def get(key, default = nil)
|
|
15
|
+
key?(key) ? yaml[key.to_s] : default
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def with_parent(or_else = nil)
|
|
19
|
+
parent ? yield(parent) : or_else
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
4
23
|
module Image
|
|
5
24
|
def image
|
|
6
25
|
image_configured :image
|
|
@@ -11,7 +30,7 @@ module Construi
|
|
|
11
30
|
end
|
|
12
31
|
|
|
13
32
|
def privileged?
|
|
14
|
-
key?(:privileged) ?
|
|
33
|
+
key?(:privileged) ? get(:privileged) : with_parent(false, &:privileged?)
|
|
15
34
|
end
|
|
16
35
|
|
|
17
36
|
def image_configured?
|
|
@@ -19,7 +38,7 @@ module Construi
|
|
|
19
38
|
end
|
|
20
39
|
|
|
21
40
|
def image_configured(what)
|
|
22
|
-
image_configured? ?
|
|
41
|
+
image_configured? ? get(what) : with_parent(&what)
|
|
23
42
|
end
|
|
24
43
|
end
|
|
25
44
|
|
|
@@ -43,55 +62,84 @@ module Construi
|
|
|
43
62
|
end
|
|
44
63
|
end
|
|
45
64
|
|
|
46
|
-
def files_configured?
|
|
47
|
-
key? :files
|
|
48
|
-
end
|
|
49
|
-
|
|
50
65
|
def files
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
with_parent([], &:files).concat fs
|
|
66
|
+
with_parent([], &:files).concat get(:files, []).map { |s| File.parse s }
|
|
54
67
|
end
|
|
55
68
|
end
|
|
56
69
|
|
|
57
|
-
module
|
|
58
|
-
|
|
59
|
-
|
|
70
|
+
module EnvironmentVariables
|
|
71
|
+
def env_configured?
|
|
72
|
+
key? :environment
|
|
73
|
+
end
|
|
60
74
|
|
|
61
|
-
def
|
|
62
|
-
|
|
75
|
+
def env_hash
|
|
76
|
+
parent = with_parent({}, &:env_hash)
|
|
77
|
+
|
|
78
|
+
child = get(:environment, {}).each_with_object({}) do |v, h|
|
|
79
|
+
key, value = v.split '='
|
|
80
|
+
|
|
81
|
+
value = ENV[key] if value.nil? || value.empty?
|
|
82
|
+
|
|
83
|
+
h[key] = value
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
parent.merge child
|
|
63
87
|
end
|
|
64
88
|
|
|
65
|
-
def
|
|
66
|
-
|
|
89
|
+
def env
|
|
90
|
+
env_hash.each_with_object([]) do |(k, v), a|
|
|
91
|
+
a << "#{k}=#{v}" unless v.nil? || v.empty?
|
|
92
|
+
end
|
|
67
93
|
end
|
|
94
|
+
end
|
|
68
95
|
|
|
69
|
-
|
|
70
|
-
|
|
96
|
+
module Options
|
|
97
|
+
def options
|
|
98
|
+
{ env: env, privileged: privileged? }
|
|
71
99
|
end
|
|
72
100
|
end
|
|
73
101
|
|
|
74
|
-
|
|
75
|
-
|
|
102
|
+
module Links
|
|
103
|
+
class Link
|
|
104
|
+
include WrappedYaml
|
|
105
|
+
include Image
|
|
106
|
+
include Files
|
|
107
|
+
include EnvironmentVariables
|
|
108
|
+
include Options
|
|
76
109
|
|
|
77
|
-
|
|
110
|
+
attr_reader :yaml
|
|
78
111
|
|
|
79
|
-
|
|
80
|
-
|
|
112
|
+
def initialize(name, yaml)
|
|
113
|
+
@yaml = yaml
|
|
114
|
+
end
|
|
81
115
|
end
|
|
82
116
|
|
|
83
|
-
def
|
|
84
|
-
|
|
117
|
+
def links
|
|
118
|
+
parent = with_parent({}, &:links)
|
|
85
119
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
120
|
+
child = get(:links, {}).each_with_object({}) do |(k, v), ls|
|
|
121
|
+
ls[k] = Link.new k, v
|
|
122
|
+
end
|
|
89
123
|
|
|
90
|
-
|
|
124
|
+
parent.merge child
|
|
125
|
+
end
|
|
126
|
+
end
|
|
91
127
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
128
|
+
module BuildEnvironment
|
|
129
|
+
include WrappedYaml
|
|
130
|
+
include Image
|
|
131
|
+
include Files
|
|
132
|
+
include EnvironmentVariables
|
|
133
|
+
include Links
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
class Global
|
|
137
|
+
include BuildEnvironment
|
|
138
|
+
|
|
139
|
+
attr_reader :yaml
|
|
140
|
+
|
|
141
|
+
def initialize(yaml)
|
|
142
|
+
@yaml = yaml
|
|
95
143
|
end
|
|
96
144
|
|
|
97
145
|
def target(target)
|
|
@@ -104,7 +152,8 @@ module Construi
|
|
|
104
152
|
end
|
|
105
153
|
|
|
106
154
|
class Target
|
|
107
|
-
include
|
|
155
|
+
include BuildEnvironment
|
|
156
|
+
include Options
|
|
108
157
|
|
|
109
158
|
attr_reader :yaml, :parent
|
|
110
159
|
|
|
@@ -116,10 +165,6 @@ module Construi
|
|
|
116
165
|
def commands
|
|
117
166
|
Array(@yaml.is_a?(Hash) ? @yaml['run'] : @yaml)
|
|
118
167
|
end
|
|
119
|
-
|
|
120
|
-
def options
|
|
121
|
-
{ env: parent.env, privileged: parent.privileged? }
|
|
122
|
-
end
|
|
123
168
|
end
|
|
124
169
|
|
|
125
170
|
def self.load(content)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
module Construi
|
|
3
|
+
module Console
|
|
4
|
+
$stdout.sync = true
|
|
5
|
+
|
|
6
|
+
def self.warn(msg)
|
|
7
|
+
puts msg.yellow
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.info(msg)
|
|
11
|
+
puts msg.green
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.progress(msg)
|
|
15
|
+
puts
|
|
16
|
+
info msg
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.output(from, msg)
|
|
20
|
+
msg.each_line do |m|
|
|
21
|
+
puts "#{from.rjust(13)} | ".blue << m
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
data/lib/construi/container.rb
CHANGED
|
@@ -1,28 +1,51 @@
|
|
|
1
1
|
require 'construi/image'
|
|
2
2
|
|
|
3
3
|
module Construi
|
|
4
|
-
|
|
5
4
|
class Container
|
|
6
5
|
private_class_method :new
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
attr_reader :name
|
|
8
|
+
|
|
9
|
+
def initialize(container, options = {})
|
|
9
10
|
@container = container
|
|
11
|
+
@name = options[:name] || @container.id
|
|
12
|
+
@log_lifecycle = options[:log_lifecycle] || false
|
|
10
13
|
end
|
|
11
14
|
|
|
12
15
|
def id
|
|
13
16
|
@container.id
|
|
14
17
|
end
|
|
15
18
|
|
|
19
|
+
def start
|
|
20
|
+
log_lifecycle "Starting container: '#{name}'..."
|
|
21
|
+
@container.start!
|
|
22
|
+
attach_stdout
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def stop
|
|
26
|
+
log_lifecycle "Stopping container: '#{name}'..."
|
|
27
|
+
@container.stop
|
|
28
|
+
end
|
|
29
|
+
|
|
16
30
|
def delete
|
|
31
|
+
stop
|
|
32
|
+
@container.kill
|
|
17
33
|
@container.delete force: true, v: true
|
|
34
|
+
log_lifecycle "Deleted container: '#{name}'"
|
|
18
35
|
end
|
|
19
36
|
|
|
20
37
|
def attach_stdout
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
38
|
+
Thread.new do
|
|
39
|
+
@container.attach(:stream => true, :logs => true) { |_, c| Console.output name, c }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def log_lifecycle?
|
|
44
|
+
@log_lifecycle
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def log_lifecycle(msg)
|
|
48
|
+
Console.progress msg if log_lifecycle?
|
|
26
49
|
end
|
|
27
50
|
|
|
28
51
|
def commit
|
|
@@ -30,13 +53,10 @@ module Construi
|
|
|
30
53
|
end
|
|
31
54
|
|
|
32
55
|
def run
|
|
33
|
-
|
|
34
|
-
attached = attach_stdout
|
|
56
|
+
start
|
|
35
57
|
status_code = @container.wait['StatusCode']
|
|
36
58
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
raise Error, "Cmd returned status code: #{status_code}" unless status_code == 0
|
|
59
|
+
raise RunError, "Cmd returned status code: #{status_code}" unless status_code == 0
|
|
40
60
|
|
|
41
61
|
commit
|
|
42
62
|
end
|
|
@@ -45,42 +65,49 @@ module Construi
|
|
|
45
65
|
other.is_a? Container and id == other.id
|
|
46
66
|
end
|
|
47
67
|
|
|
48
|
-
def self.create(image,
|
|
68
|
+
def self.create(image, options = {})
|
|
49
69
|
env = options[:env] || []
|
|
50
70
|
privileged = options[:privileged] || false
|
|
71
|
+
links = options[:links] || []
|
|
51
72
|
|
|
52
73
|
host_config = {
|
|
53
74
|
'Binds' => ["#{Dir.pwd}:/var/workspace"],
|
|
54
|
-
'Privileged' => privileged
|
|
75
|
+
'Privileged' => privileged,
|
|
76
|
+
'Links' => links
|
|
55
77
|
}
|
|
56
78
|
|
|
57
|
-
|
|
58
|
-
'Cmd' => cmd.split,
|
|
79
|
+
create_options = {
|
|
59
80
|
'Image' => image.id,
|
|
60
81
|
'Env' => env,
|
|
61
82
|
'Tty' => false,
|
|
62
83
|
'WorkingDir' => '/var/workspace',
|
|
63
|
-
'HostConfig' => host_config
|
|
84
|
+
'HostConfig' => host_config
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
create_options['Cmd'] = options[:cmd].split if options.key?(:cmd)
|
|
88
|
+
|
|
89
|
+
wrap Docker::Container.create(create_options), options
|
|
64
90
|
end
|
|
65
91
|
|
|
66
|
-
def self.wrap(container)
|
|
67
|
-
new container
|
|
92
|
+
def self.wrap(container, options = {})
|
|
93
|
+
new container, options
|
|
68
94
|
end
|
|
69
95
|
|
|
70
|
-
def self.use(image,
|
|
71
|
-
container = create image,
|
|
96
|
+
def self.use(image, options = {})
|
|
97
|
+
container = create image, options
|
|
72
98
|
yield container
|
|
73
99
|
ensure
|
|
74
100
|
container.delete unless container.nil?
|
|
75
101
|
end
|
|
76
102
|
|
|
77
|
-
def self.run(image,
|
|
78
|
-
use image,
|
|
103
|
+
def self.run(image, options = {})
|
|
104
|
+
use image, options, &:run
|
|
79
105
|
end
|
|
80
106
|
|
|
81
|
-
class
|
|
107
|
+
class RunError < StandardError
|
|
82
108
|
end
|
|
83
109
|
|
|
84
110
|
end
|
|
85
111
|
|
|
112
|
+
|
|
86
113
|
end
|
data/lib/construi/image.rb
CHANGED
|
@@ -59,8 +59,12 @@ module Construi
|
|
|
59
59
|
run chmod, options
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
def start(options = {})
|
|
63
|
+
Container.create(self, options).tap(&:start)
|
|
64
|
+
end
|
|
65
|
+
|
|
62
66
|
def run(cmd, options = {})
|
|
63
|
-
Container.run self, cmd
|
|
67
|
+
Container.run self, options.merge(cmd: cmd)
|
|
64
68
|
end
|
|
65
69
|
|
|
66
70
|
def ==(other)
|
|
@@ -77,8 +81,8 @@ module Construi
|
|
|
77
81
|
end
|
|
78
82
|
|
|
79
83
|
def self.create(image)
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
Console.progress "Creating image: '#{image}'..."
|
|
85
|
+
|
|
82
86
|
wrap Docker::Image.create('fromImage' => image) { |s|
|
|
83
87
|
status = JSON.parse(s)
|
|
84
88
|
|
|
@@ -86,17 +90,19 @@ module Construi
|
|
|
86
90
|
progress = status['progressDetail']
|
|
87
91
|
|
|
88
92
|
if progress.nil? || progress.empty?
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
msg = ''
|
|
94
|
+
msg << "#{id}: " unless id.nil?
|
|
95
|
+
msg << status['status']
|
|
96
|
+
Console.output image, msg
|
|
91
97
|
end
|
|
92
98
|
}
|
|
93
99
|
end
|
|
94
100
|
|
|
95
101
|
def self.build(build)
|
|
96
|
-
|
|
97
|
-
|
|
102
|
+
Console.progress "Building image: '#{build}'..."
|
|
103
|
+
|
|
98
104
|
wrap Docker::Image.build_from_dir(build, rm: 0) { |s|
|
|
99
|
-
|
|
105
|
+
Console.output build, JSON.parse(s)['stream']
|
|
100
106
|
}
|
|
101
107
|
end
|
|
102
108
|
|
data/lib/construi/runner.rb
CHANGED
|
@@ -9,6 +9,8 @@ require 'colorize'
|
|
|
9
9
|
require 'docker'
|
|
10
10
|
|
|
11
11
|
module Construi
|
|
12
|
+
DOCKER_TIMEOUT = 60
|
|
13
|
+
|
|
12
14
|
# Runs Construi
|
|
13
15
|
class Runner
|
|
14
16
|
def initialize(config)
|
|
@@ -25,8 +27,6 @@ module Construi
|
|
|
25
27
|
|
|
26
28
|
Docker.validate_version!
|
|
27
29
|
|
|
28
|
-
# Don't time out. We can't differentiate between a long running
|
|
29
|
-
# task and a time out.
|
|
30
30
|
Docker.options[:read_timeout] = nil
|
|
31
31
|
|
|
32
32
|
# Low chunk size as we wish to receive streaming output ASAP
|
|
@@ -43,4 +43,5 @@ module Construi
|
|
|
43
43
|
targets.map { |t| Target.new t, @config.target(t) } .each(&:run)
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
|
+
|
|
46
47
|
end
|
data/lib/construi/target.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
require 'construi/console'
|
|
2
2
|
|
|
3
3
|
module Construi
|
|
4
|
-
|
|
5
4
|
class Target
|
|
6
5
|
attr_reader :name, :config
|
|
7
6
|
|
|
@@ -15,20 +14,38 @@ module Construi
|
|
|
15
14
|
end
|
|
16
15
|
|
|
17
16
|
def run
|
|
18
|
-
|
|
17
|
+
Console.progress "Running #{name}..."
|
|
18
|
+
|
|
19
|
+
links = start_linked_images
|
|
20
|
+
|
|
21
|
+
begin
|
|
22
|
+
final_image = IntermediateImage.seed(create_initial_image).reduce(commands) do |image, command|
|
|
23
|
+
Console.progress " > #{command}"
|
|
24
|
+
|
|
25
|
+
link_option = links.each_with_object([]) do |l, o|
|
|
26
|
+
o << "#{l.id}:#{l.name}"
|
|
27
|
+
end
|
|
19
28
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
29
|
+
image.run command, @config.options.merge(links: link_option, name: name)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
final_image.delete
|
|
33
|
+
ensure
|
|
34
|
+
links.map(&:delete)
|
|
24
35
|
end
|
|
25
36
|
|
|
26
|
-
|
|
37
|
+
Console.progress "Build Successful."
|
|
27
38
|
end
|
|
28
39
|
|
|
29
|
-
def
|
|
40
|
+
def create_initial_image
|
|
30
41
|
return Image.from(@config)
|
|
31
42
|
end
|
|
43
|
+
|
|
44
|
+
def start_linked_images
|
|
45
|
+
@config.links.map do |(name, config)|
|
|
46
|
+
Image.from(config).start(config.options.merge name: name, log_lifecycle: true)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
32
49
|
end
|
|
33
50
|
|
|
34
51
|
end
|
data/lib/construi/version.rb
CHANGED
data/site/index.md
CHANGED
|
@@ -104,6 +104,7 @@ Declares environment variables that will be passed through or set in the build e
|
|
|
104
104
|
If no value is provided then the value from the host environment will be used.
|
|
105
105
|
In this example `NEXUS_SERVER_URL` will be set as provided, while `NEXUS_USERNAME` and
|
|
106
106
|
`NEXUS_PASSWORD` will be retrieved from the host.
|
|
107
|
+
Can be used on a per target basis.
|
|
107
108
|
|
|
108
109
|
```
|
|
109
110
|
image: maven:3-jdk-7
|
|
@@ -135,6 +136,32 @@ targets:
|
|
|
135
136
|
run: scripts/construi/deploy.sh
|
|
136
137
|
```
|
|
137
138
|
|
|
139
|
+
### Links
|
|
140
|
+
|
|
141
|
+
Allows specifying containers that will be linked to the build container during the build
|
|
142
|
+
process.
|
|
143
|
+
This is useful, for example, in having a database running durings tests.
|
|
144
|
+
Linked containers allow setting of environment, files, and privileged as per the build
|
|
145
|
+
container.
|
|
146
|
+
Links can be specified on a global or per target level.
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
image: ruby:1.9
|
|
150
|
+
|
|
151
|
+
targets:
|
|
152
|
+
integration-tests:
|
|
153
|
+
environment:
|
|
154
|
+
- MYSQL_HOST=mysql
|
|
155
|
+
run:
|
|
156
|
+
- bundle install --path=vendor/bundle
|
|
157
|
+
- rake test:integration
|
|
158
|
+
links:
|
|
159
|
+
mysql:
|
|
160
|
+
image: mysql:5.5
|
|
161
|
+
environment:
|
|
162
|
+
- MYSQL_ALLOW_EMPTY_PASSWORD=yes
|
|
163
|
+
```
|
|
164
|
+
|
|
138
165
|
### Targets
|
|
139
166
|
|
|
140
167
|
Any number of targets can be specified.
|
|
@@ -65,9 +65,16 @@ RSpec.describe Construi::Config do
|
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
describe '#env' do
|
|
68
|
-
subject { config.env }
|
|
68
|
+
subject { config.target('build').env }
|
|
69
69
|
|
|
70
70
|
context 'when no environment section' do
|
|
71
|
+
let(:config_content) do
|
|
72
|
+
<<-YAML
|
|
73
|
+
targets:
|
|
74
|
+
build: cmd1
|
|
75
|
+
YAML
|
|
76
|
+
end
|
|
77
|
+
|
|
71
78
|
it { is_expected.to eq([]) }
|
|
72
79
|
end
|
|
73
80
|
|
|
@@ -77,6 +84,8 @@ RSpec.describe Construi::Config do
|
|
|
77
84
|
environment:
|
|
78
85
|
- VAR1=VALUE_1
|
|
79
86
|
- VAR2=VALUE_2
|
|
87
|
+
targets:
|
|
88
|
+
build: cmd1
|
|
80
89
|
YAML
|
|
81
90
|
end
|
|
82
91
|
|
|
@@ -94,10 +103,32 @@ RSpec.describe Construi::Config do
|
|
|
94
103
|
environment:
|
|
95
104
|
- VAR1
|
|
96
105
|
- VAR2
|
|
106
|
+
targets:
|
|
107
|
+
build: cmd1
|
|
97
108
|
YAML
|
|
98
109
|
end
|
|
99
110
|
|
|
100
|
-
it { is_expected.to contain_exactly
|
|
111
|
+
it { is_expected.to contain_exactly 'VAR1=VALUE_1', 'VAR2=VALUE_2' }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
context 'when target specific environment values' do
|
|
115
|
+
let(:config_content) do
|
|
116
|
+
<<-YAML
|
|
117
|
+
environment:
|
|
118
|
+
- VAR1=VALUE_1
|
|
119
|
+
- VAR2=VALUE_2
|
|
120
|
+
targets:
|
|
121
|
+
build:
|
|
122
|
+
environment:
|
|
123
|
+
- VAR1=VALUE_OVERIDEN
|
|
124
|
+
- VAR3=VALUE_3
|
|
125
|
+
YAML
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it do
|
|
129
|
+
is_expected.to contain_exactly(
|
|
130
|
+
'VAR1=VALUE_OVERIDEN', 'VAR2=VALUE_2', 'VAR3=VALUE_3')
|
|
131
|
+
end
|
|
101
132
|
end
|
|
102
133
|
end
|
|
103
134
|
|
|
@@ -195,11 +226,13 @@ RSpec.describe Construi::Config do
|
|
|
195
226
|
targets:
|
|
196
227
|
build:
|
|
197
228
|
image: build:image
|
|
229
|
+
privileged: true
|
|
198
230
|
run: cmd1
|
|
199
231
|
YAML
|
|
200
232
|
end
|
|
201
233
|
|
|
202
234
|
it { is_expected.to have_attributes(:image => 'build:image', :build => nil) }
|
|
235
|
+
it { expect(subject.options).to include(privileged: true) }
|
|
203
236
|
end
|
|
204
237
|
|
|
205
238
|
context 'when build for target and image for global' do
|
|
@@ -325,5 +358,80 @@ RSpec.describe Construi::Config do
|
|
|
325
358
|
it { is_expected.to eq(false) }
|
|
326
359
|
end
|
|
327
360
|
end
|
|
361
|
+
|
|
362
|
+
describe '#links' do
|
|
363
|
+
subject { config.target('build').links }
|
|
364
|
+
|
|
365
|
+
context 'no links' do
|
|
366
|
+
let(:config_content) do
|
|
367
|
+
<<-YAML
|
|
368
|
+
targets:
|
|
369
|
+
build: cmd1
|
|
370
|
+
YAML
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
it { is_expected.to eq({}) }
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
context 'link on target' do
|
|
377
|
+
let(:config_content) do
|
|
378
|
+
<<-YAML
|
|
379
|
+
targets:
|
|
380
|
+
build:
|
|
381
|
+
run: cmd1
|
|
382
|
+
links:
|
|
383
|
+
db:
|
|
384
|
+
image: mysql
|
|
385
|
+
environment:
|
|
386
|
+
- VAR1=VALUE1
|
|
387
|
+
YAML
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
it { is_expected.to include('db') }
|
|
391
|
+
it { expect(subject['db'].image).to eq('mysql') }
|
|
392
|
+
it { expect(subject['db'].env).to contain_exactly('VAR1=VALUE1') }
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
context 'link on global level' do
|
|
396
|
+
let(:config_content) do
|
|
397
|
+
<<-YAML
|
|
398
|
+
links:
|
|
399
|
+
sshd:
|
|
400
|
+
build: construi/my-sshd
|
|
401
|
+
privileged: true
|
|
402
|
+
targets:
|
|
403
|
+
build:
|
|
404
|
+
run: cmd1
|
|
405
|
+
YAML
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
it { is_expected.to include('sshd') }
|
|
409
|
+
it { expect(subject['sshd'].build).to eq('construi/my-sshd') }
|
|
410
|
+
it { expect(subject['sshd'].privileged?).to be_truthy }
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
context 'multiple images' do
|
|
414
|
+
let(:config_content) do
|
|
415
|
+
<<-YAML
|
|
416
|
+
links:
|
|
417
|
+
sshd:
|
|
418
|
+
build: construi/my-sshd
|
|
419
|
+
targets:
|
|
420
|
+
build:
|
|
421
|
+
run: cmd1
|
|
422
|
+
links:
|
|
423
|
+
db:
|
|
424
|
+
image: mysql
|
|
425
|
+
nginx:
|
|
426
|
+
image: mydockers/nginx
|
|
427
|
+
YAML
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
it { is_expected.to include('sshd', 'db', 'nginx') }
|
|
431
|
+
it { expect(subject['db'].image).to eq('mysql') }
|
|
432
|
+
it { expect(subject['nginx'].image).to eq('mydockers/nginx') }
|
|
433
|
+
it { expect(subject['sshd'].build).to eq('construi/my-sshd') }
|
|
434
|
+
end
|
|
435
|
+
end
|
|
328
436
|
end
|
|
329
437
|
|
|
@@ -30,22 +30,11 @@ RSpec.describe Construi::Container do
|
|
|
30
30
|
.and_yield('stream', 'msg2')
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
subject! { attach_stdout.call }
|
|
33
|
+
subject! { attach_stdout.call.join }
|
|
34
34
|
|
|
35
35
|
it { expect(docker_container).to have_received(:attach).with(:stream => true, :logs => true) }
|
|
36
|
-
it { expect($stdout.string).to include("msg1\
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
context 'when attach times out' do
|
|
40
|
-
before do
|
|
41
|
-
allow(docker_container)
|
|
42
|
-
.to receive(:attach)
|
|
43
|
-
.and_raise(Docker::Error::TimeoutError.new)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
subject! { attach_stdout.call }
|
|
47
|
-
|
|
48
|
-
it { expect($stdout.string).to include('Failed to attach to stdout'.yellow) }
|
|
36
|
+
it { expect($stdout.string).to include("msg1\n") }
|
|
37
|
+
it { expect($stdout.string).to include("msg2\n") }
|
|
49
38
|
end
|
|
50
39
|
end
|
|
51
40
|
|
|
@@ -69,8 +58,7 @@ RSpec.describe Construi::Container do
|
|
|
69
58
|
|
|
70
59
|
subject! { run.call }
|
|
71
60
|
|
|
72
|
-
it { expect(docker_container).to have_received(:start) }
|
|
73
|
-
it { expect(docker_container).to have_received(:attach).with(:stream => true, :logs => true) }
|
|
61
|
+
it { expect(docker_container).to have_received(:start!) }
|
|
74
62
|
it { expect(docker_container).to have_received(:wait) }
|
|
75
63
|
it { expect(docker_container).to have_received(:commit) }
|
|
76
64
|
it { is_expected.to eq(Construi::Image.wrap(image)) }
|
|
@@ -79,7 +67,7 @@ RSpec.describe Construi::Container do
|
|
|
79
67
|
context 'when command fails' do
|
|
80
68
|
let(:status_code) { 1 }
|
|
81
69
|
|
|
82
|
-
it { expect { run.call }.to raise_error Construi::Container::
|
|
70
|
+
it { expect { run.call }.to raise_error Construi::Container::RunError, /status code: 1/}
|
|
83
71
|
end
|
|
84
72
|
end
|
|
85
73
|
|
|
@@ -92,7 +80,7 @@ RSpec.describe Construi::Container do
|
|
|
92
80
|
before { allow(docker_container_class).to receive(:create).and_return docker_container }
|
|
93
81
|
before { allow(Dir).to receive(:pwd).and_return(pwd) }
|
|
94
82
|
|
|
95
|
-
subject! { Construi::Container::create
|
|
83
|
+
subject! { Construi::Container::create image, cmd: cmd, env: env, privileged: privileged }
|
|
96
84
|
|
|
97
85
|
it do
|
|
98
86
|
expect(docker_container_class).to have_received(:create).with( {
|
|
@@ -103,7 +91,8 @@ RSpec.describe Construi::Container do
|
|
|
103
91
|
'WorkingDir' => '/var/workspace',
|
|
104
92
|
'HostConfig' => {
|
|
105
93
|
'Binds' => ["#{pwd}:/var/workspace"],
|
|
106
|
-
'Privileged' => true
|
|
94
|
+
'Privileged' => true,
|
|
95
|
+
'Links' => []
|
|
107
96
|
}
|
|
108
97
|
} )
|
|
109
98
|
end
|
|
@@ -117,7 +106,7 @@ RSpec.describe Construi::Container do
|
|
|
117
106
|
before { allow(docker_container).to receive(:wait).and_return({'StatusCode' => 0}) }
|
|
118
107
|
before { allow(docker_container).to receive(:commit).and_return image }
|
|
119
108
|
|
|
120
|
-
subject! { Construi::Container.run
|
|
109
|
+
subject! { Construi::Container.run image, cmd: cmd }
|
|
121
110
|
|
|
122
111
|
it do
|
|
123
112
|
expect(docker_container_class).to have_received(:create).with(
|
|
@@ -127,9 +116,11 @@ RSpec.describe Construi::Container do
|
|
|
127
116
|
}))
|
|
128
117
|
end
|
|
129
118
|
it { is_expected.to eq(Construi::Image.wrap image) }
|
|
130
|
-
it { expect(docker_container).to have_received(:start) }
|
|
119
|
+
it { expect(docker_container).to have_received(:start!) }
|
|
131
120
|
it { expect(docker_container).to have_received(:commit) }
|
|
132
|
-
it { expect(docker_container).to have_received(:
|
|
121
|
+
it { expect(docker_container).to have_received(:stop) }
|
|
122
|
+
it { expect(docker_container).to have_received(:kill) }
|
|
123
|
+
it { expect(docker_container).to have_received(:delete).with(hash_including(v: true)) }
|
|
133
124
|
end
|
|
134
125
|
|
|
135
126
|
end
|
|
@@ -52,7 +52,7 @@ RSpec.describe Construi::Image do
|
|
|
52
52
|
|
|
53
53
|
subject! { image.run(cmd, env: env) }
|
|
54
54
|
|
|
55
|
-
it { expect(container).to have_received(:run).with(image, cmd, env: env) }
|
|
55
|
+
it { expect(container).to have_received(:run).with(image, cmd: cmd, env: env) }
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
describe '#insert_local' do
|
|
@@ -80,7 +80,7 @@ RSpec.describe Construi::Image do
|
|
|
80
80
|
.to have_received(:insert_local)
|
|
81
81
|
.with 'localPath' => host, 'outputPath' => container
|
|
82
82
|
end
|
|
83
|
-
it { expect(container_class).to have_received(:run).with(image, "ls -l #{container}"
|
|
83
|
+
it { expect(container_class).to have_received(:run).with(image, default_options.merge(cmd: "ls -l #{container}")) }
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
context 'with permissions' do
|
|
@@ -95,16 +95,16 @@ RSpec.describe Construi::Image do
|
|
|
95
95
|
it do
|
|
96
96
|
expect(container_class)
|
|
97
97
|
.to have_received(:run)
|
|
98
|
-
.with(image, "chmod -R #{permissions} #{container}"
|
|
98
|
+
.with(image, cmd: "chmod -R #{permissions} #{container}")
|
|
99
99
|
end
|
|
100
100
|
it { expect($stdout.string).to include(" > chmod -R #{permissions} #{container}") }
|
|
101
|
-
it { expect(container_class).to have_received(:run).with(image, "ls -l #{container}"
|
|
101
|
+
it { expect(container_class).to have_received(:run).with(image, default_options.merge(cmd: "ls -l #{container}")) }
|
|
102
102
|
end
|
|
103
103
|
|
|
104
104
|
end
|
|
105
105
|
|
|
106
106
|
describe '.from' do
|
|
107
|
-
let(:config) { instance_double(Construi::Config::
|
|
107
|
+
let(:config) { instance_double(Construi::Config::BuildEnvironment).as_null_object }
|
|
108
108
|
|
|
109
109
|
let(:image) { nil }
|
|
110
110
|
let(:build) { nil }
|
|
@@ -191,7 +191,7 @@ RSpec.describe Construi::Image do
|
|
|
191
191
|
end
|
|
192
192
|
|
|
193
193
|
it 'outputs build status messages' do
|
|
194
|
-
expect($stdout.string).to
|
|
194
|
+
expect($stdout.string).to match(/msg1\n.*msg2\n/)
|
|
195
195
|
end
|
|
196
196
|
end
|
|
197
197
|
|
|
@@ -220,7 +220,7 @@ RSpec.describe Construi::Image do
|
|
|
220
220
|
end
|
|
221
221
|
|
|
222
222
|
it 'outputs create status messages' do
|
|
223
|
-
expect($stdout.string).to
|
|
223
|
+
expect($stdout.string).to match(/id: msg1\n.*id: msg2\n.*msg4\n/)
|
|
224
224
|
end
|
|
225
225
|
end
|
|
226
226
|
end
|
|
@@ -32,7 +32,7 @@ RSpec.describe Construi::Runner do
|
|
|
32
32
|
subject! { runner.run(targets) }
|
|
33
33
|
|
|
34
34
|
it { expect(docker).to have_received(:validate_version!) }
|
|
35
|
-
it { expect(image).to have_received(:run).with('cmd1', env: [], privileged: false) }
|
|
35
|
+
it { expect(image).to have_received(:run).with('cmd1', env: [], privileged: false, links: [], name: 'target1') }
|
|
36
36
|
it { expect(image).to have_received(:delete) }
|
|
37
37
|
|
|
38
38
|
it { expect($stdout.string).to include('Running target1...'.green) }
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: construi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.40.0
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2015-07-
|
|
12
|
+
date: 2015-07-24 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: docker-api
|
|
@@ -18,7 +18,7 @@ dependencies:
|
|
|
18
18
|
requirements:
|
|
19
19
|
- - '='
|
|
20
20
|
- !ruby/object:Gem::Version
|
|
21
|
-
version: 1.
|
|
21
|
+
version: 1.22.0
|
|
22
22
|
type: :runtime
|
|
23
23
|
prerelease: false
|
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -26,7 +26,7 @@ dependencies:
|
|
|
26
26
|
requirements:
|
|
27
27
|
- - '='
|
|
28
28
|
- !ruby/object:Gem::Version
|
|
29
|
-
version: 1.
|
|
29
|
+
version: 1.22.0
|
|
30
30
|
- !ruby/object:Gem::Dependency
|
|
31
31
|
name: colorize
|
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -243,6 +243,7 @@ files:
|
|
|
243
243
|
- construi/site/run.sh
|
|
244
244
|
- lib/construi.rb
|
|
245
245
|
- lib/construi/config.rb
|
|
246
|
+
- lib/construi/console.rb
|
|
246
247
|
- lib/construi/container.rb
|
|
247
248
|
- lib/construi/image.rb
|
|
248
249
|
- lib/construi/runner.rb
|
|
@@ -281,7 +282,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
281
282
|
version: '0'
|
|
282
283
|
segments:
|
|
283
284
|
- 0
|
|
284
|
-
hash:
|
|
285
|
+
hash: 2884457678396287104
|
|
285
286
|
requirements: []
|
|
286
287
|
rubyforge_project:
|
|
287
288
|
rubygems_version: 1.8.23.2
|