corl 0.4.15 → 0.4.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +6 -1
  3. data/Gemfile.lock +76 -30
  4. data/Rakefile +1 -1
  5. data/VERSION +1 -1
  6. data/bootstrap/bootstrap.sh +5 -2
  7. data/bootstrap/os/ubuntu/00_base.sh +1 -1
  8. data/bootstrap/os/ubuntu/01_git.sh +9 -0
  9. data/bootstrap/os/ubuntu/05_ruby.sh +7 -4
  10. data/bootstrap/os/ubuntu/06_puppet.sh +2 -2
  11. data/corl.gemspec +23 -9
  12. data/lib/CORL/action/authorize.rb +2 -5
  13. data/lib/CORL/action/bootstrap.rb +1 -5
  14. data/lib/CORL/action/build.rb +2 -10
  15. data/lib/CORL/action/destroy.rb +2 -7
  16. data/lib/CORL/action/exec.rb +1 -5
  17. data/lib/CORL/action/image.rb +1 -5
  18. data/lib/CORL/action/images.rb +12 -10
  19. data/lib/CORL/action/ip.rb +21 -0
  20. data/lib/CORL/action/lookup.rb +5 -3
  21. data/lib/CORL/action/machines.rb +12 -10
  22. data/lib/CORL/action/provision.rb +4 -7
  23. data/lib/CORL/action/regions.rb +12 -10
  24. data/lib/CORL/action/seed.rb +9 -10
  25. data/lib/CORL/action/spawn.rb +29 -15
  26. data/lib/CORL/action/ssh.rb +1 -5
  27. data/lib/CORL/action/start.rb +1 -5
  28. data/lib/CORL/action/stop.rb +1 -5
  29. data/lib/CORL/action/vagrantfile.rb +55 -0
  30. data/lib/CORL/configuration/file.rb +4 -1
  31. data/lib/CORL/machine/physical.rb +1 -1
  32. data/lib/CORL/machine/vagrant.rb +358 -0
  33. data/lib/CORL/node/local.rb +2 -3
  34. data/lib/CORL/node/vagrant.rb +238 -0
  35. data/lib/core/facade.rb +25 -2
  36. data/lib/core/mixin/macro/network_settings.rb +35 -1
  37. data/lib/core/plugin/action.rb +53 -5
  38. data/lib/core/plugin/configuration.rb +19 -5
  39. data/lib/core/plugin/fog_machine.rb +1 -1
  40. data/lib/core/plugin/fog_node.rb +9 -9
  41. data/lib/core/plugin/machine.rb +6 -13
  42. data/lib/core/plugin/network.rb +23 -7
  43. data/lib/core/plugin/node.rb +69 -36
  44. data/lib/core/plugin/provisioner.rb +1 -2
  45. data/lib/core/vagrant/Vagrantfile +7 -0
  46. data/lib/core/vagrant/commands/launcher.rb +66 -0
  47. data/lib/core/vagrant/config.rb +308 -0
  48. data/lib/core/vagrant/plugins.rb +33 -0
  49. data/lib/core/vagrant/provisioner/config.rb +39 -0
  50. data/lib/core/vagrant/provisioner/provisioner.rb +46 -0
  51. data/lib/corl.rb +8 -0
  52. data/locales/en.yml +13 -1
  53. metadata +120 -59
@@ -1,5 +1,29 @@
1
1
 
2
2
  module CORL
3
+ module Vagrant
4
+
5
+ #
6
+ # Since we can execute CORL actions from within Vagrant on a combination of
7
+ # Vagrant VMs and remote server instances we need a way to tap into the
8
+ # Vagrant environment and operate on CORL configured Vagrant machines.
9
+ #
10
+ # This command is set in the CORL launcher Vagrant command plugin execute
11
+ # method. It is then accessible anywhere within CORL if we have used that
12
+ # Vagrant command as an execution gateway. If not it will be nil, giving us
13
+ # a convienient method for checking whether we are executing through Vagrant
14
+ # which is used in the CORL Vagrant {Node} and {Machine} plugins.
15
+ #
16
+ @@command = nil
17
+
18
+ def self.command=command
19
+ @@command = command
20
+ end
21
+
22
+ def self.command
23
+ @@command
24
+ end
25
+ end
26
+
3
27
  module Plugin
