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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +6 -1
  4. data/Gemfile +9 -1
  5. data/README.md +12 -11
  6. data/Rakefile +3 -1
  7. data/bin/percheron +26 -0
  8. data/lib/percheron/cli/abstract_command.rb +19 -0
  9. data/lib/percheron/cli/console_command.rb +10 -0
  10. data/lib/percheron/cli/list_command.rb +12 -0
  11. data/lib/percheron/cli/main_command.rb +10 -0
  12. data/lib/percheron/cli/start_command.rb +12 -0
  13. data/lib/percheron/cli/stop_command.rb +12 -0
  14. data/lib/percheron/cli.rb +13 -0
  15. data/lib/percheron/config.rb +45 -0
  16. data/lib/percheron/config_delegator.rb +12 -0
  17. data/lib/percheron/container/actions/build.rb +35 -0
  18. data/lib/percheron/container/actions/create.rb +38 -0
  19. data/lib/percheron/container/actions/start.rb +40 -0
  20. data/lib/percheron/container/actions/stop.rb +26 -0
  21. data/lib/percheron/container/actions.rb +11 -0
  22. data/lib/percheron/container/main.rb +88 -0
  23. data/lib/percheron/container/null.rb +15 -0
  24. data/lib/percheron/container.rb +8 -0
  25. data/lib/percheron/core_extensions.rb +21 -0
  26. data/lib/percheron/docker_connection.rb +43 -0
  27. data/lib/percheron/errors.rb +9 -0
  28. data/lib/percheron/formatters/stack/table.rb +44 -0
  29. data/lib/percheron/formatters/stack.rb +8 -0
  30. data/lib/percheron/formatters.rb +7 -0
  31. data/lib/percheron/stack.rb +66 -0
  32. data/lib/percheron/validators/config.rb +48 -0
  33. data/lib/percheron/validators/container.rb +44 -0
  34. data/lib/percheron/validators/stack.rb +35 -0
  35. data/lib/percheron/validators.rb +8 -0
  36. data/lib/percheron/version.rb +1 -1
  37. data/lib/percheron.rb +16 -2
  38. data/percheron.gemspec +16 -13
  39. metadata +76 -6
  40. data/bin/console +0 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 91b2de939cc7e2fdd3038bc4b42cda47df2afe97
4
- data.tar.gz: eee84384ece0df20de06174161e599cd65ae9d2a
3
+ metadata.gz: 2aa1dd04adcc5a0570327825d599e9a0f9a9af87
4
+ data.tar.gz: c279d99e8dacd85601293984d6653764db508d4c
5
5
  SHA512:
