taketo 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/Gemfile +1 -1
  2. data/Gemfile.lock +3 -3
  3. data/README.md +20 -7
  4. data/VERSION +1 -1
  5. data/bin/taketo +30 -31
  6. data/features/config.feature +33 -3
  7. data/features/config_validation.feature +29 -4
  8. data/features/connect_to_server.feature +2 -1
  9. data/features/error_handling.feature +1 -0
  10. data/features/help.feature +5 -3
  11. data/features/support/env.rb +2 -0
  12. data/lib/taketo/associated_nodes.rb +90 -0
  13. data/lib/taketo/commands/ssh_command.rb +0 -4
  14. data/lib/taketo/config_printer_visitor.rb +68 -0
  15. data/lib/taketo/config_traverser.rb +55 -0
  16. data/lib/taketo/config_validator.rb +33 -34
  17. data/lib/taketo/config_visitor.rb +29 -0
  18. data/lib/taketo/constructs/base_construct.rb +9 -54
  19. data/lib/taketo/constructs/command.rb +9 -1
  20. data/lib/taketo/constructs/environment.rb +11 -1
  21. data/lib/taketo/constructs/project.rb +5 -0
  22. data/lib/taketo/constructs/server.rb +7 -4
  23. data/lib/taketo/dsl.rb +13 -2
  24. data/lib/taketo/support/named_nodes_collection.rb +10 -0
  25. data/lib/taketo/support.rb +0 -1
  26. data/lib/taketo.rb +2 -1
  27. data/spec/integration/dsl_integration_spec.rb +1 -1
  28. data/spec/lib/taketo/associated_nodes_spec.rb +101 -0
  29. data/spec/lib/taketo/commands/ssh_command_spec.rb +5 -15
  30. data/spec/lib/taketo/config_printer_visitor_spec.rb +112 -0
  31. data/spec/lib/taketo/config_traverser_spec.rb +63 -0
  32. data/spec/lib/taketo/config_validator_spec.rb +53 -39
  33. data/spec/lib/taketo/config_visitor_spec.rb +51 -0
  34. data/spec/lib/taketo/constructs/base_construct_spec.rb +7 -70
  35. data/spec/lib/taketo/constructs/command_spec.rb +20 -8
  36. data/spec/lib/taketo/constructs/config_spec.rb +2 -6
  37. data/spec/lib/taketo/constructs/environment_spec.rb +12 -7
  38. data/spec/lib/taketo/constructs/project_spec.rb +10 -4
  39. data/spec/lib/taketo/constructs/server_spec.rb +25 -16
  40. data/spec/lib/taketo/constructs_factory_spec.rb +12 -12
  41. data/spec/lib/taketo/destination_resolver_spec.rb +32 -32
  42. data/spec/lib/taketo/dsl_spec.rb +115 -98
  43. data/spec/lib/taketo/support/named_nodes_collection_spec.rb +49 -21
  44. data/spec/support/helpers/construct_spec_helper.rb +5 -5
  45. data/spec/support/matchers/be_appropriate_construct_matcher.rb +9 -1
  46. data/spec/support/matchers/have_accessor_matcher.rb +6 -3
  47. metadata +18 -7
  48. data/lib/taketo/config_printer.rb +0 -84
  49. data/lib/taketo/support/eval_delegator.rb +0 -25
  50. data/spec/lib/taketo/config_printer_spec.rb +0 -116
  51. data/spec/lib/taketo/support/eval_delegator_spec.rb +0 -43
data/Gemfile CHANGED
@@ -1,7 +1,7 @@
1
1
  source :rubygems
2
2
 
3
3
  group :development do
4
- gem "rspec", "~> 2.10"
4
+ gem "rspec", "~> 2.11"
5
5
  gem "rake", "~> 0.9"
6
6
  gem "aruba", "~> 0.4"
7
7
  gem 'simplecov', '~> 0.6', :require => false
data/Gemfile.lock CHANGED
@@ -26,9 +26,9 @@ GEM
26
26
  rspec-expectations (~> 2.11.0)