4
28
  class CloudAction < CORL.plugin_class(:action)
5
29
 
@@ -38,10 +62,11 @@ class CloudAction < CORL.plugin_class(:action)
38
62
  true
39
63
  end
40
64
  register :node_provider, :str, :local, 'corl.core.action.options.node_provider' do |value|
41
- value = value.to_sym
65
+ value = value.to_sym
66
+ node_providers = node_plugins.keys
42
67
 
43
- unless node_plugins.keys.include?(value)
44
- warn('corl.core.action.errors.node_provider', { :value => value, :choices => node_plugins.keys.join(", ") })
68
+ unless CORL.vagrant? || node_providers.include?(value)
69
+ warn('corl.core.action.errors.node_provider', { :value => value, :choices => node_providers.join(", ") })
45
70
  next false
46
71
  end
47
72
  true
@@ -94,7 +119,7 @@ class CloudAction < CORL.plugin_class(:action)
94
119
  #
95
120
  # A fork in the road...
96
121
  #
97
- if network.has_nodes? && ! settings[:nodes].empty?
122
+ if network && network.has_nodes? && ! settings[:nodes].empty?
98
123
  # Execute action on remote nodes
99
124
  success = network.batch(settings[:nodes], settings[:node_provider], settings[:parallel]) do |node|
100
125
  exec_config = Config.new(settings)
@@ -110,7 +135,8 @@ class CloudAction < CORL.plugin_class(:action)
110
135
  myself.status = code.batch_error unless success
111
136
  else
112
137
  # Execute statement locally
113
- node = network.local_node
138
+ node = nil
139
+ node = network.local_node if network
114
140
 
115
141
  if validate(node, network)
116
142
  yield(node, network) if block_given?
@@ -149,6 +175,28 @@ class CloudAction < CORL.plugin_class(:action)
149
175
  # Implement in sub classes if needed
150
176
  data
151
177
  end
178
+
179
+ #---
180
+
181
+ def ensure_network(network, &block)
182
+ codes :network_failure
183
+
184
+ if network
185
+ block.call
186
+ else
187
+ myself.status = code.network_failure
188
+ end
189
+ end
190
+
191
+ def ensure_node(node, &block)
192
+ codes :node_failure
193
+
194
+ if node
195
+ block.call
196
+ else
197
+ myself.status = code.node_failure
198
+ end
199
+ end
152
200
  end
153
201
  end
154
202
  end
@@ -16,11 +16,13 @@ class Configuration < CORL.plugin_class(:base)
16
16
 
17
17
  logger.info("Setting source configuration project")
18
18
  @project = CORL.project(extended_config(:project, {
19
- :directory => _delete(:directory, Dir.pwd),
20
- :url => _delete(:url),
21
- :revision => _delete(:revision),
22
- :create => _delete(:create, false),
23
- :pull => true
19
+ :directory => _delete(:directory, Dir.pwd),
20
+ :url => _delete(:url),
21
+ :revision => _delete(:revision),
22
+ :create => _delete(:create, false),
23
+ :pull => true,
24
+ :internal_ip => CORL.public_ip, # Needed for seeding Vagrant VMs
25
+ :manage_ignore => true
24
26
  }), _delete(:project_provider))
25
27
 
26
28
  _init(:autoload, true)
@@ -53,6 +55,18 @@ class Configuration < CORL.plugin_class(:base)
53
55
 
54
56
  #---
55
57
 
58
+ def cache
59
+ project.cache
60
+ end
61
+
62
+ #---
63
+
64
+ def ignore(files)
65
+ project.ignore(files)
66
+ end
67
+
68
+ #---
69
+
56
70
  def autoload(default = false)
57
71
  _get(:autoload, default)
58
72
  end
@@ -149,7 +149,7 @@ class Fog < CORL.plugin_class(:machine)
149
149
 
150
150
  def download(remote_path, local_path, options = {})
151
151
  super do |config, success|
152
- logger.debug("Executing SCP download to #{local_path} from #{remote_path} on machine #{name}")
152
+ logger.debug("Executing SCP download to #{local_path} from #{remote_path} on machine #{plugin_name}")
153
153
 
154
154
  begin
155
155
  if init_ssh_session(server)
