bigrig 0.0.0 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rspec +3 -0
- data/.rubocop.yml +20 -0
- data/.travis.yml +3 -0
- data/CHANGELOG.md +19 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +84 -0
- data/LICENSE +28 -0
- data/README.md +191 -0
- data/Rakefile +10 -0
- data/bigrig.gemspec +29 -0
- data/bin/bigrig +113 -0
- data/lib/bigrig/actions/destroy_action.rb +21 -0
- data/lib/bigrig/actions/dev_action.rb +19 -0
- data/lib/bigrig/actions/log_action.rb +45 -0
- data/lib/bigrig/actions/run_action.rb +14 -0
- data/lib/bigrig/actions/ship_action.rb +51 -0
- data/lib/bigrig/actions.rb +5 -0
- data/lib/bigrig/dependency_graph.rb +21 -0
- data/lib/bigrig/descriptor.rb +43 -0
- data/lib/bigrig/docker_adapter.rb +129 -0
- data/lib/bigrig/models/application.rb +22 -0
- data/lib/bigrig/models/base_model.rb +7 -0
- data/lib/bigrig/models/container.rb +40 -0
- data/lib/bigrig/models.rb +3 -0
- data/lib/bigrig/output_parser.rb +52 -0
- data/lib/bigrig/runner.rb +94 -0
- data/lib/bigrig/version.rb +3 -0
- data/lib/bigrig.rb +8 -0
- data/spec/bigrig/actions/destroy_action_spec.rb +55 -0
- data/spec/bigrig/actions/dev_action_spec.rb +17 -0
- data/spec/bigrig/actions/log_action_spec.rb +33 -0
- data/spec/bigrig/actions/run_action_spec.rb +270 -0
- data/spec/bigrig/actions/ship_action_spec.rb +82 -0
- data/spec/bigrig/dependency_graph_spec.rb +19 -0
- data/spec/bigrig/descriptor_spec.rb +56 -0
- data/spec/bigrig/docker_adapter_spec.rb +409 -0
- data/spec/bigrig/models/application_spec.rb +32 -0
- data/spec/bigrig/models/container_spec.rb +115 -0
- data/spec/bigrig/output_parser_spec.rb +71 -0
- data/spec/bigrig_spec.rb +249 -0
- data/spec/data/addscontainer.json +24 -0
- data/spec/data/build/Dockerfile +2 -0
- data/spec/data/build/test +0 -0
- data/spec/data/dev.json +26 -0
- data/spec/data/duplicate.json +13 -0
- data/spec/data/env.json +12 -0
- data/spec/data/hosts_ip.json +12 -0
- data/spec/data/hosts_name.json +11 -0
- data/spec/data/links.json +13 -0
- data/spec/data/log.json +8 -0
- data/spec/data/multiple.json +13 -0
- data/spec/data/path.json +5 -0
- data/spec/data/ports.json +9 -0
- data/spec/data/profiles.json +24 -0
- data/spec/data/ship.json +5 -0
- data/spec/data/single.json +8 -0
- data/spec/data/tagandpath.json +24 -0
- data/spec/data/tiny-image.tar +0 -0
- data/spec/data/volumes.json +13 -0
- data/spec/spec_helper.rb +104 -0
- data/spec/support/bigrig_vcr +8 -0
- data/spec/support/vcr.rb +15 -0
- data/spec/vcr/Bigrig_DestroyAction/_perform/given_json_with_a_single_container/and_the_container_has_exited/should_remove_the_container.yml +392 -0
- data/spec/vcr/Bigrig_DestroyAction/_perform/given_json_with_a_single_container/and_the_container_is_running/kills_and_removes_the_container.yml +418 -0
- data/spec/vcr/Bigrig_DockerAdapter/_build/builds_the_given_directory.yml +63 -0
- data/spec/vcr/Bigrig_DockerAdapter/_build/passes_build_input_to_a_block.yml +35 -0
- data/spec/vcr/Bigrig_DockerAdapter/_container_exists_/when_the_container_does_not_exist/is_false.yml +115 -0
- data/spec/vcr/Bigrig_DockerAdapter/_container_exists_/when_the_container_exists/is_true.yml +82 -0
- data/spec/vcr/Bigrig_DockerAdapter/_image_id_by_tag/when_the_image_does_not_exist/raise_a_ImageNotFoundError.yml +172 -0
- data/spec/vcr/Bigrig_DockerAdapter/_image_id_by_tag/when_the_image_exists/returns_the_image_id.yml +148 -0
- data/spec/vcr/Bigrig_DockerAdapter/_kill/given_the_container_does_not_exist/should_raise_an_error.yml +115 -0
- data/spec/vcr/Bigrig_DockerAdapter/_kill/given_the_container_is_running/should_kill_the_container.yml +200 -0
- data/spec/vcr/Bigrig_DockerAdapter/_logs/streams_logs_to_a_block.yml +163 -0
- data/spec/vcr/Bigrig_DockerAdapter/_pull/given_a_block_to_capture_output/should_capture_output.yml +64 -0
- data/spec/vcr/Bigrig_DockerAdapter/_pull/given_the_repo_does_not_exist/raises_a_RepoNotFoundError.yml +30 -0
- data/spec/vcr/Bigrig_DockerAdapter/_pull/given_the_repo_exists/returns_the_image_id.yml +92 -0
- data/spec/vcr/Bigrig_DockerAdapter/_push/given_credentials/will_pass_login_and_password.yml +254 -0
- data/spec/vcr/Bigrig_DockerAdapter/_push/should_push_the_image.yml +482 -0
- data/spec/vcr/Bigrig_DockerAdapter/_remove_container/when_the_container_does_not_exist/raises_a_ContainerNotFoundError.yml +227 -0
- data/spec/vcr/Bigrig_DockerAdapter/_remove_container/when_the_container_exists/should_remove_the_container.yml +222 -0
- data/spec/vcr/Bigrig_DockerAdapter/_remove_container/when_the_container_is_running/raises_a_ContainerRunningError.yml +80 -0
- data/spec/vcr/Bigrig_DockerAdapter/_remove_image/when_the_image_doesnt_exist/raises_an_error.yml +115 -0
- data/spec/vcr/Bigrig_DockerAdapter/_remove_image/when_the_image_exists/removes_the_image.yml +199 -0
- data/spec/vcr/Bigrig_DockerAdapter/_run/given_an_image_id_that_exists/and_a_name/starts_the_container_with_the_right_name.yml +204 -0
- data/spec/vcr/Bigrig_DockerAdapter/_run/given_an_image_id_that_exists/and_a_name_and_env_variables/starts_the_container_with_env_set.yml +204 -0
- data/spec/vcr/Bigrig_DockerAdapter/_run/given_an_image_id_that_exists/and_a_name_and_ports/starts_the_container_with_ports_exposed.yml +230 -0
- data/spec/vcr/Bigrig_DockerAdapter/_running_/when_the_container_does_not_exist/returns_false.yml +115 -0
- data/spec/vcr/Bigrig_DockerAdapter/_running_/when_the_container_is_not_running/returns_false.yml +82 -0
- data/spec/vcr/Bigrig_DockerAdapter/_running_/when_the_container_is_running/returns_true.yml +103 -0
- data/spec/vcr/Bigrig_DockerAdapter/_tag/should_tag_the_image.yml +290 -0
- data/spec/vcr/Bigrig_LogAction/_perform/follows_the_log.yml +163 -0
- data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_a_path/builds_the_image_before_starting_it.yml +310 -0
- data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_hosts_by_ip/should_pass_hosts_to_container.yml +430 -0
- data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_hosts_by_name/should_lookup_ips_for_hosts_with_a_hostname.yml +430 -0
- data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_links/should_pass_links_to_the_right_container.yml +805 -0
- data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_multiple_containers/launches_both_containers_in_parallel.yml +731 -0
- data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_multiple_containers/spins_up_multiple_containers.yml +805 -0
- data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_one_container/should_spin_up_a_single_container.yml +404 -0
- data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_one_container/when_a_dead_container_exists/should_remove_existing_containers.yml +329 -0
- data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_volumes_from/should_pass_volumes_from_to_the_right_container.yml +805 -0
- data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_volumes_from/starts_the_dependant_container_last.yml +731 -0
- data/spec/vcr/Bigrig_RunAction/_perform/when_activating_profiles_that_do_not_exist/ignores_the_missing_profile.yml +409 -0
- data/spec/vcr/Bigrig_RunAction/_perform/with_a_file_with_active_profiles/uses_the_overridden_image.yml +432 -0
- data/spec/vcr/bigrig/destroy/spec/data/single_json/kills_the_container.yml +166 -0
- data/spec/vcr/bigrig/dev/spec/data/dev_json/activates_the_dev_profile.yml +37 -0
- data/spec/vcr/bigrig/dev/spec/data/dev_json/destroys_containers_on_exit.yml +227 -0
- data/spec/vcr/bigrig/dev/spec/data/dev_json/starts_the_containers.yml +107 -0
- data/spec/vcr/bigrig/logs/spec/data/log_json/tails_the_logs.yml +163 -0
- data/spec/vcr/bigrig/run/spec/data/profiles_json_-p_qa/leaves_existing_env_values_alone.yml +110 -0
- data/spec/vcr/bigrig/run/spec/data/profiles_json_-p_qa/overrides_the_env.yml +110 -0
- data/spec/vcr/bigrig/run/spec/data/profiles_json_-p_qa/overrides_the_tag.yml +103 -0
- data/spec/vcr/bigrig/run/spec/data/single_json/sends_the_name_of_the_container_to_stdout.yml +75 -0
- data/spec/vcr/bigrig/run/spec/data/single_json/starts_the_container.yml +101 -0
- data/spec/vcr/bigrig/ship/spec/data/ship_json/with_a_version/-c/cleans_the_image_when_its_done.yml +212 -0
- data/spec/vcr/bigrig/ship/spec/data/ship_json/with_a_version/builds_and_pushes_the_image.yml +128 -0
- data/spec/vcr/bigrig_bin_bigrig_destroy_spec/data/single_json_kills_the_container.yml +179 -0
- data/spec/vcr/bigrig_bin_bigrig_dev_spec/data/dev_json_activates_the_dev_profile.yml +1068 -0
- data/spec/vcr/bigrig_bin_bigrig_dev_spec/data/dev_json_destroys_containers_on_exit.yml +1070 -0
- data/spec/vcr/bigrig_bin_bigrig_dev_spec/data/dev_json_starts_the_containers.yml +1068 -0
- data/spec/vcr/bigrig_bin_bigrig_dev_spec/data/dev_json_tails_the_logs.yml +1075 -0
- data/spec/vcr/bigrig_bin_bigrig_logs_spec/data/log_json_tails_the_logs.yml +69 -0
- data/spec/vcr/bigrig_bin_bigrig_run_spec/data/profiles_json_-p_qa_leaves_existing_env_values_alone.yml +306 -0
- data/spec/vcr/bigrig_bin_bigrig_run_spec/data/profiles_json_-p_qa_overrides_the_env.yml +306 -0
- data/spec/vcr/bigrig_bin_bigrig_run_spec/data/profiles_json_-p_qa_overrides_the_tag.yml +306 -0
- data/spec/vcr/bigrig_bin_bigrig_run_spec/data/single_json_sends_the_name_of_the_container_to_stdout.yml +306 -0
- data/spec/vcr/bigrig_bin_bigrig_run_spec/data/single_json_starts_the_container.yml +306 -0
- data/spec/vcr/bigrig_bin_bigrig_ship_spec/data/ship_json_with_a_version_-c_cleans_the_image_when_its_done.yml +335 -0
- data/spec/vcr/bigrig_bin_bigrig_ship_spec/data/ship_json_with_a_version_builds_and_pushes_the_image.yml +285 -0
- data/test/dev/shipper.json +24 -0
- data/test/logs/bigrig.json +6 -0
- data/test/logs/container1/Dockerfile +4 -0
- data/test/logs/container1/run.sh +7 -0
- data/test/logs/container2/Dockerfile +4 -0
- data/test/logs/container2/run.sh +7 -0
- data/test/ship/bigrig-1.2.3.json +1 -0
- data/test/ship/bigrig.json +5 -0
- data/test/ship/registry.json +11 -0
- data/test/ship/ship/Dockerfile +4 -0
- data/test/ship/ship/run.sh +6 -0
- data/test/volumes_from/exports_volumes/Dockerfile +8 -0
- data/test/volumes_from/exports_volumes/index.html +8 -0
- data/test/volumes_from/exports_volumes/run.sh +7 -0
- data/test/volumes_from/shipper.json +14 -0
- metadata +278 -8
@@ -0,0 +1,21 @@
|
|
1
|
+
module Bigrig
|
2
|
+
class DependencyGraph
|
3
|
+
def initialize(containers)
|
4
|
+
@containers = containers
|
5
|
+
@map = containers.each_with_object({}) { |e, o| o[e.name] = e }
|
6
|
+
end
|
7
|
+
|
8
|
+
def resolve
|
9
|
+
resolved = []
|
10
|
+
@containers.each { |c| resolve_deps(c, resolved) unless resolved.include? c.name }
|
11
|
+
resolved.map { |n| @map[n] }
|
12
|
+
end
|
13
|
+
|
14
|
+
def resolve_deps(container, resolved)
|
15
|
+
container.dependencies.each do |dep_name|
|
16
|
+
resolve_deps @map[dep_name], resolved unless resolved.include? dep_name
|
17
|
+
end
|
18
|
+
resolved << container.name
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Bigrig
|
4
|
+
class Descriptor
|
5
|
+
attr_reader :as_json
|
6
|
+
|
7
|
+
def initialize(container_map, profiles)
|
8
|
+
profiles.each do |_name, containers|
|
9
|
+
containers.each do |container, overrides|
|
10
|
+
apply_overrides container_map, container, overrides
|
11
|
+
end
|
12
|
+
end
|
13
|
+
@as_json = container_map
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.read(file, active_profiles = [])
|
17
|
+
json = JSON.parse File.read file
|
18
|
+
Descriptor.new json['containers'], profiles_for(json, active_profiles)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def apply_overrides(container_map, name, overrides)
|
24
|
+
overrides.each do |key, value|
|
25
|
+
container = container_map[name] ||= {}
|
26
|
+
if value.is_a? Hash
|
27
|
+
(container[key] ||= {}).merge! value
|
28
|
+
else
|
29
|
+
container[key] = value
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.profiles_for(json, active_profiles)
|
35
|
+
if json['profiles']
|
36
|
+
profiles = active_profiles.map do |profile|
|
37
|
+
[profile, json['profiles'][profile]]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
Hash[*(profiles || []).flatten].select { |_n, v| !v.nil? }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'docker'
|
2
|
+
|
3
|
+
Excon.defaults[:ssl_verify_peer] = false
|
4
|
+
Excon.defaults[:read_timeout] = 3600
|
5
|
+
|
6
|
+
module Bigrig
|
7
|
+
class ContainerNotFoundError < StandardError; end
|
8
|
+
class ImageNotFoundError < StandardError; end
|
9
|
+
class RepoNotFoundError < StandardError; end
|
10
|
+
class ContainerRunningError < StandardError; end
|
11
|
+
|
12
|
+
class DockerAdapter
|
13
|
+
class << self
|
14
|
+
def build(path, &block)
|
15
|
+
Docker::Image.build_from_dir(path, {}, connection, &block).id
|
16
|
+
end
|
17
|
+
|
18
|
+
def container_exists?(name)
|
19
|
+
!Docker::Container.get(name).nil?
|
20
|
+
rescue Docker::Error::NotFoundError
|
21
|
+
false
|
22
|
+
end
|
23
|
+
|
24
|
+
def hosts(arr)
|
25
|
+
(arr || []).map do |line|
|
26
|
+
parts = line.split ':'
|
27
|
+
"#{Resolv.getaddress parts.first}:#{parts[1]}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def image_id_by_tag(tag)
|
32
|
+
Docker::Image.get(tag).id
|
33
|
+
rescue Docker::Error::NotFoundError
|
34
|
+
raise ImageNotFoundError
|
35
|
+
end
|
36
|
+
|
37
|
+
def kill(name)
|
38
|
+
Docker::Container.get(name).kill
|
39
|
+
rescue Docker::Error::NotFoundError
|
40
|
+
raise ContainerNotFoundError
|
41
|
+
end
|
42
|
+
|
43
|
+
def logs(name, &block)
|
44
|
+
container = Docker::Container.get name, {}, connection
|
45
|
+
container.streaming_logs follow: true, stdout: true, stderr: true, &block
|
46
|
+
end
|
47
|
+
|
48
|
+
def pull(tag, &block)
|
49
|
+
Docker::Image.get(Docker::Image.create('fromImage' => tag, &block).id).id
|
50
|
+
rescue Docker::Error::ArgumentError => e
|
51
|
+
e.to_s =~ /"id"=>nil/ && raise(RepoNotFoundError)
|
52
|
+
raise
|
53
|
+
end
|
54
|
+
|
55
|
+
def push(tag, credentials = nil, &block)
|
56
|
+
puts "Pushing #{tag}"
|
57
|
+
Docker::Image.get(tag).push credentials, {}, &block
|
58
|
+
end
|
59
|
+
|
60
|
+
def tag(id, tag)
|
61
|
+
i = tag.rindex ':'
|
62
|
+
repo, version = [tag[0...i], tag[i + 1..-1]]
|
63
|
+
Docker::Image.get(id).tag 'repo' => repo, 'tag' => version, 'force' => true
|
64
|
+
end
|
65
|
+
|
66
|
+
def remove_container(name)
|
67
|
+
fail ContainerRunningError, 'You cannot remove a running container' if running?(name)
|
68
|
+
Docker::Container.get(name).delete
|
69
|
+
true
|
70
|
+
rescue Docker::Error::NotFoundError
|
71
|
+
raise ContainerNotFoundError
|
72
|
+
end
|
73
|
+
|
74
|
+
def remove_image(tag)
|
75
|
+
Docker::Image.get(tag).remove 'force' => true
|
76
|
+
true
|
77
|
+
rescue Docker::Error::NotFoundError
|
78
|
+
raise ImageNotFoundError
|
79
|
+
end
|
80
|
+
|
81
|
+
def run(args)
|
82
|
+
container = create_container args
|
83
|
+
container.start(
|
84
|
+
'Links' => args[:links],
|
85
|
+
'ExtraHosts' => hosts(args[:hosts]),
|
86
|
+
'PortBindings' => port_bindings(args[:ports]),
|
87
|
+
'VolumesFrom' => args[:volumes_from]
|
88
|
+
)
|
89
|
+
container.id
|
90
|
+
end
|
91
|
+
|
92
|
+
def running?(name)
|
93
|
+
Docker::Container.get(name).info['State']['Running']
|
94
|
+
rescue Docker::Error::NotFoundError
|
95
|
+
false
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def connection
|
101
|
+
options = Docker.env_options.merge read_timeout: 31_536_000
|
102
|
+
Docker::Connection.new Docker.url, options
|
103
|
+
end
|
104
|
+
|
105
|
+
def create_container(args)
|
106
|
+
Docker::Container.create(
|
107
|
+
'Env' => (args[:env] || {}).map { |n, v| "#{n}=#{v}" },
|
108
|
+
'Image' => args[:image_id],
|
109
|
+
'name' => args[:name],
|
110
|
+
'ExposedPorts' => exposed_ports(args[:ports])
|
111
|
+
)
|
112
|
+
end
|
113
|
+
|
114
|
+
def exposed_ports(ports)
|
115
|
+
container_ports = (ports || []).map do |port|
|
116
|
+
port.include?(':') && port.split(':')[1] || port
|
117
|
+
end
|
118
|
+
Hash[*container_ports.map { |p| ["#{p}/tcp", {}] }.flatten]
|
119
|
+
end
|
120
|
+
|
121
|
+
def port_bindings(ports)
|
122
|
+
(ports || []).each_with_object({}) do |port, hash|
|
123
|
+
host_port, container_port = port.include?(':') && port.split(':') || [nil, port]
|
124
|
+
hash["#{container_port}/tcp"] = [host_port && { 'HostPort' => host_port } || {}]
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Bigrig
|
4
|
+
class Application < BaseModel
|
5
|
+
attr_accessor :name, :containers
|
6
|
+
|
7
|
+
def initialize(args = {})
|
8
|
+
@containers = []
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def from_json(json)
|
14
|
+
containers = json.map do |name, value|
|
15
|
+
Container.from_json name, value
|
16
|
+
end
|
17
|
+
|
18
|
+
Application.new containers: containers
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Bigrig
|
2
|
+
class Container < BaseModel
|
3
|
+
attr_accessor :env, :name, :path, :ports, :tag, :volumes_from, :links, :hosts, :repo
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def from_json(name, json)
|
7
|
+
opts = [:env, :path, :ports, :tag, :repo].each_with_object(name: name) do |e, o|
|
8
|
+
o[e] = json.send :[], e.to_s
|
9
|
+
end
|
10
|
+
opts[:volumes_from] = as_array json['volumes_from']
|
11
|
+
opts[:links] = as_array json['links']
|
12
|
+
opts[:hosts] = as_array json['hosts']
|
13
|
+
|
14
|
+
Container.new opts
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def as_array(value)
|
20
|
+
value && [value].flatten.compact
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(*opts)
|
25
|
+
super
|
26
|
+
@env ||= {}
|
27
|
+
@ports ||= []
|
28
|
+
@volumes_from ||= []
|
29
|
+
@links ||= []
|
30
|
+
@hosts ||= []
|
31
|
+
|
32
|
+
# Yes rubocop, I know this is a very stupid thing to do
|
33
|
+
@env = Hash[*@env.map { |k, v| [k, eval("\"#{v}\"")] }.flatten] # rubocop:disable Lint/Eval
|
34
|
+
end
|
35
|
+
|
36
|
+
def dependencies
|
37
|
+
volumes_from + links.map { |l| l.split(':').first }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Bigrig
|
2
|
+
class OutputParser
|
3
|
+
def self.parser_proc
|
4
|
+
proc { |chunk| print OutputParser.new.parse chunk }
|
5
|
+
end
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@last_line = ''
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse(input)
|
12
|
+
json = JSON.parse input
|
13
|
+
if json['errorDetail']
|
14
|
+
"#{json['errorDetail']['message'].red}\n"
|
15
|
+
elsif json['stream'] || json['id'].nil?
|
16
|
+
json['stream'] || "#{json['status']}\n"
|
17
|
+
else
|
18
|
+
parse_progress json
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def last_line_length
|
25
|
+
@last_line.strip.length
|
26
|
+
end
|
27
|
+
|
28
|
+
def padding(line)
|
29
|
+
if last_line_length > line.length
|
30
|
+
padding = ' ' * (last_line_length - line.length)
|
31
|
+
end
|
32
|
+
@last_line = line
|
33
|
+
padding || ''
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse_progress(json)
|
37
|
+
line = should_print_cr?(json['id']) ? "\n" : ''
|
38
|
+
line << "#{json['id']}: #{json['status']} #{json['progress']}"
|
39
|
+
line << padding(line) << "\r"
|
40
|
+
end
|
41
|
+
|
42
|
+
def should_print_cr?(id)
|
43
|
+
@current_id ||= id
|
44
|
+
if id != @current_id
|
45
|
+
@current_id = id
|
46
|
+
true
|
47
|
+
else
|
48
|
+
false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Bigrig
|
2
|
+
class Runner
|
3
|
+
def initialize(application)
|
4
|
+
@application = application
|
5
|
+
end
|
6
|
+
|
7
|
+
def run
|
8
|
+
steps.each { |s| perform_step s }
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def build(path)
|
14
|
+
puts "Building #{path}"
|
15
|
+
DockerAdapter.build path, &OutputParser.parser_proc
|
16
|
+
end
|
17
|
+
|
18
|
+
def containers
|
19
|
+
@application.containers
|
20
|
+
end
|
21
|
+
|
22
|
+
def depends_on_containers(container, containers)
|
23
|
+
(containers.map(&:name) & container.dependencies).any?
|
24
|
+
end
|
25
|
+
|
26
|
+
def destroy_existing(containers)
|
27
|
+
containers.each do |container|
|
28
|
+
begin
|
29
|
+
DockerAdapter.remove_container container[:name]
|
30
|
+
rescue Bigrig::ContainerNotFoundError # rubocop:disable Lint/HandleExceptions
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def docker_opts(step)
|
36
|
+
step.map do |container|
|
37
|
+
docker_opts_for(container).merge image_id: image_id(container)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def docker_opts_for(container)
|
42
|
+
{ env: container.env,
|
43
|
+
name: container.name,
|
44
|
+
ports: container.ports,
|
45
|
+
volumes_from: container.volumes_from,
|
46
|
+
links: container.links,
|
47
|
+
hosts: container.hosts }
|
48
|
+
end
|
49
|
+
|
50
|
+
def image_id(container)
|
51
|
+
if container.path
|
52
|
+
build container.path
|
53
|
+
else
|
54
|
+
tag = "#{container.repo}:#{container.tag}"
|
55
|
+
begin
|
56
|
+
DockerAdapter.image_id_by_tag tag, &OutputParser.parser_proc
|
57
|
+
rescue ImageNotFoundError
|
58
|
+
DockerAdapter.pull tag, &OutputParser.parser_proc
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def ordered_containers
|
64
|
+
DependencyGraph.new(containers).resolve
|
65
|
+
end
|
66
|
+
|
67
|
+
def perform_step(step)
|
68
|
+
containers = docker_opts step
|
69
|
+
destroy_existing containers
|
70
|
+
|
71
|
+
threads = containers.map do |container|
|
72
|
+
Thread.new do
|
73
|
+
puts "Starting #{container[:name]}"
|
74
|
+
puts DockerAdapter.run(container)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
threads.each(&:join)
|
79
|
+
end
|
80
|
+
|
81
|
+
def steps
|
82
|
+
current_step = []
|
83
|
+
steps = [current_step]
|
84
|
+
ordered_containers.each do |container|
|
85
|
+
if depends_on_containers(container, current_step)
|
86
|
+
current_step = []
|
87
|
+
steps << current_step
|
88
|
+
end
|
89
|
+
current_step << container
|
90
|
+
end
|
91
|
+
steps
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/bigrig.rb
CHANGED
@@ -0,0 +1,55 @@
|
|
1
|
+
module Bigrig
|
2
|
+
describe DestroyAction do
|
3
|
+
describe '#perform' do
|
4
|
+
subject { described_class.new(active_containers.as_json).perform }
|
5
|
+
let(:active_containers) { Descriptor.read test_file(file), profiles }
|
6
|
+
let(:profiles) { [] }
|
7
|
+
|
8
|
+
context 'given json with a single container' do
|
9
|
+
let(:container) do
|
10
|
+
image = Docker::Image.create 'fromImage' => 'hawknewton/show-env'
|
11
|
+
Docker::Container.create 'Image' => image.id, 'name' => 'single-test'
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:file) { 'single.json' }
|
15
|
+
|
16
|
+
let(:exists?) do
|
17
|
+
begin
|
18
|
+
Docker::Container.get(container.id)
|
19
|
+
true
|
20
|
+
rescue Docker::Error::NotFoundError
|
21
|
+
false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
after do
|
26
|
+
begin
|
27
|
+
container.kill.delete
|
28
|
+
rescue Docker::Error::NotFoundError # rubocop:disable Lint/HandleExceptions
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'and the container is running' do
|
33
|
+
before { container.start }
|
34
|
+
|
35
|
+
it 'kills and removes the container', :vcr do
|
36
|
+
subject
|
37
|
+
expect(exists?).to be false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'and the container has exited' do
|
42
|
+
before do
|
43
|
+
container.start
|
44
|
+
container.kill
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should remove the container', :vcr do
|
48
|
+
subject
|
49
|
+
expect(exists?).to be false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Bigrig
|
2
|
+
describe DevAction do
|
3
|
+
describe '#perform' do
|
4
|
+
subject { described_class.new descriptor.as_json }
|
5
|
+
let(:descriptor) { Descriptor.new test_file(file), profiles }
|
6
|
+
|
7
|
+
context 'when no profile is provided' do
|
8
|
+
let(:profiles) { [] }
|
9
|
+
let(:file) { 'dev.json' }
|
10
|
+
|
11
|
+
it 'should initialize dev action' do
|
12
|
+
expect { subject }.to_not raise_error
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Bigrig
|
2
|
+
describe LogAction do
|
3
|
+
describe '#perform' do
|
4
|
+
subject { runner.perform }
|
5
|
+
let(:active_containers) { Descriptor.read test_file(file), active_profiles }
|
6
|
+
let(:active_profiles) { [] }
|
7
|
+
let(:runner) { described_class.new(active_containers.as_json) }
|
8
|
+
let(:file) { 'log.json' }
|
9
|
+
let(:image) { Docker::Image.create 'fromImage' => 'hawknewton/log-test:0.0.1' }
|
10
|
+
let(:container) { Docker::Container.get 'log-test' }
|
11
|
+
|
12
|
+
before do
|
13
|
+
Docker::Container.create('Image' => image.id, 'name' => 'log-test').start
|
14
|
+
end
|
15
|
+
|
16
|
+
after do
|
17
|
+
container.kill.delete
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'follows the log', :vcr do
|
21
|
+
allow(Docker::Container).to receive(:get).
|
22
|
+
with('log-test', {}, anything).and_return container
|
23
|
+
allow(container).to receive(:streaming_logs).
|
24
|
+
and_yield(:stdout, 'stdout message').
|
25
|
+
and_yield(:stderr, 'stderr message')
|
26
|
+
expect($stdout).to receive(:puts).with "\e[0;32;49mlog-test\e[0m: stdout message"
|
27
|
+
expect(runner).to receive(:print).
|
28
|
+
with "\e[0;32;49mlog-test\e[0m: \e[0;91;49mstderr message\e[0m"
|
29
|
+
subject
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|