27
27
  rspec-mocks (~> 2.11.0)
28
28
  rspec-core (2.11.1)
29
- rspec-expectations (2.11.1)
29
+ rspec-expectations (2.11.3)
30
30
  diff-lcs (~> 1.1.3)
31
- rspec-mocks (2.11.1)
31
+ rspec-mocks (2.11.3)
32
32
  simplecov (0.6.4)
33
33
  multi_json (~> 1.0)
34
34
  simplecov-html (~> 0.5.3)
@@ -40,5 +40,5 @@ PLATFORMS
40
40
  DEPENDENCIES
41
41
  aruba (~> 0.4)
42
42
  rake (~> 0.9)
43
- rspec (~> 2.10)
43
+ rspec (~> 2.11)
44
44
  simplecov (~> 0.6)
data/README.md CHANGED
@@ -92,6 +92,10 @@ You can use shared server configs to reduce duplication:
92
92
  end
93
93
  end
94
94
 
95
+ shared_server_config :some_other_shared_config do |folder|
96
+ location File.join("/var", folder)
97
+ end
98
+
95
99
  project :my_project do
96
100
  environment :staging do
97
101
  server :s1 do
@@ -101,7 +105,7 @@ You can use shared server configs to reduce duplication:
101
105
 
102
106
  server :s2 do
103
107
  host :s2 do
104
- include_shared_server_config(:my_staging)
108
+ include_shared_server_config(:my_staging => [], :some_other_shared_config => "qux")
105
109
  end
106
110
  end
107
111
  end
@@ -109,15 +113,24 @@ You can use shared server configs to reduce duplication:
109
113
 
110
114
  This will give you ```console``` commands available both on s1 and s2
111
115
 
112
- To-Do:
113
- ------
114
-
115
- * Add support for defaults
116
- * Add support for generating shortcuts
117
-
118
116
  The Changelog:
119
117
  --------------
120
118
 
119
+ ### v0.0.7 (08.10.2012) ###
120
+ * Add ability to include several shared server config at once
121
+ Use hash as include_shared_server_config parameter to include
122
+ multiple shared server configs with arguments, like:
123
+ ```ruby
124
+ include_shared_server_config(:foo => :some_arg, :bar => [:arg1, :arg2])
125
+ ```
126
+ or just enumerate them if no arguments needed:
127
+ ```ruby
128
+ include_shared_server_configs(:baz, :quux)
129
+ ```
130
+
131
+ NOTE: This change will break your config if you've used parametrized
132
+ shared server configs before; rewrite them using hash-form
133
+
121
134
  ### v0.0.6 (26.07.2012) ###
122
135
  * Add identity_file server config option
123
136
  * Add shared server config support
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.6
1
+ 0.0.7
data/bin/taketo CHANGED
@@ -1,21 +1,16 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ if ENV["TAKETO_DEV"]
4
+ $:.unshift(File.expand_path('../../lib', __FILE__))
5
+ end
6
+
3
7
  require 'rubygems'
4
8
 
5
- begin
6
- require 'taketo'
7
- require 'taketo/constructs_factory'
8
- require 'taketo/commands'
9
- require 'taketo/destination_resolver'
10
- require 'optparse'
11
- rescue LoadError => e #development
12
- raise if $loaded
13
- $:.unshift File.expand_path('../../lib', __FILE__)
14
- require 'bundler'
15
- Bundler.setup(:default)
16
- $loaded = true
17
- retry
18
- end
9
+ require 'taketo'
10
+ require 'taketo/constructs_factory'
11
+ require 'taketo/commands'
12
+ require 'taketo/destination_resolver'
13
+ require 'optparse'
19
14
 
20
15
  Signal.trap("SIGINT") do
21
16
  puts "Terminating"
@@ -39,7 +34,7 @@ def parse_options
39
34
  options[:config] = c
40
35
  end
41
36
 
