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.
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