percheron 0.1.0 → 0.2.0
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 +2 -0
- data/.travis.yml +6 -1
- data/Gemfile +9 -1
- data/README.md +12 -11
- data/Rakefile +3 -1
- data/bin/percheron +26 -0
- data/lib/percheron/cli/abstract_command.rb +19 -0
- data/lib/percheron/cli/console_command.rb +10 -0
- data/lib/percheron/cli/list_command.rb +12 -0
- data/lib/percheron/cli/main_command.rb +10 -0
- data/lib/percheron/cli/start_command.rb +12 -0
- data/lib/percheron/cli/stop_command.rb +12 -0
- data/lib/percheron/cli.rb +13 -0
- data/lib/percheron/config.rb +45 -0
- data/lib/percheron/config_delegator.rb +12 -0
- data/lib/percheron/container/actions/build.rb +35 -0
- data/lib/percheron/container/actions/create.rb +38 -0
- data/lib/percheron/container/actions/start.rb +40 -0
- data/lib/percheron/container/actions/stop.rb +26 -0
- data/lib/percheron/container/actions.rb +11 -0
- data/lib/percheron/container/main.rb +88 -0
- data/lib/percheron/container/null.rb +15 -0
- data/lib/percheron/container.rb +8 -0
- data/lib/percheron/core_extensions.rb +21 -0
- data/lib/percheron/docker_connection.rb +43 -0
- data/lib/percheron/errors.rb +9 -0
- data/lib/percheron/formatters/stack/table.rb +44 -0
- data/lib/percheron/formatters/stack.rb +8 -0
- data/lib/percheron/formatters.rb +7 -0
- data/lib/percheron/stack.rb +66 -0
- data/lib/percheron/validators/config.rb +48 -0
- data/lib/percheron/validators/container.rb +44 -0
- data/lib/percheron/validators/stack.rb +35 -0
- data/lib/percheron/validators.rb +8 -0
- data/lib/percheron/version.rb +1 -1
- data/lib/percheron.rb +16 -2
- data/percheron.gemspec +16 -13
- metadata +76 -6
- data/bin/console +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2aa1dd04adcc5a0570327825d599e9a0f9a9af87
|
4
|
+
data.tar.gz: c279d99e8dacd85601293984d6653764db508d4c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 16994a1a63a0e6f468d9619113abfda4b0cbf7d7501feb510d8cf814ff24d7dd704f1d56b744105edc7b58e379a631460da8a7f3780746d571cc84144c844c18
|
7
|
+
data.tar.gz: e75b7829e7572a6864a5301afe36c408d109717c9681a9a8ec336e100c5f55145bb5ae79bf4cb07b2aef08c5edf003e885d1b7d701e90c8719626a8932f377f5
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -1,4 +1,12 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
# Specify your gem's dependencies in percheron.gemspec
|
4
3
|
gemspec
|
4
|
+
|
5
|
+
group :test do
|
6
|
+
gem 'codeclimate-test-reporter', '~> 0.1', require: nil
|
7
|
+
end
|
8
|
+
|
9
|
+
group :development do
|
10
|
+
gem 'pry-byebug', '~> 2.0'
|
11
|
+
gem 'awesome_print', '~> 1.0'
|
12
|
+
end
|
data/README.md
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
# Percheron
|
2
2
|
|
3
|
-
|
3
|
+
[](http://badge.fury.io/rb/percheron)
|
4
|
+
[](https://travis-ci.org/ashmckenzie/percheron)
|
5
|
+
[](https://codeclimate.com/github/ashmckenzie/percheron)
|
4
6
|
|
7
|
+
Organise your Docker containers with muscle and intelligence.
|
5
8
|
|
6
9
|
## Installation
|
7
10
|
|
@@ -13,25 +16,23 @@ gem 'percheron'
|
|
13
16
|
|
14
17
|
And then execute:
|
15
18
|
|
16
|
-
|
19
|
+
```shell
|
20
|
+
$ bundle install
|
21
|
+
```
|
17
22
|
|
18
23
|
Or install it yourself as:
|
19
24
|
|
20
|
-
|
25
|
+
```shell
|
26
|
+
$ gem install percheron
|
27
|
+
```
|
21
28
|
|
22
29
|
## Usage
|
23
30
|
|
24
|
-
|
25
|
-
|
26
|
-
## Development
|
27
|
-
|
28
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
29
|
-
|
30
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
31
|
+
WIP, not ready yet :)
|
31
32
|
|
32
33
|
## Contributing
|
33
34
|
|
34
|
-
1. Fork it ( https://github.com/
|
35
|
+
1. Fork it ( https://github.com/ashmckenzie/percheron/fork )
|
35
36
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
36
37
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
37
38
|
4. Push to the branch (`git push origin my-new-feature`)
|
data/Rakefile
CHANGED
data/bin/percheron
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
lib = File.expand_path('../../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'logger'
|
7
|
+
require 'percheron'
|
8
|
+
require 'percheron/cli'
|
9
|
+
|
10
|
+
$logger = Logger.new(STDOUT)
|
11
|
+
|
12
|
+
logger_level = Logger::ERROR
|
13
|
+
|
14
|
+
if ENV['VERBOSE'] == 'true'
|
15
|
+
logger_level = Logger::INFO
|
16
|
+
end
|
17
|
+
|
18
|
+
if ENV['DEBUG'] == 'true'
|
19
|
+
require 'pry-byebug'
|
20
|
+
require 'awesome_print'
|
21
|
+
logger_level = Logger::DEBUG
|
22
|
+
end
|
23
|
+
|
24
|
+
$logger.level = logger_level
|
25
|
+
|
26
|
+
Percheron::CLI::MainCommand.run
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Percheron
|
2
|
+
module CLI
|
3
|
+
class AbstractCommand < Clamp::Command
|
4
|
+
|
5
|
+
option [ '-c', '--config_file' ], 'CONFIG', 'Configuration file', default: '.percheron.yml'
|
6
|
+
|
7
|
+
option '--version', :flag, 'show version' do
|
8
|
+
puts Percheron::VERSION
|
9
|
+
exit(0)
|
10
|
+
end
|
11
|
+
|
12
|
+
def config
|
13
|
+
@config ||= Percheron::Config.new(config_file)
|
14
|
+
rescue Errors::ConfigFileInvalid => e
|
15
|
+
$logger.error "An error has occurred while reading your config file - #{e.message}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Percheron
|
2
|
+
module CLI
|
3
|
+
class MainCommand < AbstractCommand
|
4
|
+
subcommand 'list', "List stacks and it's containers", ListCommand
|
5
|
+
subcommand 'console', 'Start a pry console session', ConsoleCommand
|
6
|
+
subcommand 'start', 'Start a stack', StartCommand
|
7
|
+
subcommand 'stop', 'Stop a stack', StopCommand
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'clamp'
|
2
|
+
|
3
|
+
require 'percheron/cli/abstract_command'
|
4
|
+
require 'percheron/cli/list_command'
|
5
|
+
require 'percheron/cli/start_command'
|
6
|
+
require 'percheron/cli/stop_command'
|
7
|
+
require 'percheron/cli/console_command'
|
8
|
+
require 'percheron/cli/main_command'
|
9
|
+
|
10
|
+
module Percheron
|
11
|
+
module CLI
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Percheron
|
4
|
+
class Config
|
5
|
+
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def_delegators :contents, :docker
|
9
|
+
|
10
|
+
def initialize(file)
|
11
|
+
@file = Pathname.new(file).expand_path
|
12
|
+
valid?
|
13
|
+
docker_setup!
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def stacks
|
18
|
+
contents.stacks.inject({}) do |all, stack_config|
|
19
|
+
all[stack_config.name] = stack_config unless all[stack_config.name]
|
20
|
+
all
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def file_base_path
|
25
|
+
file.dirname
|
26
|
+
end
|
27
|
+
|
28
|
+
def valid?
|
29
|
+
Validators::Config.new(file).valid?
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :file
|
35
|
+
|
36
|
+
def contents
|
37
|
+
Hashie::Mash.new(YAML.load_file(file))
|
38
|
+
end
|
39
|
+
|
40
|
+
def docker_setup!
|
41
|
+
Percheron::DockerConnection.new(self).setup!
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Percheron
|
2
|
+
module Container
|
3
|
+
module Actions
|
4
|
+
class Build
|
5
|
+
|
6
|
+
def initialize(container, nocache: false)
|
7
|
+
@container = container
|
8
|
+
@nocache = nocache
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute!
|
12
|
+
base_dir = container.dockerfile.dirname.to_s
|
13
|
+
$logger.debug "Building '#{container.image}'"
|
14
|
+
Docker::Image.build_from_dir(base_dir, build_opts) do |out|
|
15
|
+
$logger.debug '%s' % [ out.strip ]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
attr_reader :container, :nocache
|
22
|
+
|
23
|
+
def build_opts
|
24
|
+
{
|
25
|
+
'dockerfile' => container.dockerfile.basename.to_s,
|
26
|
+
't' => container.image,
|
27
|
+
'forcerm' => true,
|
28
|
+
'nocache' => nocache
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Percheron
|
2
|
+
module Container
|
3
|
+
module Actions
|
4
|
+
class Create
|
5
|
+
|
6
|
+
def initialize(container)
|
7
|
+
@container = container
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute!
|
11
|
+
Container::Actions::Build.new(container).execute! unless image_exists?
|
12
|
+
$logger.debug "Creating '#{container.name}'"
|
13
|
+
Docker::Container.create(create_opts)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
attr_reader :container
|
19
|
+
|
20
|
+
def create_opts
|
21
|
+
{
|
22
|
+
'name' => container.name,
|
23
|
+
'Image' => container.image,
|
24
|
+
'Hostname' => container.name,
|
25
|
+
'Env' => container.env,
|
26
|
+
'ExposedPorts' => container.exposed_ports,
|
27
|
+
'VolumesFrom' => container.volumes
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def image_exists?
|
32
|
+
Docker::Image.exist?(container.image)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Percheron
|
2
|
+
module Container
|
3
|
+
module Actions
|
4
|
+
class Start
|
5
|
+
|
6
|
+
def initialize(container)
|
7
|
+
@container = container
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute!
|
11
|
+
if container.exists?
|
12
|
+
$logger.debug "Starting '#{container.name}'"
|
13
|
+
container.docker_container.start!(start_opts)
|
14
|
+
else
|
15
|
+
raise Errors::ContainerDoesNotExist.new
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
attr_reader :container
|
22
|
+
|
23
|
+
def start_opts
|
24
|
+
opts = container.ports.inject({}) do |all, p|
|
25
|
+
destination, source = p.split(':')
|
26
|
+
all[source] = [ { 'HostPort' => destination } ]
|
27
|
+
all
|
28
|
+
end
|
29
|
+
|
30
|
+
{
|
31
|
+
'PortBindings' => opts,
|
32
|
+
'Links' => container.links,
|
33
|
+
'Binds' => container.volumes
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Percheron
|
2
|
+
module Container
|
3
|
+
module Actions
|
4
|
+
class Stop
|
5
|
+
|
6
|
+
def initialize(container)
|
7
|
+
@container = container
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute!
|
11
|
+
if container.running?
|
12
|
+
$logger.debug "Stopping '#{container.name}'"
|
13
|
+
container.docker_container.stop!
|
14
|
+
else
|
15
|
+
raise Errors::ContainerNotRunning.new
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
attr_reader :container
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Percheron
|
2
|
+
module Container
|
3
|
+
class Main
|
4
|
+
|
5
|
+
extend Forwardable
|
6
|
+
extend ConfigDelegator
|
7
|
+
|
8
|
+
def_delegators :container_config, :name, :version
|
9
|
+
|
10
|
+
def_config_item_with_default :container_config, [], :env, :ports, :volumes, :dependant_container_names
|
11
|
+
|
12
|
+
def initialize(config, stack, container_name)
|
13
|
+
@config = config
|
14
|
+
@stack = stack
|
15
|
+
@container_name = container_name
|
16
|
+
valid?
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def stop!
|
21
|
+
Container::Actions::Stop.new(self).execute!
|
22
|
+
rescue Errors::ContainerNotRunning
|
23
|
+
$logger.debug "Container '#{name}' is not running"
|
24
|
+
end
|
25
|
+
|
26
|
+
def start!
|
27
|
+
Container::Actions::Create.new(self).execute! unless exists?
|
28
|
+
Container::Actions::Start.new(self).execute!
|
29
|
+
end
|
30
|
+
|
31
|
+
def id
|
32
|
+
exists? ? info.id[0...12] : 'N/A'
|
33
|
+
end
|
34
|
+
|
35
|
+
def image
|
36
|
+
'%s:%s' % [ name, version ]
|
37
|
+
end
|
38
|
+
|
39
|
+
def dockerfile
|
40
|
+
container_config.dockerfile ? Pathname.new(File.expand_path(container_config.dockerfile, config.file_base_path)): nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def exposed_ports
|
44
|
+
ports.inject({}) do |all, p|
|
45
|
+
all[p.split(':')[1]] = {}
|
46
|
+
all
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def links
|
51
|
+
dependant_container_names.map do |container_name|
|
52
|
+
'%s:%s' % [ container_name, container_name ]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def docker_container
|
57
|
+
Docker::Container.get(name)
|
58
|
+
rescue Docker::Error::NotFoundError, Excon::Errors::SocketError
|
59
|
+
Container::Null.new
|
60
|
+
end
|
61
|
+
|
62
|
+
def running?
|
63
|
+
exists? && info.State.Running
|
64
|
+
end
|
65
|
+
|
66
|
+
def exists?
|
67
|
+
!info.empty?
|
68
|
+
end
|
69
|
+
|
70
|
+
def valid?
|
71
|
+
Validators::Container.new(self).valid?
|
72
|
+
end
|
73
|
+
|
74
|
+
protected
|
75
|
+
|
76
|
+
attr_reader :config, :stack, :container_name
|
77
|
+
|
78
|
+
def info
|
79
|
+
Hashie::Mash.new(docker_container.info)
|
80
|
+
end
|
81
|
+
|
82
|
+
def container_config
|
83
|
+
@container_config ||= stack.container_configs[container_name] || Hashie::Mash.new({})
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Percheron
|
2
|
+
module CoreExtensions
|
3
|
+
module Array
|
4
|
+
module Returning
|
5
|
+
def return
|
6
|
+
result = nil
|
7
|
+
each do |x|
|
8
|
+
r = yield(x)
|
9
|
+
if r
|
10
|
+
result = r
|
11
|
+
break
|
12
|
+
end
|
13
|
+
end
|
14
|
+
result
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Array.include(Percheron::CoreExtensions::Array::Returning)
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Percheron
|
2
|
+
class DockerConnection
|
3
|
+
|
4
|
+
def initialize(config)
|
5
|
+
@config = config
|
6
|
+
end
|
7
|
+
|
8
|
+
def setup!
|
9
|
+
Docker.logger = $logger if ENV['DOCKER_DEBUG'] == 'true'
|
10
|
+
Docker.url = config.docker.host
|
11
|
+
|
12
|
+
opts = {
|
13
|
+
chunk_size: 1,
|
14
|
+
connect_timeout: config.docker.timeout,
|
15
|
+
scheme: 'https'
|
16
|
+
}
|
17
|
+
|
18
|
+
if cert_path
|
19
|
+
opts.merge!({
|
20
|
+
client_cert: File.join(cert_path, 'cert.pem'),
|
21
|
+
client_key: File.join(cert_path, 'key.pem'),
|
22
|
+
ssl_ca_file: File.join(cert_path, 'ca.pem')
|
23
|
+
})
|
24
|
+
end
|
25
|
+
|
26
|
+
Docker.options = opts
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_reader :config
|
32
|
+
|
33
|
+
def cert_path
|
34
|
+
@cert_path ||= begin
|
35
|
+
if ENV['DOCKER_CERT_PATH']
|
36
|
+
File.expand_path(ENV['DOCKER_CERT_PATH'])
|
37
|
+
else
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module Percheron
|
2
|
+
module Errors
|
3
|
+
class ConfigFileInvalid < StandardError; end
|
4
|
+
class StackInvalid < StandardError; end
|
5
|
+
class ContainerInvalid < StandardError; end
|
6
|
+
class ContainerDoesNotExist < StandardError; end
|
7
|
+
class ContainerNotRunning < StandardError; end
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Percheron
|
2
|
+
module Formatters
|
3
|
+
module Stack
|
4
|
+
class Table
|
5
|
+
|
6
|
+
def initialize(stack)
|
7
|
+
@stack = stack
|
8
|
+
end
|
9
|
+
|
10
|
+
def generate
|
11
|
+
Terminal::Table.new(
|
12
|
+
title: stack.name,
|
13
|
+
headings: headings,
|
14
|
+
rows: rows
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :stack
|
21
|
+
|
22
|
+
def headings
|
23
|
+
[
|
24
|
+
'Container name',
|
25
|
+
'ID',
|
26
|
+
'Version',
|
27
|
+
'Running?'
|
28
|
+
]
|
29
|
+
end
|
30
|
+
|
31
|
+
def rows
|
32
|
+
stack.containers.map do |container_name, container|
|
33
|
+
[
|
34
|
+
container_name,
|
35
|
+
container.id,
|
36
|
+
container.version,
|
37
|
+
container.running?
|
38
|
+
]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Percheron
|
2
|
+
class Stack
|
3
|
+
|
4
|
+
extend Forwardable
|
5
|
+
|
6
|
+
def_delegators :stack_config, :name, :description
|
7
|
+
|
8
|
+
def initialize(config, stack_name)
|
9
|
+
@config = config
|
10
|
+
@stack_name = stack_name
|
11
|
+
valid?
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.all(config)
|
16
|
+
all = {}
|
17
|
+
config.stacks.each do |stack_name, _|
|
18
|
+
stack = new(config, stack_name)
|
19
|
+
all[stack.name] = stack
|
20
|
+
end
|
21
|
+
all
|
22
|
+
end
|
23
|
+
|
24
|
+
def container_configs
|
25
|
+
stack_config.containers.inject({}) do |all, container|
|
26
|
+
all[container.name] = container unless all[container.name]
|
27
|
+
all
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def containers
|
32
|
+
containers = {}
|
33
|
+
stack_config.containers.each do |container|
|
34
|
+
container = Container::Main.new(config, self, container.name)
|
35
|
+
containers[container.name] = container
|
36
|
+
end
|
37
|
+
containers
|
38
|
+
end
|
39
|
+
|
40
|
+
def stop!
|
41
|
+
exec_on_containers { |container| container.stop! }
|
42
|
+
end
|
43
|
+
|
44
|
+
def start!
|
45
|
+
exec_on_containers { |container| container.start! }
|
46
|
+
end
|
47
|
+
|
48
|
+
def valid?
|
49
|
+
Validators::Stack.new(self).valid?
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
attr_reader :config, :stack_name
|
55
|
+
|
56
|
+
def stack_config
|
57
|
+
@stack_config ||= config.stacks[stack_name] || Hashie::Mash.new({})
|
58
|
+
end
|
59
|
+
|
60
|
+
def exec_on_containers
|
61
|
+
containers.each do |container_name, container|
|
62
|
+
yield(container)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Percheron
|
2
|
+
module Validators
|
3
|
+
class Config
|
4
|
+
|
5
|
+
def initialize(config_file)
|
6
|
+
@config_file = config_file
|
7
|
+
end
|
8
|
+
|
9
|
+
def valid?
|
10
|
+
message = rules.return { |rule| send(rule) }
|
11
|
+
|
12
|
+
if message
|
13
|
+
raise Errors::ConfigFileInvalid.new(message)
|
14
|
+
else
|
15
|
+
true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
attr_reader :config_file
|
22
|
+
|
23
|
+
def rules
|
24
|
+
[
|
25
|
+
:validate_config_file_existence,
|
26
|
+
:validate_config_file_not_empty,
|
27
|
+
:validate_config_file_contents
|
28
|
+
]
|
29
|
+
end
|
30
|
+
|
31
|
+
def config_file_contents
|
32
|
+
@config_file_contents ||= Hashie::Mash.new(YAML.load_file(config_file))
|
33
|
+
end
|
34
|
+
|
35
|
+
def validate_config_file_existence
|
36
|
+
'Config file does not exist' unless config_file.exist?
|
37
|
+
end
|
38
|
+
|
39
|
+
def validate_config_file_not_empty
|
40
|
+
'Config file is empty' if config_file_contents.empty?
|
41
|
+
end
|
42
|
+
|
43
|
+
def validate_config_file_contents
|
44
|
+
'Config file is invalid' unless config_file_contents.docker
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Percheron
|
2
|
+
module Validators
|
3
|
+
class Container
|
4
|
+
|
5
|
+
def initialize(container)
|
6
|
+
@container = container
|
7
|
+
end
|
8
|
+
|
9
|
+
def valid?
|
10
|
+
message = rules.return { |rule| send(rule) }
|
11
|
+
|
12
|
+
if message
|
13
|
+
raise Errors::ContainerInvalid.new(message)
|
14
|
+
else
|
15
|
+
true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
attr_reader :container
|
22
|
+
|
23
|
+
def rules
|
24
|
+
[
|
25
|
+
:validate_name,
|
26
|
+
:validate_version,
|
27
|
+
:validate_dockerfile
|
28
|
+
]
|
29
|
+
end
|
30
|
+
|
31
|
+
def validate_name
|
32
|
+
'Name is invalid' if container.name.nil? || !container.name.to_s.match(/[\w\d]{3,}/)
|
33
|
+
end
|
34
|
+
|
35
|
+
def validate_version
|
36
|
+
'Version is invalid' if container.version.nil? || !container.version.to_s.match(/[\w\d]{1,}/)
|
37
|
+
end
|
38
|
+
|
39
|
+
def validate_dockerfile
|
40
|
+
'Dockerfile is invalid' if container.dockerfile.nil? || !File.exist?(container.dockerfile)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Percheron
|
2
|
+
module Validators
|
3
|
+
class Stack
|
4
|
+
|
5
|
+
def initialize(stack)
|
6
|
+
@stack = stack
|
7
|
+
end
|
8
|
+
|
9
|
+
def valid?
|
10
|
+
message = rules.return { |rule| send(rule) }
|
11
|
+
|
12
|
+
if message
|
13
|
+
raise Errors::StackInvalid.new(message)
|
14
|
+
else
|
15
|
+
true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
attr_reader :stack
|
22
|
+
|
23
|
+
def rules
|
24
|
+
[
|
25
|
+
:validate_name
|
26
|
+
]
|
27
|
+
end
|
28
|
+
|
29
|
+
def validate_name
|
30
|
+
'Name is invalid' if stack.name.nil? || !stack.name.to_s.match(/[\w\d]{3,}/)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/percheron/version.rb
CHANGED
data/lib/percheron.rb
CHANGED
@@ -1,5 +1,19 @@
|
|
1
|
-
require
|
1
|
+
require 'hashie'
|
2
|
+
require 'forwardable'
|
3
|
+
require 'pathname'
|
4
|
+
require 'docker'
|
5
|
+
require 'naught'
|
6
|
+
|
7
|
+
require 'percheron/core_extensions'
|
8
|
+
require 'percheron/version'
|
9
|
+
require 'percheron/config'
|
10
|
+
require 'percheron/errors'
|
11
|
+
require 'percheron/config_delegator'
|
12
|
+
require 'percheron/formatters'
|
13
|
+
require 'percheron/validators'
|
14
|
+
require 'percheron/stack'
|
15
|
+
require 'percheron/container'
|
16
|
+
require 'percheron/docker_connection'
|
2
17
|
|
3
18
|
module Percheron
|
4
|
-
# Your code goes here...
|
5
19
|
end
|
data/percheron.gemspec
CHANGED
@@ -4,25 +4,28 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'percheron/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'percheron'
|
8
8
|
spec.version = Percheron::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
9
|
+
spec.authors = [ 'Ash McKenzie' ]
|
10
|
+
spec.email = [ 'ash@the-rebellion.net' ]
|
11
11
|
|
12
12
|
spec.summary = %q{Organise your Docker containers with muscle and intelligence}
|
13
|
-
spec.homepage =
|
14
|
-
spec.license =
|
13
|
+
spec.homepage = 'https://github.com/ashmckenzie/percheron'
|
14
|
+
spec.license = 'MIT'
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
-
spec.bindir =
|
17
|
+
spec.bindir = 'exe'
|
18
18
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
-
spec.require_paths = [
|
19
|
+
spec.require_paths = [ 'lib' ]
|
20
20
|
|
21
|
-
spec.add_runtime_dependency
|
22
|
-
spec.add_runtime_dependency
|
23
|
-
spec.add_runtime_dependency
|
24
|
-
spec.add_runtime_dependency
|
21
|
+
spec.add_runtime_dependency 'clamp', '~> 0.6'
|
22
|
+
spec.add_runtime_dependency 'docker-api', '~> 1.13'
|
23
|
+
spec.add_runtime_dependency 'hashie', '~> 3.2'
|
24
|
+
spec.add_runtime_dependency 'terminal-table', '~> 1.4'
|
25
|
+
spec.add_runtime_dependency 'naught', '~> 1.0'
|
25
26
|
|
26
|
-
spec.add_development_dependency
|
27
|
-
spec.add_development_dependency
|
27
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
28
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
29
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
30
|
+
spec.add_development_dependency 'simplecov', '~> 0.9'
|
28
31
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: percheron
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ash McKenzie
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-02-
|
11
|
+
date: 2015-02-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: clamp
|
@@ -66,20 +66,34 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '1.4'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: naught
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: bundler
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
72
86
|
requirements:
|
73
87
|
- - "~>"
|
74
88
|
- !ruby/object:Gem::Version
|
75
|
-
version: '1.
|
89
|
+
version: '1.7'
|
76
90
|
type: :development
|
77
91
|
prerelease: false
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
80
94
|
- - "~>"
|
81
95
|
- !ruby/object:Gem::Version
|
82
|
-
version: '1.
|
96
|
+
version: '1.7'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: rake
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,6 +108,34 @@ dependencies:
|
|
94
108
|
- - "~>"
|
95
109
|
- !ruby/object:Gem::Version
|
96
110
|
version: '10.0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rspec
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '3.0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '3.0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: simplecov
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0.9'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0.9'
|
97
139
|
description:
|
98
140
|
email:
|
99
141
|
- ash@the-rebellion.net
|
@@ -109,8 +151,36 @@ files:
|
|
109
151
|
- LICENSE.txt
|
110
152
|
- README.md
|
111
153
|
- Rakefile
|
112
|
-
- bin/
|
154
|
+
- bin/percheron
|
113
155
|
- lib/percheron.rb
|
156
|
+
- lib/percheron/cli.rb
|
157
|
+
- lib/percheron/cli/abstract_command.rb
|
158
|
+
- lib/percheron/cli/console_command.rb
|
159
|
+
- lib/percheron/cli/list_command.rb
|
160
|
+
- lib/percheron/cli/main_command.rb
|
161
|
+
- lib/percheron/cli/start_command.rb
|
162
|
+
- lib/percheron/cli/stop_command.rb
|
163
|
+
- lib/percheron/config.rb
|
164
|
+
- lib/percheron/config_delegator.rb
|
165
|
+
- lib/percheron/container.rb
|
166
|
+
- lib/percheron/container/actions.rb
|
167
|
+
- lib/percheron/container/actions/build.rb
|
168
|
+
- lib/percheron/container/actions/create.rb
|
169
|
+
- lib/percheron/container/actions/start.rb
|
170
|
+
- lib/percheron/container/actions/stop.rb
|
171
|
+
- lib/percheron/container/main.rb
|
172
|
+
- lib/percheron/container/null.rb
|
173
|
+
- lib/percheron/core_extensions.rb
|
174
|
+
- lib/percheron/docker_connection.rb
|
175
|
+
- lib/percheron/errors.rb
|
176
|
+
- lib/percheron/formatters.rb
|
177
|
+
- lib/percheron/formatters/stack.rb
|
178
|
+
- lib/percheron/formatters/stack/table.rb
|
179
|
+
- lib/percheron/stack.rb
|
180
|
+
- lib/percheron/validators.rb
|
181
|
+
- lib/percheron/validators/config.rb
|
182
|
+
- lib/percheron/validators/container.rb
|
183
|
+
- lib/percheron/validators/stack.rb
|
114
184
|
- lib/percheron/version.rb
|
115
185
|
- percheron.gemspec
|
116
186
|
homepage: https://github.com/ashmckenzie/percheron
|
@@ -133,7 +203,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
133
203
|
version: '0'
|
134
204
|
requirements: []
|
135
205
|
rubyforge_project:
|
136
|
-
rubygems_version: 2.
|
206
|
+
rubygems_version: 2.4.5
|
137
207
|
signing_key:
|
138
208
|
specification_version: 4
|
139
209
|
summary: Organise your Docker containers with muscle and intelligence
|
data/bin/console
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "bundler/setup"
|
4
|
-
require "percheron"
|
5
|
-
|
6
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
-
# with your gem easier. You can also use a different console, if you like.
|
8
|
-
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
# require "pry"
|
11
|
-
# Pry.start
|
12
|
-
|
13
|
-
require "irb"
|
14
|
-
IRB.start
|