@@ -11,15 +11,15 @@ class Fog < CORL.plugin_class(:node)
11
11
  # Node plugin interface
12
12
 
13
13
  def normalize(reload)
14
- super do
15
- myself.region = region
14
+ super
15
+
16
+ myself.region = region
16
17
 
17
- unless reload
18
- machine_provider = :fog
19
- machine_provider = yield if block_given?
18
+ unless reload
19
+ machine_provider = :fog
20
+ machine_provider = yield if block_given?
20
21
 
21
- myself.machine = create_machine(:machine, machine_provider, machine_config)
22
- end
22
+ myself.machine = create_machine(:machine, machine_provider, machine_config)
23
23
  end
24
24
  end
25
25
 
@@ -81,8 +81,8 @@ class Fog < CORL.plugin_class(:node)
81
81
  if region = myself[:region]
82
82
  region
83
83
  else
84
- first_region = regions.first
85
- myself.region = first_region
84
+ first_region = regions.first
85
+ myself.region = first_region
86
86
  first_region
87
87
  end
88
88
  end
@@ -7,7 +7,6 @@ class Machine < CORL.plugin_class(:base)
7
7
  # Machine plugin interface
8
8
 
9
9
  def normalize(reload)
10
- myself.plugin_name = node[:id]
11
10
  end
12
11
 
13
12
  #-----------------------------------------------------------------------------
@@ -85,12 +84,6 @@ class Machine < CORL.plugin_class(:base)
85
84
  #-----------------------------------------------------------------------------
86
85
  # Management
87
86
 
88
- def init_ssh(ssh_port)
89
- # Implement in sub classes if needed
90
- end
91
-
92
- #---
93
-
94
87
  def load
95
88
  success = true
96
89
 
@@ -158,7 +151,7 @@ class Machine < CORL.plugin_class(:base)
158
151
  results = []
159
152
 
160
153
  if running?
161
- logger.debug("Executing command on #{plugin_provider} machine with: #{options.inspect}")
154
+ logger.debug("Executing commands on #{plugin_provider} machine with: #{options.inspect}")
162
155
  config = Config.ensure(options)
163
156
  results = yield(config, results) if block_given?
164
157
  else
@@ -246,12 +239,12 @@ class Machine < CORL.plugin_class(:base)
246
239
  else
247
240
  logger.debug("Starting #{plugin_provider} machine with: #{options.inspect}")
248
241
 
249
- if created?
250
- logger.debug("Machine #{plugin_name} has already been created")
242
+ logger.debug("Machine #{plugin_name} is not running yet")
243
+ if block_given?
244
+ success = yield(config)
251
245
  else
252
- logger.debug("Machine #{plugin_name} does not yet exist")
253
- success = create(options)
254
- end
246
+ success = create(options)
247
+ end
255
248
  end
256
249
 
257
250
  logger.warn("There was an error starting the machine #{plugin_name}") unless success
@@ -4,7 +4,6 @@ module Plugin
4
4
  class Network < CORL.plugin_class(:base)
5
5
 
6
6
  init_plugin_collection
7
- task_class TaskThread
8
7
 
9
8
  #-----------------------------------------------------------------------------
10
9
  # Cloud plugin interface
@@ -14,6 +13,8 @@ class Network < CORL.plugin_class(:base)
14
13
 
15
14
  logger.info("Initializing sub configuration from source with: #{myself._export.inspect}")
16
15
  myself.config = CORL.configuration(Config.new(myself._export).import({ :autosave => false, :create => false })) unless reload
16
+
17
+ ignore('build')
17
18
  end
18
19
 
19
20
  #-----------------------------------------------------------------------------
@@ -49,6 +50,18 @@ class Network < CORL.plugin_class(:base)
49
50
 
50
51
  #---
51
52
 
53
+ def cache
54
+ config.cache
55
+ end
56
+
57
+ #---
58
+
59
+ def ignore(files)
60
+ config.ignore(files)
61
+ end
62
+
63
+ #---
64
+
52
65
  def remote(name)
53
66
  config.remote(name)
54
67
  end
@@ -130,7 +143,7 @@ class Network < CORL.plugin_class(:base)
130
143
  #---
131
144
 
132
145
  def local_node(require_new = false)
