decking 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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