decking 0.0.2

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fc3e349f7fa27cafe70b4a5e2fb901715bb52159
4
+ data.tar.gz: b01316554056df885d230d9c3632fec9e012469a
5
+ SHA512:
6
+ metadata.gz: a3ab5bf1ff01a0e210174b3cc574c8b28f45fc3e723e50ff9964815dd91dccccf4d534101d8677a05b1b74372304469214bf62c22cebafc160cdb7a7e57da8ec
7
+ data.tar.gz: ab3d2c5a3d2c0fea722f8481476e43a5e5a50201b15e6ca54fe0cc44dba991c7912afb25fa8a12a48f2211e6983a23f33070983798c550ffcc9545aa4749ff03
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ *.bundle
10
+ *.so
11
+ *.o
12
+ *.a
13
+ mkmf.log
14
+ /vendor/
15
+ /.idea/
16
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in decking.gemspec
4
+
5
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,89 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ decking (0.0.2)
5
+ docker-api (~> 1.22.2)
6
+ gli
7
+ hashie
8
+ io-console
9
+ log4r
10
+ ruby-progressbar
11
+
12
+ GEM
13
+ remote: https://rubygems.org/
14
+ specs:
15
+ celluloid (0.16.0)
16
+ timers (~> 4.0.0)
17
+ coderay (1.1.0)
18
+ diff-lcs (1.2.5)
19
+ docker-api (1.22.2)
20
+ excon (>= 0.38.0)
21
+ json
22
+ excon (0.45.4)
23
+ ffi (1.9.6)
24
+ formatador (0.2.5)
25
+ gli (2.13.1)
26
+ guard (2.9.0)
27
+ formatador (>= 0.2.4)
28
+ listen (~> 2.7)
29
+ lumberjack (~> 1.0)
30
+ pry (>= 0.9.12)
31
+ thor (>= 0.18.1)
32
+ guard-rspec (4.3.1)
33
+ guard (~> 2.1)
34
+ rspec (>= 2.14, < 4.0)
35
+ hashie (3.4.2)
36
+ hitimes (1.2.2)
37
+ io-console (0.4.2)
38
+ json (1.8.3)
39
+ listen (2.8.2)
40
+ celluloid (>= 0.15.2)
41
+ rb-fsevent (>= 0.9.3)
42
+ rb-inotify (>= 0.9)
43
+ log4r (1.1.10)
44
+ lumberjack (1.0.9)
45
+ method_source (0.8.2)
46
+ pry (0.10.1)
47
+ coderay (~> 1.1.0)
48
+ method_source (~> 0.8.1)
49
+ slop (~> 3.4)
50
+ pry-nav (0.2.4)
51
+ pry (>= 0.9.10, < 0.11.0)
52
+ pry-remote (0.1.8)
53
+ pry (~> 0.9)
54
+ slop (~> 3.0)
55
+ rake (10.4.0)
56
+ rb-fsevent (0.9.4)
57
+ rb-inotify (0.9.5)
58
+ ffi (>= 0.5.0)
59
+ rspec (3.1.0)
60
+ rspec-core (~> 3.1.0)
61
+ rspec-expectations (~> 3.1.0)
62
+ rspec-mocks (~> 3.1.0)
63
+ rspec-core (3.1.7)
64
+ rspec-support (~> 3.1.0)
65
+ rspec-expectations (3.1.2)
66
+ diff-lcs (>= 1.2.0, < 2.0)
67
+ rspec-support (~> 3.1.0)
68
+ rspec-mocks (3.1.3)
69
+ rspec-support (~> 3.1.0)
70
+ rspec-support (3.1.2)
71
+ ruby-progressbar (1.7.5)
72
+ slop (3.6.0)
73
+ thor (0.19.1)
74
+ timers (4.0.1)
75
+ hitimes
76
+
77
+ PLATFORMS
78
+ ruby
79
+
80
+ DEPENDENCIES
81
+ bundler (~> 1.7)
82
+ decking!
83
+ guard
84
+ guard-rspec
85
+ pry
86
+ pry-nav
87
+ pry-remote
88
+ rake (~> 10.0)
89
+ rspec (~> 3.1.0)
data/Guardfile ADDED
@@ -0,0 +1,14 @@
1
+ # Note: The cmd option is now required due to the increasing number of ways
2
+ # rspec may be run, below are examples of the most common uses.
3
+ # * bundler: 'bundle exec rspec'
4
+ # * bundler binstubs: 'bin/rspec'
5
+ # * spring: 'bin/rsspec' (This will use spring if running and you have
6
+ # installed the spring binstubs per the docs)
7
+ # * zeus: 'zeus rspec' (requires the server to be started separetly)
8
+ # * 'just' rspec: 'rspec'
9
+ guard :rspec, cmd: 'bundle exec rspec --color --format documentation' do
10
+ watch(%r{^spec/.+_spec\.rb$})
11
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
12
+ watch('spec/spec_helper.rb') { "spec" }
13
+ end
14
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Randy D. Wallace Jr.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Decking
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'decking'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install decking
20
+
21
+ ## Usage
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it ( https://github.com/[my-github-username]/decking/fork )
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |task|
5
+ task.rspec_opts = ['--color', '--format', 'documentation']
6
+ end
7
+
8
+ task :default => :spec
9
+
data/bin/decking ADDED
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'gli'
4
+ require 'decking'
5
+
6
+ module Decking::CLI
7
+ @@logger = Log4r::Logger.new('decking::cli')
8
+ extend GLI::App
9
+ sort_help :manually
10
+ subcommand_option_handling :normal
11
+ config_file '.deckingrc'
12
+ program_desc "Manage a cluster of docker containers"
13
+ version "Decking: #{Decking::VERSION}\n Docker: #{Docker.version["Version"]}"
14
+
15
+ desc 'Path to decking.yaml'
16
+ flag [:config], :default_value => File.expand_path(File.dirname(__FILE__)) + '/../spec/resources/decking-container-tests.yaml'
17
+
18
+ desc 'The name of the cluster to use for all commands'
19
+ flag [:cluster], :default_value => "unknown"
20
+
21
+
22
+ pre do |global_options,options,args|
23
+ @@logger.debug "Using #{global_options[:config]} for decking config"
24
+ Decking::Parser.config_file global_options[:config]
25
+ Decking::Parser.parse global_options[:cluster]
26
+ Decking::Parser.config.containers.map { |name, config| Decking::Container.add config }
27
+ end
28
+
29
+ desc 'Create and start the cluster'
30
+ command :run do |c|
31
+ c.switch [:f, :force], :default_value => false, :desc => 'Force the command in a destructive manner'
32
+ c.action do |global_options,options,args|
33
+ @@logger.info "Running cluster #{global_options[:cluster]}"
34
+ unless options[:force]
35
+ Decking::Container.create_all
36
+ else
37
+ Decking::Container.create_all!
38
+ end
39
+ Decking::Container.start_all
40
+ end
41
+ end
42
+
43
+ desc 'Show the cluster logs'
44
+ command [:logs,:attach] do |c|
45
+ c.switch [:t,:timestamps], :default_value => false, :desc => 'Show Timestamps'
46
+ c.switch [:f,:follow], :default_value => false, :desc => 'Tail the logs'
47
+ c.flag [:n,:lines], :default_value => 100, :desc => 'Number of lines to print'
48
+ c.action do |global_options,options,args|
49
+ Decking::Container.tail_all_logs options[:timestamps], options[:lines], options[:follow]
50
+ end
51
+ end
52
+
53
+ desc 'Completely remove the cluster containers'
54
+ command [:rm] do |c|
55
+ c.switch [:f, :force], :default_value => false, :desc => 'Force the Delete'
56
+ c.action do |global_options,options,args|
57
+ unless options[:force]
58
+ Decking::Container.delete_all
59
+ else
60
+ Decking::Container.delete_all!
61
+ end
62
+ end
63
+ end
64
+
65
+ desc 'Stop the cluster'
66
+ command [:stop] do |c|
67
+ c.switch [:f, :force], :default_value => false, :desc => 'Force Stop the cluster'
68
+ c.action do |global_options,options,args|
69
+ unless options[:force]
70
+ Decking::Container.stop_all
71
+ else
72
+ Decking::Container.stop_all!
73
+ end
74
+ end
75
+ end
76
+
77
+ desc 'Start the cluster'
78
+ command [:start] do |c|
79
+ c.action do |global_options,options,args|
80
+ Decking::Container.start_all
81
+ end
82
+ end
83
+
84
+ desc 'Only create the cluster'
85
+ command [:create] do |c|
86
+ c.switch [:f, :force], :default_value => false, :desc => 'Force create the cluster by removing any existing duplicates'
87
+ c.action do |global_options,options,args|
88
+ unless options[:force]
89
+ Decking::Container.create_all
90
+ else
91
+ Decking::Container.create_all!
92
+ end
93
+ end
94
+ end
95
+
96
+ desc 'Print to STDOUT the compiled cluster configuration'
97
+ command [:printcluster] do |c|
98
+ c.action do |global_options,options,args|
99
+ Decking::Parser.print
100
+ end
101
+ end
102
+
103
+ desc 'Build Container(s)'
104
+ command [:build] do |c|
105
+ end
106
+
107
+ end
108
+
109
+ exit Decking::CLI.run(ARGV)
data/bin/decking-test ADDED
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+ require 'decking'
3
+
4
+ puts FileUtils.pwd
5
+
6
+ img = nil
7
+ FileUtils.cd(File.expand_path('~/git/decking/docker-eds-tests')) do
8
+ img = Docker::Image.build_from_tar(Docker::Util.create_dir_tar('.'), 'dockerfile' => 'eds-webapp/Dockerfile') do |chunk|
9
+ puts JSON.parse(chunk)['stream']
10
+ end
11
+ end
12
+
13
+ img.tag 'repo' => 'test', 'tag' => 'test', 'force' => true
14
+
15
+ puts img.inspect
16
+
17
+ #Decking::Parser.config_file File.expand_path(File.dirname(__FILE__)) + '/../spec/resources/decking-container-tests.yaml'
18
+ #Decking::Parser.parse 'container-tests'
19
+ #Decking::Parser.config.containers.map { |name, config| Decking::Container.add config }
20
+ #Decking::Parser.print
21
+ #Decking::Container.delete_all!
22
+ #Decking::Container.create_all
23
+ #Decking::Container.create_all!
24
+ #Decking::Container.start_all
25
+ #Decking::Container.tail_all_logs
26
+ #Decking::Container.stop_all
27
+ #Decking::Container.stop_all!
28
+ #
29
+ #
30
+ #
31
+ #
32
+ #
33
+ ## Garbage
34
+ #container_name="ubuntu-hello-world.container-tests"
35
+ #Decking::Container.map { |name, inst| puts name + ': ' + inst.config.image + ', ' + inst.config.name}
36
+ #puts Decking::Container.count #=> 5
37
+ #puts Decking::Container.all? { |name, inst| inst.config.data == true } #=> true if any of the containers have the data=true
38
+ #puts Decking::Container.group_by{ |name, inst| inst.config.data } #=> Container instances grouped by whether or not data is true or false
39
+ #puts Decking::Container.find_all{ |name, inst| inst.config.data == true } #=> Container instances that have data=true
40
+ #puts Decking::Container[container_name].config.image #=> webapp
41
+ #puts Decking::Container[container_name].volumes_from.inspect #=> ["repo", "config"]
42
+ #puts Decking::Container[container_name].image.inspect #=> webapp
43
+ #puts Decking::Container[container_name].domainname.inspect #=> qa.randywallace.com
44
+ #puts Docker.url
45
+ #puts Decking::Container[container_name].config
46
+ #Decking::Container.deleteall
47
+ #puts Docker::Container.get(container_name).logs('stdout'=>true, 'stderr'=>true).gsub(/\f/,'')
data/decking.gemspec ADDED
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'decking/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "decking"
8
+ spec.version = Decking::VERSION
9
+ spec.authors = ["Randy D. Wallace Jr."]
10
+ spec.email = ["randy@randywallace.com"]
11
+ spec.summary = %q{Decking is a rewrite of the node tool also called decking which provides docker orchestration}
12
+ spec.description = %q{Decking is a rewrite of the node tool also called decking which provides docker orchestration}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "hashie"
22
+ spec.add_dependency "ruby-progressbar"
23
+ spec.add_dependency "docker-api", "~> 1.22.2"
24
+ spec.add_dependency "log4r"
25
+ spec.add_dependency "io-console"
26
+ spec.add_dependency "gli"
27
+
28
+ spec.add_development_dependency "bundler", "~> 1.7"
29
+ spec.add_development_dependency "rake", "~> 10.0"
30
+ spec.add_development_dependency "rspec", "~> 3.1.0"
31
+ spec.add_development_dependency "guard"
32
+ spec.add_development_dependency "guard-rspec"
33
+ spec.add_development_dependency "pry"
34
+ spec.add_development_dependency "pry-remote"
35
+ spec.add_development_dependency "pry-nav"
36
+ end
37
+
data/doc/notes.md ADDED
@@ -0,0 +1,5 @@
1
+ ### API Sources
2
+
3
+ [Container Config](https://godoc.org/github.com/docker/docker/runconfig#Config)
4
+ [HostConfig](https://godoc.org/github.com/docker/docker/runconfig#HostConfig)
5
+ [ImageConfig](https://godoc.org/github.com/docker/docker/builder#Config)
@@ -0,0 +1,22 @@
1
+ module Decking
2
+ class Container
3
+ @@logger = Log4r::Logger.new('decking::container::attach')
4
+ def tail_logs timestamps = false, lines = 0, follow = true
5
+ @@logger.info "Grabbing logs from #{name}... stop with ^C".yellow
6
+ begin
7
+ Docker::Container.get(name).streaming_logs(follow: follow, stdout: true, stderr: true, tail: lines, timestamps: timestamps) do |stream, chunk|
8
+ case stream
9
+ when :stdout
10
+ $stdout.print "(#{name}) #{chunk}"
11
+ $stdout.flush
12
+ when :stderr
13
+ $stdout.print "(#{name}) #{chunk}".red
14
+ $stdout.flush
15
+ end
16
+ end
17
+ rescue Docker::Error::NotFoundError
18
+ @@logger.error "Container #{name} does not exist"
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,64 @@
1
+ module Decking
2
+ class Container
3
+ def create force = false
4
+ run_with_progress("#{"Forcefully " if force}Creating #{name}") do
5
+ begin
6
+ exposed_ports = Hash.new
7
+ port.each do |val|
8
+ vars = val.split(':')
9
+ if vars.size == 3
10
+ exposed_ports[vars[-2]] = Hash.new
11
+ else
12
+ exposed_ports[vars[-1]] = Hash.new
13
+ end
14
+ end
15
+ @container = Docker::Container.create 'name' => name,
16
+ 'Image' => image,
17
+ 'Hostname' => hostname,
18
+ 'Domainname' => domainname,
19
+ 'Entrypoint' => entrypoint,
20
+ 'Memory' => memory,
21
+ 'MemorySwap' => memory_swap,
22
+ 'CpuShares' => cpu_shares,
23
+ 'Cpuset' => cpu_set,
24
+ 'AttachStdout' => attach_stdout,
25
+ 'AttachStderr' => attach_stderr,
26
+ 'AttachStdin' => attach_stdin,
27
+ 'Tty' => tty,
28
+ 'OpenStdin' => open_stdin,
29
+ 'StdinOnce' => stdin_once,
30
+ 'Cmd' => command.scan(/(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*'|[^'" ])+/).map{|val| val.gsub(/^['"]/,"").gsub(/['"]$/,"")},
31
+ 'Env' => env.map { |k, v| "#{k}=#{v}" },
32
+ 'ExposedPorts' => exposed_ports
33
+ rescue Docker::Error::ConflictError
34
+ clear_progressline
35
+ puts "Container #{name} already exists".yellow
36
+ if force
37
+ delete!
38
+ retry
39
+ else
40
+ exit
41
+ end
42
+ rescue Docker::Error::NotFoundError
43
+ clear_progressline
44
+ puts "Image #{image} not found".yellow
45
+ exit
46
+ rescue Docker::Error::ServerError => e
47
+ clear_progressline
48
+ puts "Container #{name} encountered a ServerError".red
49
+ puts e.message.red
50
+ exit
51
+ rescue Exception => e
52
+ clear_progressline
53
+ puts "Unhandled Exception #{e.message}"
54
+ e.backtrace.map{|msg| puts " #{msg}"}
55
+ exit
56
+ end
57
+ end
58
+ end
59
+
60
+ def create!
61
+ create true
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,21 @@
1
+ module Decking
2
+ class Container
3
+ def delete force = false
4
+ run_with_progress("#{'Forcefully ' if force}Deleting #{name}") do
5
+ begin
6
+ Docker::Container.get(name).remove('force' => force)
7
+ rescue Docker::Error::NotFoundError
8
+ clear_progressline
9
+ @@logger.warn "Container #{name} does not exist, nothing to delete".yellow
10
+ rescue Docker::Error::ServerError => e
11
+ clear_progressline
12
+ @@logger.error e.message.red
13
+ end
14
+ end
15
+ end
16
+
17
+ def delete!
18
+ delete true
19
+ end
20
+ end
21
+ end
File without changes
File without changes
@@ -0,0 +1,40 @@
1
+ module Decking
2
+ class Container
3
+ def start
4
+ run_with_progress("Starting #{name}") do
5
+ begin
6
+ port_bindings = Hash.new
7
+ port.each do |val|
8
+ vars = val.split(':')
9
+ case vars.size
10
+ when 3
11
+ port_bindings[vars[-1]] = [ { 'HostIp' => vars[0], 'HostPort' => vars[1] } ]
12
+ when 2
13
+ port_bindings[vars[-1]] = [ { 'HostIp' => '', 'HostPort' => vars[0] } ]
14
+ else
15
+ port_bindings[vars[0]] = [ { 'HostPort' => vars[0] } ]
16
+ end
17
+ end
18
+ Docker::Container.get(name).start! 'Links' => links,
19
+ 'Binds' => binds,
20
+ 'LxcConf' => lxc_conf,
21
+ 'PortBindings' => port_bindings
22
+ rescue Docker::Error::NotFoundError
23
+ clear_progressline
24
+ puts "Container #{name} not found".red
25
+ exit
26
+ rescue Docker::Error::ServerError => e
27
+ clear_progressline
28
+ puts "Container #{name} encountered a ServerError".red
29
+ puts e.message.red
30
+ exit
31
+ rescue Exception => e
32
+ clear_progressline
33
+ puts "Unhandled Exception #{e.message}"
34
+ e.backtrace.map{|msg| puts " #{msg}"}
35
+ exit
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,25 @@
1
+ module Decking
2
+ class Container
3
+
4
+ def stop time_to_kill = 30
5
+ run_with_progress("Stopping #{name}") do
6
+ begin
7
+ Docker::Container.get(name).stop('t' => time_to_kill)
8
+ rescue Docker::Error::NotFoundError
9
+ clear_progressline
10
+ puts "Container #{name} does not exist, nothing to stop".yellow
11
+ rescue Docker::Error::ServerError => e
12
+ clear_progressline
13
+ puts "Container #{name} encountered a ServerError".red
14
+ puts e.message.red
15
+ exit
16
+ end
17
+ end
18
+ end
19
+
20
+ def stop!
21
+ stop 1
22
+ end
23
+ end
24
+ end
25
+
File without changes
File without changes
@@ -0,0 +1,71 @@
1
+ require 'decking/container/create'
2
+ require 'decking/container/start'
3
+ require 'decking/container/delete'
4
+ require 'decking/container/stop'
5
+ require 'decking/container/attach'
6
+
7
+ module Decking
8
+ class Container
9
+ @@logger = Log4r::Logger.new('decking::container')
10
+ include Decking::Helpers
11
+ class << self
12
+ include Decking::Helpers
13
+ include Enumerable
14
+
15
+ def delete_all ; map{|n, c| c.delete }; end
16
+ def delete_all!; map{|n, c| c.delete! }; end
17
+
18
+ def create_all ; map{|n, c| c.create }; end
19
+ def create_all!; map{|n, c| c.create! }; end
20
+
21
+ def start_all ; map{|n, c| c.start }; end
22
+
23
+ def stop_all ; map{|n, c| c.stop }; end
24
+ def stop_all! ; map{|n, c| c.stop! }; end
25
+
26
+ def attach_all
27
+ run_with_threads_multiplexed :attach, instances
28
+ end
29
+
30
+ def tail_all_logs *args
31
+ run_with_threads_multiplexed :tail_logs, instances, *args
32
+ end
33
+
34
+ def containers
35
+ @containers ||= Hash.new
36
+ end
37
+
38
+ def instances
39
+ @instances ||= Hash.new
40
+ end
41
+
42
+ def add params
43
+ containers.update params.name => params
44
+ self[params.name]
45
+ end
46
+
47
+ def [](name)
48
+ instances[name] ||= new(name, @containers[name])
49
+ end
50
+
51
+ def each &block
52
+ @instances.each(&block)
53
+ end
54
+ end
55
+
56
+ attr_reader :name, :config, :container
57
+
58
+ def initialize name, params
59
+ @name = name
60
+ @config = params
61
+ end
62
+
63
+ def method_missing method, *args, &block
64
+ if config.key? method
65
+ config[method]
66
+ else
67
+ super
68
+ end
69
+ end
70
+ end
71
+ end