42
- opts.on("-c COMMAND", "--command", "Command to execute on destination server",
37
+ opts.on("-c COMMAND", "--command", "Command to execute on destination server",
43
38
  " (COMMAND either declared in config or passed as an argument)") do |c|
44
39
  raise OptionParser::MissingArgument if String(c).strip.empty?
45
40
  options[:command] = c
@@ -71,21 +66,28 @@ end
71
66
 
72
67
  def parse_config(config)
73
68
  DSL.new.configure(config).tap do |config|
74
- ConfigValidator.new(config).validate!
69
+ traverser = ConfigTraverser.new(config)
70
+ ConfigValidator.new(traverser).validate!
75
71
  end
76
72
  end
77
73
 
78
74
  def remote_command(server, options)
79
- command_name = options[:command] or return default_command(options)
80
- server.find(:command, command_name.to_sym) { default_command(options) }
75
+ command = options[:command]
76
+ if String(command).empty?
77
+ server.default_command
78
+ else
79
+ server.find_command(command.to_sym) || Constructs::Command.explicit_command(command)
80
+ end
81
81
  end
82
82
 
83
- def default_command(options)
84
- cmd = Constructs::Command.new(:default)
85
- cmd.command = options.fetch(:command) { "bash" }
86
- cmd
83
+ def execute(shell_command, options)
84
+ if options[:dry_run]
85
+ puts shell_command
86
+ else
87
+ system shell_command
88
+ end
87
89
  end
88
-
90
+
89
91
  begin
90
92
  options = parse_options
91
93
  config = parse_config(options[:config])
@@ -94,18 +96,15 @@ begin
94
96
 
95
97
  if options.delete(:view)
96
98
  node = resolver.get_node
97
- puts ConfigPrinter.new.render(node).chomp
99
+ traverser = ConfigTraverser.new(node)
100
+ config_printer = ConfigPrinterVisitor.new
101
+ traverser.visit_depth_first(config_printer)
102
+ puts config_printer.result
98
103
  else
99
104
  server = resolver.resolve
100
-
101
105
  server_command = remote_command(server, options)
102
106
  command_to_execute = Commands::SSHCommand.new(server).render(server_command.render(server, options))
103
-
104
- if options[:dry_run]
105
- puts command_to_execute
106
- else
107
- system command_to_execute
108
- end
107
+ execute(command_to_execute, options)
109
108
  end
110
109
  rescue SystemExit
111
110
  # Do nothing
@@ -6,20 +6,50 @@ Feature: taketo config
6
6
  Scenario: Shared server configs
7
7
  When I have the following config in "/tmp/taketo_test_cfg.rb"
8
8
  """
9
- shared_server_config :shared_config_example do
9
+ shared_server_config :sc1 do
10
10
  port 9999 #can contain any server config options
11
11
  end
12
12
 
13
+ shared_server_config :sc2 do
14
+ location "/var/qux"
15
+ end
16
+
17
+ project :slots do
18
+ environment :staging do
19
+ server(:s1) { host "1.2.3.4"; include_shared_server_config(:sc1, :sc2) }
20
+ end
21
+ end
22
+ """
23
+ And I successfully run `taketo slots:staging:s1 --config=/tmp/taketo_test_cfg.rb --dry-run`
24
+ Then the output should contain
25
+ """
26
+ ssh -t -p 9999 1.2.3.4 "cd /var/qux; RAILS_ENV=staging bash"
27
+ """
28
+
29
+ Scenario: Shared server configs with arguments
30
+ When I have the following config in "/tmp/taketo_test_cfg.rb"
31
+ """
32
+ shared_server_config :sc_args1 do |port_num|
33
+ port port_num
34
+ end
35
+
36
+ shared_server_config :sc_args2 do |projects_root_folder, project_folder|
37
+ location File.join(projects_root_folder, project_folder)
38
+ end
39
+
13
40
  project :slots do
14
41
  environment :staging do
15
- server(:s1) { host "1.2.3.4"; include_shared_server_config(:shared_config_example) }
42
+ server(:s1) do
43
+ host "1.2.3.4"
44
+ include_shared_server_config(:sc_args1 => 9999, :sc_args2 => ['/var', 'qux'])
45
+ end
16
46
  end
17
47
  end
18
48
  """
19
49
  And I successfully run `taketo slots:staging:s1 --config=/tmp/taketo_test_cfg.rb --dry-run`
20
50
  Then the output should contain
21
51
  """
22
- ssh -t -p 9999 1.2.3.4 "RAILS_ENV=staging bash"
52
+ ssh -t -p 9999 1.2.3.4 "cd /var/qux; RAILS_ENV=staging bash"
23
53
  """
24
54
 
25
55
  Scenario: Set environment variables
@@ -12,7 +12,32 @@ Feature:
12
12
  Then the stderr should contain "<error>"
13
13
 
14
14
  Examples:
15
- | config | error |
16
- | | There are no projects. Add some to your config (~/.taketo.rc.rb by default) |
17
- | project(:foo) {} | There is no environments for the following projects: foo |
18
- | project(:foo) { environment(:bar) {}} | There is no servers for the following environments in project foo: bar |
15
+ | config | error |
16
+ | | There are no projects. Add some to your config (~/.taketo.rc.rb by default) |
17
+ | project(:foo) {} | Project foo: no environments |
18
+ | project(:foo) { environment(:bar) {} } | Environment foo:bar: no servers |
19
+ | project(:foo) { environment(:bar) { server {} }} | Server foo:bar:default: host is not defined |
20
+
21
+ Scenario: Global server alias clash
22
+ When I have the following config in "/tmp/taketo_test_cfg.rb"
23
+ """
24
+ project(:foo) { environment(:bar) {
25
+ server(:s1) { host '1.2.3.4'; global_alias :a1 }
26
+ server(:s2) { host '2.3.4.5'; global_alias :a1 }
27
+ }}
28
+ """
29
+ And I run `taketo --config=/tmp/taketo_test_cfg.rb`
30
+ Then the stderr should contain "Server foo:bar:s2: global alias 'a1' has already been taken by server foo:bar:s1"
31
+
32
+ Scenario: Bad command
33
+ When I have the following config in "/tmp/taketo_test_cfg.rb"
34
+ """
35
+ project(:foo) { environment(:bar) {
36
+ server(:s1) { host '1.2.3.4'
37
+ command(:foo) {}
38
+ }
39
+ }}
40
+ """
41
+ And I run `taketo --config=/tmp/taketo_test_cfg.rb`
42
+ Then the stderr should contain "Don't know what to execute on command foo"
43
+
@@ -1,4 +1,4 @@
1
- Feature:
1
+ Feature:
2
2
  In order to be able to access my servers quickly
3
3
  As a developer
4
4
  I want to have a nifty little utility
@@ -53,6 +53,7 @@ Feature:
53
53
  project :slots do
54
54
  environment :staging do
55
55
  server :s1 do
56
+ host "1.2.3.4"
56
57
  end
57
58
  server :s2 do
58
59
  host "2.3.4.5"
@@ -10,6 +10,7 @@ Feature:
10
10
  project :slots do
11
11
  environment :staging do
12
12
  server :s1 do
13
+ host "1.2.3.4"
13
14
  end
14
15
  server :s2 do
15
16
  host "2.3.4.5"
@@ -37,6 +37,7 @@ Feature:
37
37
  When I run `taketo --config=/tmp/taketo_test_cfg.rb --view`
38
38
  Then the output should contain exactly:
39
39
  """
40
+
40
41
  Project: foo
41
42
  Environment: bar
42
43
  Server: default
@@ -45,16 +46,16 @@ Feature:
45
46
  User: pivo
46
47
  Default location: /var/apps/vodka
47
48
  Environment: RAILS_ENV=bar
48
-
49
+ (No commands)
50
+
49
51
  Project: baz
50
52
  Environment: qux
51
53
  Server: bart
52
54
  Host: 2.3.4.5
53
55
  Environment: RAILS_ENV=qux
54
- Commands:
55
56
  console
56
57
  killall - Kill ALL humans
57
-
58
+
58
59
  """
59
60
 
60
61
  Scenario: View particular server
@@ -67,5 +68,6 @@ Feature:
67
68
  User: pivo
68
69
  Default location: /var/apps/vodka
69
70
  Environment: RAILS_ENV=bar
71
+ (No commands)
70
72
 
71
73
  """
@@ -4,6 +4,8 @@ require 'aruba/cucumber'
4
4
  require 'fileutils'
5
5
 
6
6
  ENV['PATH'] = "#{File.expand_path('../../../bin', __FILE__)}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
7
+ ENV['RUBYOPT'] = '-Ilib'
8
+ ENV["TAKETO_DEV"] = 'true'
7
9
 
8
10
  After do
9
11
  if defined? @config_path and File.exist?(@config_path)
@@ -0,0 +1,90 @@
1
+ module Taketo
2
+ class NodesNotDefinedError < StandardError; end
3
+
4
+ module AssociatedNodes
5
+
6
+ def self.included(base)
7
+ base.extend ClassMethods
8
+ base.send(:include, InstanceMethods)
9
+ end
10
+
11
+ module ClassMethods
12
+ ##
13
+ # Adds nodes collections to the construct
14
+ #
15
+ # Example:
16
+ #
17
+ # class Bar < BaseConstruct
18
+ # has_nodes :foos, :foo
19
+ # end
20
+ #
21
+ # bar = Bar.new
22
+ # bar.foos # => foos collection
23
+ # bar.append_foo(foo) # adds node the collection
24
+ # bar.find_foo(:foo_name) # find foo in foos collection by name
25
+ #
26
+ def has_nodes(name_plural, name_singular)
27
+ self.node_types << name_plural
28
+ includable = Module.new do
29
+ define_method "append_#{name_singular}" do |object|
30
+ nodes(name_plural) << object
31
+ end
32
+
33
+ define_method "find_#{name_singular}" do |name|
34
+ nodes(name_plural).find_by_name(name)
35
+ end
36
+
37
+ define_method name_plural do
38
+ nodes(name_plural)
39
+ end
40
+
41
+ define_method "has_#{name_plural}?" do
42
+ nodes(name_plural).any?
43
+ end
44
+ end
45
+ self.send(:include, includable)
46
+ end
47
+
48
+ def node_types
49
+ @node_types ||= []
50
+ end
51
+ end
52
+
53
+ module InstanceMethods
54
+ def initialize(*args)
55
+ @nodes = {}
56
+ end
57
+
58
+ def find(singular_node_name, name)
59
+ send("find_#{singular_node_name}", name) or
60
+ if block_given?
61
+ yield
62
+ else
63
+ raise KeyError, "#{singular_node_name} #{name} not found for #{qualified_name}"
64
+ end
65
+ end
66
+
67
+ def nodes(name_plural)
68
+ unless self.class.node_types.include?(name_plural)
69
+ raise NodesNotDefinedError, "#{name_plural} not defined for #{qualified_name}"
70
+ end
71
+ @nodes[name_plural] ||= Taketo::Support::NamedNodesCollection.new
72
+ end
73
+
74
+ def has_nodes?(name_plural)
75
+ unless self.class.node_types.include?(name_plural)
76
+ raise NodesNotDefinedError, "#{name_plural} not defined for #{qualified_name}"
77
+ end
78
+ @nodes.fetch(name_plural) { [] }.any?
79
+ end
80
+
81
+ ##
82
+ # Ovverride for better error messages
83
+ def qualified_name
84
+ self.class.name
85
+ end
86
+ end
87
+
88
+ end
89
+ end
90
+
@@ -9,7 +9,6 @@ module Taketo
9
9
 
10
10
  def initialize(server, options = {})
11
11
  @server = server
12
- @environment = @server.environment
13
12
  end
14
13
 
15
14
  def render(rendered_command)
@@ -17,9 +16,6 @@ module Taketo
17
16
  end
18
17
 
19
18
  def host
20
- unless @server.host
21
- raise ArgumentError, "host for server #{@server.name} in #{@environment.name} is not defined!"
22
- end
23
19
  shellescape @server.host
24
20
  end
25
21
 
@@ -0,0 +1,68 @@
1
+ require 'taketo/config_visitor'
2
+
3
+ module Taketo
4
+ class ConfigPrinterVisitor < ConfigVisitor
5
+ def initialize
6
+ @indent_level = 0
7
+ @result = ""
8
+ end
9
+
10
+ visit Config do |config|
11
+ indent(0) do
12
+ if config.has_projects?
13
+ put "Default destination: #{config.default_destination}" if config.default_destination
14
+ else
15
+ put "There are no projects yet..."
16
+ end
17
+ end
18
+ end
19
+
20
+ visit Project do |project|
21
+ put
22
+ indent(0) do
23
+ put "Project: #{project.name}"
24
+ indent { put "(No environments)" unless project.has_environments? }
25
+ end
26
+ end
27
+
28
+ visit Environment do |environment|
29
+ indent(1) do
30
+ put "Environment: #{environment.name}"
31
+ indent { put "(No servers)" unless environment.has_servers? }
32
+ end
33
+ end
34
+
35
+ visit Server do |server|
36
+ indent(2) do
37
+ put "Server: #{server.name}"
38
+ indent do
39
+ put "Host: #{server.host}"
40
+ put "Port: #{server.port}" if server.port
41
+ put "User: #{server.username}" if server.username
42
+ put "Default location: #{server.default_location}" if server.default_location
43
+ put "Environment: " + server.environment_variables.map { |n, v| "#{n}=#{v}" }.join(" ")
44
+ indent { put "(No commands)" unless server.has_commands? }
45
+ end
46
+ end
47
+ end
48
+
49
+ visit Command do |command|
50
+ indent(4) { put command.name.to_s + (" - " + command.description if command.description).to_s }
51
+ end
52
+
53
+ def result
54
+ @result.chomp
55
+ end
56
+
57
+ def indent(level = nil)
58
+ @first_indent ||= level || @indent_level
59
+ level ? @indent_level = level - @first_indent : @indent_level += 1
60
+ yield
61
+ end
62
+
63
+ def put(str = nil)
64
+ @result += (" " * @indent_level + str.to_s).rstrip.chomp + "\n"
65
+ end
66
+ end
67
+ end
68
+
@@ -0,0 +1,55 @@
1
+ require 'delegate'
2
+ require 'forwardable'
3
+
4
+ module Taketo
5
+ class ConfigTraverser
6
+ LEVELS = { :config => 1, :project => 2, :environment => 3, :server => 4, :command => 5 }.freeze
7
+ PLURALS = { :config => :configs, :project => :projects, :environment => :environments, :server => :servers, :command => :commands }.freeze
8
+
9
+ class NodeWithPath < SimpleDelegator
10
+ extend Forwardable
11
+ def_delegators :__getobj__, :class, :ancestors, :== # avoid ancestry tree troubles
12
+
13
+ attr_reader :path
14
+
15
+ def initialize(node, path_string)
16
+ super(node)
17
+ @path = path_string.freeze
18
+ end
19
+ end
20
+
21
+ def initialize(root)
22
+ @root = root
23
+ end
24
+
25
+ def visit_depth_first(visitor)
26
+ parents = []
27
+ path_stack = [wrap_node_with_path(parents, @root)]
28
+
29
+ while path_stack.any?
30
+ node = path_stack.pop
31
+ visitor.visit(node)
32
+
33
+ next_level_node_type = LEVELS.respond_to?(:key) ? LEVELS.key(LEVELS[node.node_type] + 1) : LEVELS.index(LEVELS[node.node_type] + 1)
34
+ if next_level_node_type && node.has_nodes?(PLURALS[next_level_node_type])
35
+ parents << node
36
+ node.nodes(PLURALS[next_level_node_type]).reverse_each do |n|
37
+ n = wrap_node_with_path(parents, n)
38
+ path_stack.push(n)
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def wrap_node_with_path(parents, node)
47
+ NodeWithPath.new(node, path(parents, node))
48
+ end
49
+
50
+ def path(parents, node)
51
+ parents.map(&:name).reject(&:nil?).concat([node.name]).join(":")
52
+ end
53
+ end
54
+ end
55
+
@@ -1,53 +1,52 @@
1
+ require 'taketo/config_traverser'
2
+ require 'taketo/config_visitor'
3
+
1
4
  module Taketo
2
5
  class ConfigError < StandardError; end
3
6
 
4
- class ConfigValidator
5
- def initialize(config)
6
- @config = config
7
+ class ConfigValidatorVisitor < ConfigVisitor
8
+ def initialize
9
+ @global_server_aliases = {}
7
10
  end
8
11
 
9
- def validate!
10
- ensure_projects_exist
11
- ensure_environments_exist
12
- ensure_servers_exist
13
- ensure_global_server_aliases_unique
12
+ visit Config do |c|
13
+ raise ConfigError, "There are no projects. Add some to your config (~/.taketo.rc.rb by default)" unless c.has_projects?
14
14
  end
15
15
 
16
- private
17
-
18
- def ensure_projects_exist
19
- if @config.projects.empty?
20
- raise ConfigError,
21
- "There are no projects. Add some to your config (~/.taketo.rc.rb by default)"
22
- end
16
+ visit Project do |p|
17
+ raise ConfigError, "Project #{p.path}: no environments" unless p.has_environments?
23
18
  end
24
19
 
25
- def ensure_environments_exist
26
- projects_without_environments = @config.projects.select { |project| project.environments.empty? }
27
- if projects_without_environments.any?
28
- raise ConfigError,
29
- "There is no environments for the following projects: #{projects_without_environments.map(&:name).join(", ")}"
30
- end
20
+ visit Environment do |e|
21
+ raise ConfigError, "Environment #{e.path}: no servers" unless e.has_servers?
31
22
  end
32
23
 
33
- def ensure_servers_exist
34
- @config.projects.each do |project|
35
- environments_without_servers = project.environments.select { |environment| environment.servers.empty? }
36
- if environments_without_servers.any?
37
- raise ConfigError,
38
- "There is no servers for the following environments in project #{project.name}: #{environments_without_servers.map(&:name).join(", ")}"
24
+ visit Server do |s|
25
+ if !String(s.global_alias).empty?
26
+ if @global_server_aliases.key?(s.global_alias)
27
+ raise ConfigError, "Server #{s.path}: global alias '#{s.global_alias}' has already been taken by server #{@global_server_aliases[s.global_alias].path}"
28
+ else
29
+ @global_server_aliases[s.global_alias] = s
39
30
  end
40
31
  end
32
+
33
+ raise ConfigError, "Server #{s.path}: host is not defined" if String(s.host).empty?
41
34
  end
42
35
 
43
- def ensure_global_server_aliases_unique
44
- aliases = @config.projects.map { |p| p.environments.map { |e| e.servers.map(&:global_alias) } }.flatten
45
- non_uniq_aliases = aliases.reject(&:nil?).group_by(&:to_sym).reject { |k, v| v.size == 1 }
46
- unless non_uniq_aliases.empty?
47
- raise ConfigError,
48
- "There are duplicates among global server aliases: #{non_uniq_aliases.keys.join(", ")}"
49
- end
36
+ visit Command do |c|
37
+ raise ConfigError, "Don't know what to execute on command #{c.name}" if String(c.command).empty?
50
38
  end
51
39
  end
40
+
41
+ class ConfigValidator
42
+ def initialize(traverser)
43
+ @traverser = traverser
44
+ end
45
+
46
+ def validate!
47
+ @traverser.visit_depth_first(ConfigValidatorVisitor.new)
48
+ end
49
+ end
50
+
52
51
  end
53
52