bigrig 0.0.0 → 0.0.7
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 +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
|