bigrig 0.0.0 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +20 -0
  5. data/.travis.yml +3 -0
  6. data/CHANGELOG.md +19 -0
  7. data/Gemfile +2 -0
  8. data/Gemfile.lock +84 -0
  9. data/LICENSE +28 -0
  10. data/README.md +191 -0
  11. data/Rakefile +10 -0
  12. data/bigrig.gemspec +29 -0
  13. data/bin/bigrig +113 -0
  14. data/lib/bigrig/actions/destroy_action.rb +21 -0
  15. data/lib/bigrig/actions/dev_action.rb +19 -0
  16. data/lib/bigrig/actions/log_action.rb +45 -0
  17. data/lib/bigrig/actions/run_action.rb +14 -0
  18. data/lib/bigrig/actions/ship_action.rb +51 -0
  19. data/lib/bigrig/actions.rb +5 -0
  20. data/lib/bigrig/dependency_graph.rb +21 -0
  21. data/lib/bigrig/descriptor.rb +43 -0
  22. data/lib/bigrig/docker_adapter.rb +129 -0
  23. data/lib/bigrig/models/application.rb +22 -0
  24. data/lib/bigrig/models/base_model.rb +7 -0
  25. data/lib/bigrig/models/container.rb +40 -0
  26. data/lib/bigrig/models.rb +3 -0
  27. data/lib/bigrig/output_parser.rb +52 -0
  28. data/lib/bigrig/runner.rb +94 -0
  29. data/lib/bigrig/version.rb +3 -0
  30. data/lib/bigrig.rb +8 -0
  31. data/spec/bigrig/actions/destroy_action_spec.rb +55 -0
  32. data/spec/bigrig/actions/dev_action_spec.rb +17 -0
  33. data/spec/bigrig/actions/log_action_spec.rb +33 -0
  34. data/spec/bigrig/actions/run_action_spec.rb +270 -0
  35. data/spec/bigrig/actions/ship_action_spec.rb +82 -0
  36. data/spec/bigrig/dependency_graph_spec.rb +19 -0
  37. data/spec/bigrig/descriptor_spec.rb +56 -0
  38. data/spec/bigrig/docker_adapter_spec.rb +409 -0
  39. data/spec/bigrig/models/application_spec.rb +32 -0
  40. data/spec/bigrig/models/container_spec.rb +115 -0
  41. data/spec/bigrig/output_parser_spec.rb +71 -0
  42. data/spec/bigrig_spec.rb +249 -0
  43. data/spec/data/addscontainer.json +24 -0
  44. data/spec/data/build/Dockerfile +2 -0
  45. data/spec/data/build/test +0 -0
  46. data/spec/data/dev.json +26 -0
  47. data/spec/data/duplicate.json +13 -0
  48. data/spec/data/env.json +12 -0
  49. data/spec/data/hosts_ip.json +12 -0
  50. data/spec/data/hosts_name.json +11 -0
  51. data/spec/data/links.json +13 -0
  52. data/spec/data/log.json +8 -0
  53. data/spec/data/multiple.json +13 -0
  54. data/spec/data/path.json +5 -0
  55. data/spec/data/ports.json +9 -0
  56. data/spec/data/profiles.json +24 -0
  57. data/spec/data/ship.json +5 -0
  58. data/spec/data/single.json +8 -0
  59. data/spec/data/tagandpath.json +24 -0
  60. data/spec/data/tiny-image.tar +0 -0
  61. data/spec/data/volumes.json +13 -0
  62. data/spec/spec_helper.rb +104 -0
  63. data/spec/support/bigrig_vcr +8 -0
  64. data/spec/support/vcr.rb +15 -0
  65. data/spec/vcr/Bigrig_DestroyAction/_perform/given_json_with_a_single_container/and_the_container_has_exited/should_remove_the_container.yml +392 -0
  66. 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
  67. data/spec/vcr/Bigrig_DockerAdapter/_build/builds_the_given_directory.yml +63 -0
  68. data/spec/vcr/Bigrig_DockerAdapter/_build/passes_build_input_to_a_block.yml +35 -0
  69. data/spec/vcr/Bigrig_DockerAdapter/_container_exists_/when_the_container_does_not_exist/is_false.yml +115 -0
  70. data/spec/vcr/Bigrig_DockerAdapter/_container_exists_/when_the_container_exists/is_true.yml +82 -0
  71. data/spec/vcr/Bigrig_DockerAdapter/_image_id_by_tag/when_the_image_does_not_exist/raise_a_ImageNotFoundError.yml +172 -0
  72. data/spec/vcr/Bigrig_DockerAdapter/_image_id_by_tag/when_the_image_exists/returns_the_image_id.yml +148 -0
  73. data/spec/vcr/Bigrig_DockerAdapter/_kill/given_the_container_does_not_exist/should_raise_an_error.yml +115 -0
  74. data/spec/vcr/Bigrig_DockerAdapter/_kill/given_the_container_is_running/should_kill_the_container.yml +200 -0
  75. data/spec/vcr/Bigrig_DockerAdapter/_logs/streams_logs_to_a_block.yml +163 -0
  76. data/spec/vcr/Bigrig_DockerAdapter/_pull/given_a_block_to_capture_output/should_capture_output.yml +64 -0
  77. data/spec/vcr/Bigrig_DockerAdapter/_pull/given_the_repo_does_not_exist/raises_a_RepoNotFoundError.yml +30 -0
  78. data/spec/vcr/Bigrig_DockerAdapter/_pull/given_the_repo_exists/returns_the_image_id.yml +92 -0
  79. data/spec/vcr/Bigrig_DockerAdapter/_push/given_credentials/will_pass_login_and_password.yml +254 -0
  80. data/spec/vcr/Bigrig_DockerAdapter/_push/should_push_the_image.yml +482 -0
  81. data/spec/vcr/Bigrig_DockerAdapter/_remove_container/when_the_container_does_not_exist/raises_a_ContainerNotFoundError.yml +227 -0
  82. data/spec/vcr/Bigrig_DockerAdapter/_remove_container/when_the_container_exists/should_remove_the_container.yml +222 -0
  83. data/spec/vcr/Bigrig_DockerAdapter/_remove_container/when_the_container_is_running/raises_a_ContainerRunningError.yml +80 -0
  84. data/spec/vcr/Bigrig_DockerAdapter/_remove_image/when_the_image_doesnt_exist/raises_an_error.yml +115 -0
  85. data/spec/vcr/Bigrig_DockerAdapter/_remove_image/when_the_image_exists/removes_the_image.yml +199 -0
  86. 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
  87. 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
  88. 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
  89. data/spec/vcr/Bigrig_DockerAdapter/_running_/when_the_container_does_not_exist/returns_false.yml +115 -0
  90. data/spec/vcr/Bigrig_DockerAdapter/_running_/when_the_container_is_not_running/returns_false.yml +82 -0
  91. data/spec/vcr/Bigrig_DockerAdapter/_running_/when_the_container_is_running/returns_true.yml +103 -0
  92. data/spec/vcr/Bigrig_DockerAdapter/_tag/should_tag_the_image.yml +290 -0
  93. data/spec/vcr/Bigrig_LogAction/_perform/follows_the_log.yml +163 -0
  94. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_a_path/builds_the_image_before_starting_it.yml +310 -0
  95. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_hosts_by_ip/should_pass_hosts_to_container.yml +430 -0
  96. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_hosts_by_name/should_lookup_ips_for_hosts_with_a_hostname.yml +430 -0
  97. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_links/should_pass_links_to_the_right_container.yml +805 -0
  98. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_multiple_containers/launches_both_containers_in_parallel.yml +731 -0
  99. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_multiple_containers/spins_up_multiple_containers.yml +805 -0
  100. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_one_container/should_spin_up_a_single_container.yml +404 -0
  101. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_one_container/when_a_dead_container_exists/should_remove_existing_containers.yml +329 -0
  102. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_volumes_from/should_pass_volumes_from_to_the_right_container.yml +805 -0
  103. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_volumes_from/starts_the_dependant_container_last.yml +731 -0
  104. data/spec/vcr/Bigrig_RunAction/_perform/when_activating_profiles_that_do_not_exist/ignores_the_missing_profile.yml +409 -0
  105. data/spec/vcr/Bigrig_RunAction/_perform/with_a_file_with_active_profiles/uses_the_overridden_image.yml +432 -0
  106. data/spec/vcr/bigrig/destroy/spec/data/single_json/kills_the_container.yml +166 -0
  107. data/spec/vcr/bigrig/dev/spec/data/dev_json/activates_the_dev_profile.yml +37 -0
  108. data/spec/vcr/bigrig/dev/spec/data/dev_json/destroys_containers_on_exit.yml +227 -0
  109. data/spec/vcr/bigrig/dev/spec/data/dev_json/starts_the_containers.yml +107 -0
  110. data/spec/vcr/bigrig/logs/spec/data/log_json/tails_the_logs.yml +163 -0
  111. data/spec/vcr/bigrig/run/spec/data/profiles_json_-p_qa/leaves_existing_env_values_alone.yml +110 -0
  112. data/spec/vcr/bigrig/run/spec/data/profiles_json_-p_qa/overrides_the_env.yml +110 -0
  113. data/spec/vcr/bigrig/run/spec/data/profiles_json_-p_qa/overrides_the_tag.yml +103 -0
  114. data/spec/vcr/bigrig/run/spec/data/single_json/sends_the_name_of_the_container_to_stdout.yml +75 -0
  115. data/spec/vcr/bigrig/run/spec/data/single_json/starts_the_container.yml +101 -0
  116. data/spec/vcr/bigrig/ship/spec/data/ship_json/with_a_version/-c/cleans_the_image_when_its_done.yml +212 -0
  117. data/spec/vcr/bigrig/ship/spec/data/ship_json/with_a_version/builds_and_pushes_the_image.yml +128 -0
  118. data/spec/vcr/bigrig_bin_bigrig_destroy_spec/data/single_json_kills_the_container.yml +179 -0
  119. data/spec/vcr/bigrig_bin_bigrig_dev_spec/data/dev_json_activates_the_dev_profile.yml +1068 -0
  120. data/spec/vcr/bigrig_bin_bigrig_dev_spec/data/dev_json_destroys_containers_on_exit.yml +1070 -0
  121. data/spec/vcr/bigrig_bin_bigrig_dev_spec/data/dev_json_starts_the_containers.yml +1068 -0
  122. data/spec/vcr/bigrig_bin_bigrig_dev_spec/data/dev_json_tails_the_logs.yml +1075 -0
  123. data/spec/vcr/bigrig_bin_bigrig_logs_spec/data/log_json_tails_the_logs.yml +69 -0
  124. data/spec/vcr/bigrig_bin_bigrig_run_spec/data/profiles_json_-p_qa_leaves_existing_env_values_alone.yml +306 -0
  125. data/spec/vcr/bigrig_bin_bigrig_run_spec/data/profiles_json_-p_qa_overrides_the_env.yml +306 -0
  126. data/spec/vcr/bigrig_bin_bigrig_run_spec/data/profiles_json_-p_qa_overrides_the_tag.yml +306 -0
  127. data/spec/vcr/bigrig_bin_bigrig_run_spec/data/single_json_sends_the_name_of_the_container_to_stdout.yml +306 -0
  128. data/spec/vcr/bigrig_bin_bigrig_run_spec/data/single_json_starts_the_container.yml +306 -0
  129. data/spec/vcr/bigrig_bin_bigrig_ship_spec/data/ship_json_with_a_version_-c_cleans_the_image_when_its_done.yml +335 -0
  130. data/spec/vcr/bigrig_bin_bigrig_ship_spec/data/ship_json_with_a_version_builds_and_pushes_the_image.yml +285 -0
  131. data/test/dev/shipper.json +24 -0
  132. data/test/logs/bigrig.json +6 -0
  133. data/test/logs/container1/Dockerfile +4 -0
  134. data/test/logs/container1/run.sh +7 -0
  135. data/test/logs/container2/Dockerfile +4 -0
  136. data/test/logs/container2/run.sh +7 -0
  137. data/test/ship/bigrig-1.2.3.json +1 -0
  138. data/test/ship/bigrig.json +5 -0
  139. data/test/ship/registry.json +11 -0
  140. data/test/ship/ship/Dockerfile +4 -0
  141. data/test/ship/ship/run.sh +6 -0
  142. data/test/volumes_from/exports_volumes/Dockerfile +8 -0
  143. data/test/volumes_from/exports_volumes/index.html +8 -0
  144. data/test/volumes_from/exports_volumes/run.sh +7 -0
  145. data/test/volumes_from/shipper.json +14 -0
  146. 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,7 @@
1
+ module Bigrig
2
+ class BaseModel
3
+ def initialize(args = {})
4
+ args.each { |k, v| send "#{k}=", v }
5
+ end
6
+ end
7
+ 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,3 @@
1
+ require 'bigrig/models/base_model'
2
+ require 'bigrig/models/application'
3
+ require 'bigrig/models/container'
@@ -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
@@ -0,0 +1,3 @@
1
+ module Bigrig
2
+ VERSION = '0.0.7'
3
+ end
data/lib/bigrig.rb CHANGED
@@ -1,2 +1,10 @@
1
+ require 'bigrig/actions'
2
+ require 'bigrig/descriptor'
3
+ require 'bigrig/dependency_graph'
4
+ require 'bigrig/docker_adapter'
5
+ require 'bigrig/models'
6
+ require 'bigrig/output_parser'
7
+ require 'bigrig/runner'
1
8
  require 'bigrig/version'
9
+
2
10
  module Bigrig; end
@@ -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