6
- metadata.gz: 1b6d30e19bceb46b10d8feeb6faf6a5a9b6b60b30feb7b57fc09f682c1f26dd3976c1b8aeacede15ba452595d9b78b308bedafd755dc75b08d13d64cec6ee5dc
7
- data.tar.gz: 07e6182862a857c1213bc13715d6e4f65945eaafa991ed65212988a271c182f5458b74398d25e6bc88226b34a6ec2887961b07db125b83be4db33daeb5f55cd7
6
+ metadata.gz: 16994a1a63a0e6f468d9619113abfda4b0cbf7d7501feb510d8cf814ff24d7dd704f1d56b744105edc7b58e379a631460da8a7f3780746d571cc84144c844c18
7
+ data.tar.gz: e75b7829e7572a6864a5301afe36c408d109717c9681a9a8ec336e100c5f55145bb5ae79bf4cb07b2aef08c5edf003e885d1b7d701e90c8719626a8932f377f5
data/.gitignore CHANGED
@@ -7,3 +7,5 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ .ruby-version
11
+ percheron.sublime-*
data/.travis.yml CHANGED
@@ -1,3 +1,8 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.3.0
3
+ - 2.2
4
+ branches:
5
+ only:
6
+ - master
7
+ - wip
8
+ script: bundle exec rake spec
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
- Organise your Docker containers with muscle and intelligence.
3
+ [![Gem Version](https://badge.fury.io/rb/percheron.svg)](http://badge.fury.io/rb/percheron)
4
+ [![Travis CI](https://travis-ci.org/ashmckenzie/percheron.svg)](https://travis-ci.org/ashmckenzie/percheron)
5
+ [![Code Climate GPA](https://codeclimate.com/github/ashmckenzie/percheron/badges/gpa.svg)](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
- $ bundle
19
+ ```shell
20
+ $ bundle install
21
+ ```
17
22
 
18
23
  Or install it yourself as:
19
24
 
20
- $ gem install percheron
25
+ ```shell
26
+ $ gem install percheron
27
+ ```
21
28
 
22
29
  ## Usage
23
30
 
24
- TODO: Write usage instructions here
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/[my-github-username]/percheron/fork )
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
@@ -1,2 +1,4 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
2
3
 
4
+ RSpec::Core::RakeTask.new('spec')
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 ConsoleCommand < AbstractCommand
4
+
5
+ def execute
6
+ pry
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ module Percheron
2
+ module CLI
3
+ class ListCommand < AbstractCommand
4
+
5
+ def execute
6
+ Stack.all(config).each do |stack_name, stack|
7
+ puts Percheron::Formatters::Stack::Table.new(stack).generate
8
+ end
9
+ end
10
+ end
11
+ end
12
+ 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,12 @@
1
+ module Percheron
2
+ module CLI
3
+ class StartCommand < AbstractCommand
4
+
5
+ parameter 'STACK_NAME', 'stack name'
6
+
7
+ def execute
8
+ Percheron::Stack.new(config, stack_name).start!
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Percheron
2
+ module CLI
3
+ class StopCommand < AbstractCommand
4
+
5
+ parameter 'STACK_NAME', 'stack name'
6
+
7
+ def execute
8
+ Percheron::Stack.new(config, stack_name).stop!
9
+ end
10
+ end
11
+ end
12
+ 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,12 @@
1
+ module Percheron
2
+ module ConfigDelegator
3
+
4
+ def def_config_item_with_default(config, default, *symbols)
5
+ symbols.each do |symbol|
6
+ define_method(symbol) do
7
+ send(config).fetch(symbol, default)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ 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,11 @@
1
+ require 'percheron/container/actions/stop'
2
+ require 'percheron/container/actions/start'
3
+ require 'percheron/container/actions/create'
4
+ require 'percheron/container/actions/build'
5
+
6
+ module Percheron
7
+ module Container
8
+ module Actions
9
+ end
10
+ end
11
+ 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,15 @@
1
+ module Percheron
2
+ module Container
3
+ Null = Naught.build do |config|
4
+ config.mimic Container
5
+
6
+ def info
7
+ {}
8
+ end
9
+
10
+ def kind_of?(klass)
11
+ self.class == klass
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,8 @@
1
+ require 'percheron/container/main'
2
+ require 'percheron/container/null'
3
+ require 'percheron/container/actions'
4
+
5
+ module Percheron
6
+ module Container
7
+ end
8
+ 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,8 @@
1
+ require 'percheron/formatters/stack/table'
2
+
3
+ module Percheron
4
+ module Formatters
5
+ module Stack
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ require 'terminal-table'
2
+ require 'percheron/formatters/stack'
3
+
4
+ module Percheron
5
+ module Formatters
6
+ end
7
+ 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
@@ -0,0 +1,8 @@
1
+ require 'percheron/validators/config'
2
+ require 'percheron/validators/stack'
3
+ require 'percheron/validators/container'
4
+
5
+ module Percheron
6
+ module Validators
7
+ end
8
+ end
@@ -1,3 +1,3 @@
1
1
  module Percheron
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/percheron.rb CHANGED
@@ -1,5 +1,19 @@
1
- require "percheron/version"
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 = "percheron"
7
+ spec.name = 'percheron'
8
8
  spec.version = Percheron::VERSION
9
- spec.authors = ["Ash McKenzie"]
10
- spec.email = ["ash@the-rebellion.net"]
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 = "https://github.com/ashmckenzie/percheron"
14
- spec.license = "MIT"
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 = "exe"
17
+ spec.bindir = 'exe'
18
18
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
- spec.require_paths = ["lib"]
19
+ spec.require_paths = [ 'lib' ]
20
20
 
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"
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 "bundler", "~> 1.8"
27
- spec.add_development_dependency "rake", "~> 10.0"
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.1.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-22 00:00:00.000000000 Z
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.8'
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.8'
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/console
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.2.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