133
- ip_address = CORL.ip_address
146
+ ip_address = CORL.public_ip
134
147
  local_node = node_by_ip(ip_address, require_new)
135
148
 
136
149
  if local_node.nil?
@@ -223,15 +236,18 @@ class Network < CORL.plugin_class(:base)
223
236
 
224
237
  remote_name = config.delete(:remote, :edit)
225
238
 
226
- # Set node data
227
- node = set_node(provider, name, {
228
- :settings => array(config.delete(:groups, [])),
239
+ node_options = Util::Data.clean({
240
+ :settings => array(config.delete(:groups, [])) | [ "server" ],
229
241
  :region => config.delete(:region, nil),
230
242
  :machine_type => config.delete(:machine_type, nil),
243
+ :public_ip => config.delete(:public_ip, nil),
231
244
  :image => config.delete(:image, nil),
232
245
  :user => config.delete(:user, :root),
233
246
  :hostname => name
234
247
  })
248
+
249
+ # Set node data
250
+ node = set_node(provider, name, node_options)
235
251
  hook_config = { :node => node, :remote => remote_name, :config => config }
236
252
  success = true
237
253
 
@@ -252,11 +268,11 @@ class Network < CORL.plugin_class(:base)
252
268
  if success
253
269
  seed_project = config.get(:project_reference, nil)
254
270
  save_config = { :commit => true, :remote => remote_name, :push => true }
255
-
271
+
256
272
  if seed_project && remote_name
257
273
  # Reset project remote
258
274
  seed_info = Plugin::Project.translate_reference(seed_project)
259
-
275
+
260
276
  if seed_info
261
277
  seed_url = seed_info[:url]
262
278
  seed_branch = seed_info[:revision] if seed_info[:revision]
@@ -4,8 +4,7 @@ module Plugin
4
4
  class Node < CORL.plugin_class(:base)
5
5
 
6
6
  include Celluloid
7
- task_class TaskThread
8
-
7
+
9
8
  #-----------------------------------------------------------------------------
10
9
  # Node plugin interface
11
10
 
@@ -18,12 +17,10 @@ class Node < CORL.plugin_class(:base)
18
17
  myself[name] = value
19
18
  end
20
19
 
21
- yield if block_given? # Chance to create a machine to feed hostname
22
-
23
20
  ui.resource = Util::Console.colorize(hostname, @class_color)
24
21
  logger = hostname
25
22
 
26
- myself[:settings] = [ "all", plugin_provider.to_s, plugin_name.to_s ] | setting(:settings, [], :array)
23
+ add_groups([ "all", plugin_provider.to_s, plugin_name.to_s ])
27
24
 
28
25
  unless reload
29
26
  @cli_interface = Util::Liquid.new do |method, args, &code|
@@ -317,7 +314,7 @@ class Node < CORL.plugin_class(:base)
317
314
 
318
315
  def machine_config
319
316
  name = myself[:id]
320
- name = myself[:hostname] if name.nil?
317
+ name = myself[:hostname] if name.nil? || name.empty?
321
318
  config = Config.new({ :name => name })
322
319
 
323
320
  yield(config) if block_given?
@@ -436,6 +433,8 @@ class Node < CORL.plugin_class(:base)
436
433
  if machine
437
434
  config = Config.ensure(options)
438
435
 
436
+ clear_cache
437
+
439
438
  if extension_check(:create, { :config => config })
440
439
  logger.info("Creating node: #{plugin_name}")
441
440
 
@@ -571,7 +570,8 @@ class Node < CORL.plugin_class(:base)
571
570
  execute_block_on_receiver :exec
572
571
 
573
572
  def exec(options = {})
574
- results = nil
573
+ default_error = Util::Shell::Result.new(:error, 255)
574
+ results = [ default_error ]
575
575
 
576
576
  if machine && machine.running?
577
577
  config = Config.ensure(options)
@@ -594,25 +594,35 @@ class Node < CORL.plugin_class(:base)
594
594
  results = active_machine.exec(commands, config.export) do |type, command, data|
595
595
  unless local?
596
596
  if type == :error
597
- alert(data)
597
+ alert(filter_output(type, data))
598
598
  else
599
- render(data)
599
+ render(filter_output(type, data))
600
600
  end
601
601
  end
602
602
  yield(:progress, { :type => type, :command => command, :data => data }) if block_given?
603
603
  end
604
+ else
605
+ default_error.append_errors("No execution command")
604
606
  end
605
607
 
606
- success = true
607
- results.each do |result|
608
- success = false if result.status != code.success
609
- end
610
- if success
611
- yield(:process, config) if block_given?
612
- extension(:exec_success, { :config => config, :results => results })
608
+ if results
609
+ success = true
610
+ results.each do |result|
611
+ success = false if result.status != code.success
612
+ end
613
+ if success
614
+ yield(:process, config) if block_given?
615
+ extension(:exec_success, { :config => config, :results => results })
616
+ end
617
+ else
618
+ default_error.append_errors("No execution results")
613
619
  end
620
+ else
621
+ default_error.append_errors("Execution prevented by exec hook")
614
622
  end
615
623
  else
624
+ default_error.append_errors("No attached machine")
625
+
616
626
  logger.warn("Node #{plugin_name} does not have an attached machine or is not running so cannot execute commands")
617
627
  end
618
628
  results
@@ -638,14 +648,14 @@ class Node < CORL.plugin_class(:base)
638
648
 
639
649
  admin_command = ''
640
650
  if as_admin
641
- admin_command = 'sudo' if user.to_s == 'ubuntu'
651
+ admin_command = 'sudo' if user.to_s != 'root'
642
652
  admin_command = extension_set(:admin_command, admin_command, config)
643
653
  end
644
654
 
645
655
  results = exec({ :commands => [ "#{admin_command} #{command.to_s}".strip ] }) do |op, data|
646
656
  yield(op, data) if block_given?
647
- end
648
- results.first
657
+ end
658
+ results.first
649
659
  end
650
660
 
651
661
  #---
@@ -737,6 +747,7 @@ class Node < CORL.plugin_class(:base)
737
747
  codes :local_path_not_found,
738
748
  :home_path_lookup_failure,
739
749
  :auth_upload_failure,
750
+ :root_auth_copy_failure,
740
751
  :bootstrap_upload_failure,
741
752
  :bootstrap_exec_failure,
742
753
  :reload_failure
@@ -748,7 +759,7 @@ class Node < CORL.plugin_class(:base)
748
759
  # Transmit authorisation / credential files
749
760
  package_files = [ '.fog', '.netrc', '.google-privatekey.p12', '.vimrc' ]
750
761
  auth_files.each do |file|
751
- package_files = file.gsub(local_path + '/', '')
762
+ package_files << file.gsub(local_path + '/', '')
752
763
  end
753
764
  send_success = send_files(local_path, user_home, package_files, '0600') do |op, data|
754
765
  yield("send_#{op}".to_sym, data) if block_given?
@@ -757,6 +768,14 @@ class Node < CORL.plugin_class(:base)
757
768
  unless send_success
758
769
  myself.status = code.auth_upload_failure
759
770
  end
771
+
772
+ if user.to_sym != config.get(:root_user, :root).to_sym
773
+ auth_files = package_files.collect { |path| "'#{path}'"}
774
+ root_home_path = config.get(:root_home, '/root')
775
+
776
+ result = command("cp #{auth_files.join(' ')} #{root_home_path}", { :as_admin => true })
777
+ myself.status = code.root_auth_copy_failure unless result.status == code.success
778
+ end
760
779
 
761
780
  # Send bootstrap package
762
781
  if status == code.success
@@ -809,16 +828,19 @@ class Node < CORL.plugin_class(:base)
809
828
  config = Config.ensure(options)
810
829
 
811
830
  # Record machine parameters
812
- id(true)
813
- public_ip(true)
814
- private_ip(true)
815
- state(true)
816
-
817
- machine_type(false)
818
- image(false)
831
+ if block_given?
832
+ # Provider or external configuration preparation
833
+ yield(config)
834
+ else
835
+ # Default configuration preparation
836
+ id(true)
837
+ public_ip(true)
838
+ private_ip(true)
839
+ state(true)
819
840
 
820
- # Provider or external configuration preparation
821
- yield(config) if block_given?
841
+ machine_type(false)
842
+ image(false)
843
+ end
822
844
 
823
845
  network.save(config.import({
824
846
  :commit => true,
@@ -943,15 +965,19 @@ class Node < CORL.plugin_class(:base)
943
965
 
944
966
  myself.machine = nil
945
967
 
946
- delete_setting(:id)
947
- delete_setting(:public_ip)
948
- delete_setting(:private_ip)
949
- delete_setting(:ssh_port)
950
- delete_setting(:build)
968
+ override_settings = false
969
+ override_settings = yield(:finalize, config) if success && block_given?
951
970
 
952
- myself[:state] = :stopped
971
+ if success && ! override_settings
972
+ delete_setting(:id)
973
+ delete_setting(:public_ip)
974
+ delete_setting(:private_ip)
975
+ delete_setting(:ssh_port)
976
+ delete_setting(:build)
953
977
 
954
- success = save(config) if success
978
+ myself[:state] = :stopped
979
+ end
980
+ success = save(config)
955
981
 
956
982
  if success && block_given?
957
983
  process_success = yield(:process, config)
@@ -1005,6 +1031,7 @@ class Node < CORL.plugin_class(:base)
1005
1031
 
1006
1032
  if success
1007
1033
  extension(:destroy_success, { :config => config })
1034
+ clear_cache
1008
1035
  end
1009
1036
  end
1010
1037
  else
@@ -1091,6 +1118,12 @@ class Node < CORL.plugin_class(:base)
1091
1118
  result.status == code.success ? true : false
1092
1119
  end
1093
1120
 
1121
+ #---
1122
+
1123
+ def filter_output(type, data)
1124
+ data
1125
+ end
1126
+
1094
1127
  #-----------------------------------------------------------------------------
1095
1128
  # Machine type utilities
1096
1129
 
@@ -4,8 +4,7 @@ module Plugin
4
4
  class Provisioner < CORL.plugin_class(:base)
5
5
 
6
6
  include Celluloid
7
- task_class TaskThread
8
-
7
+
9
8
  #-----------------------------------------------------------------------------
10
9
  # Provisioner plugin interface
11
10
 
@@ -0,0 +1,7 @@
1
+ #
2
+ # CORL Vagrant environment (development platform)
3
+ #-------------------------------------------------------------------------------
4
+ #
5
+ Vagrant.configure('2') do |config|
6
+ CORL.vagrant_config(File.dirname(__FILE__), config)
7
+ end
@@ -0,0 +1,66 @@
1
+
2
+ module VagrantPlugins
3
+ module CORL
4
+ module Command
5
+ class Launcher < ::Vagrant.plugin("2", :command)
6
+
7
+ include Celluloid
8
+
9
+ #-----------------------------------------------------------------------------
10
+
11
+ def self.synopsis
12
+ "execute CORL actions within the defined network"
13
+ end
14
+
15
+ #-----------------------------------------------------------------------------
16
+ # Property accessors / modifiers
17
+
18
+ def env
19
+ @env
20
+ end
21
+
22
+ #-----------------------------------------------------------------------------
23
+ # Execution
24
+
25
+ def execute
26
+ # Set the base command so we can access in any actions executed
27
+ ::CORL::Vagrant.command = current_actor
28
+ ::CORL.executable(@argv - [ "--" ], "vagrant corl")
29
+ end
30
+
31
+ #-----------------------------------------------------------------------------
32
+ # Utilities
33
+
34
+ def vm_machine(name, provider = nil, refresh = false)
35
+ machine = nil
36
+
37
+ # Mostly derived from Vagrant base command with_target_vms() method
38
+ provider = provider.to_sym if provider
39
+
40
+ env.active_machines.each do |active_name, active_provider|
41
+ if name == active_name
42
+ if provider && provider != active_provider
43
+ raise ::Vagrant::Errors::ActiveMachineWithDifferentProvider,
44
+ :name => active_name.to_s,
45
+ :active_provider => active_provider.to_s,
46
+ :requested_provider => provider.to_s
47
+ else
48
+ @logger.info("Active machine found with name #{active_name}. " +
49
+ "Using provider: #{active_provider}")
50
+ provider = active_provider
51
+ break
52
+ end
53
+ end
54
+ end
55
+
56
+ provider ||= env.default_provider
57
+
58
+ machine = env.machine(name, provider, refresh)
59
+ machine.ui.opts[:color] = :default # TODO: Something better??
60
+
61
+ machine
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end