leap_cli 1.6.2 → 1.7.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,76 +9,91 @@ module LeapCli; module Commands
9
9
  default_value '1'
10
10
  flag [:v, :verbose]
11
11
 
12
- desc 'Override default log file'
12
+ desc 'Override default log file.'
13
13
  arg_name 'FILE'
14
14
  default_value nil
15
15
  flag :log
16
16
 
17
- desc 'Display version number and exit'
17
+ desc 'Display version number and exit.'
18
18
  switch :version, :negatable => false
19
19
 
20
- desc 'Skip prompts and assume "yes"'
20
+ desc 'Skip prompts and assume "yes".'
21
21
  switch :yes, :negatable => false
22
22
 
23
+ desc 'Like --yes, but also skip prompts that are potentially dangerous to skip.'
24
+ switch :force, :negatable => false
25
+
23
26
  desc 'Print full stack trace for exceptions and load `debugger` gem if installed.'
24
27
  switch [:d, :debug], :negatable => false
25
28
 
26
- desc 'Disable colors in output'
29
+ desc 'Disable colors in output.'
27
30
  default_value true
28
31
  switch 'color', :negatable => true
29
32
 
30
33
  pre do |global,command,options,args|
31
- #
34
+ if global[:force]
35
+ global[:yes] = true
36
+ end
37
+ initialize_leap_cli(true, global)
38
+ true
39
+ end
40
+
41
+ protected
42
+
43
+ #
44
+ # available options:
45
+ # :verbose -- integer log verbosity level
46
+ # :log -- log file path
47
+ # :color -- true or false, to log in color or not.
48
+ #
49
+ def initialize_leap_cli(require_provider, options={})
32
50
  # set verbosity
33
- #
34
- LeapCli.set_log_level(global[:verbose].to_i)
51
+ options[:verbose] ||= 1
52
+ LeapCli.set_log_level(options[:verbose].to_i)
35
53
 
36
- #
37
54
  # load Leapfile
38
- #
39
- unless LeapCli.leapfile.load
55
+ LeapCli.leapfile.load
56
+ if LeapCli.leapfile.valid?
57
+ Path.set_platform_path(LeapCli.leapfile.platform_directory_path)
58
+ Path.set_provider_path(LeapCli.leapfile.provider_directory_path)
59
+ if !Path.provider || !File.directory?(Path.provider)
60
+ bail! { log :missing, "provider directory '#{Path.provider}'" }
61
+ end
62
+ if !Path.platform || !File.directory?(Path.platform)
63
+ bail! { log :missing, "platform directory '#{Path.platform}'" }
64
+ end
65
+ elsif require_provider
40
66
  bail! { log :missing, 'Leapfile in directory tree' }
41
67
  end
42
- Path.set_platform_path(LeapCli.leapfile.platform_directory_path)
43
- Path.set_provider_path(LeapCli.leapfile.provider_directory_path)
44
- if !Path.provider || !File.directory?(Path.provider)
45
- bail! { log :missing, "provider directory '#{Path.provider}'" }
46
- end
47
- if !Path.platform || !File.directory?(Path.platform)
48
- bail! { log :missing, "platform directory '#{Path.platform}'" }
49
- end
50
68
 
51
- #
52
69
  # set log file
53
- #
54
- LeapCli.log_file = global[:log] || LeapCli.leapfile.log
70
+ LeapCli.log_file = options[:log] || LeapCli.leapfile.log
55
71
  LeapCli::Util.log_raw(:log) { $0 + ' ' + ORIGINAL_ARGV.join(' ')}
56
72
  log_version
57
- LeapCli.log_in_color = global[:color]
58
-
59
- true
73
+ LeapCli.log_in_color = options[:color]
60
74
  end
61
75
 
62
- private
63
-
64
76
  #
65
77
  # add a log entry for the leap command and leap platform versions
66
78
  #
67
79
  def log_version
68
80
  if LeapCli.log_level >= 2
69
81
  str = "leap command v#{LeapCli::VERSION}"
70
- cli_dir = File.dirname(__FILE__)
71
- if Util.is_git_directory?(cli_dir)
72
- str << " (%s %s)" % [Util.current_git_branch(cli_dir), Util.current_git_commit(cli_dir)]
82
+ if Util.is_git_directory?(LEAP_CLI_BASE_DIR)
83
+ str << " (%s %s)" % [Util.current_git_branch(LEAP_CLI_BASE_DIR),
84
+ Util.current_git_commit(LEAP_CLI_BASE_DIR)]
85
+ else
86
+ str << " (%s)" % LEAP_CLI_BASE_DIR
73
87
  end
74
88
  log 2, str
75
- str = "leap platform v#{Leap::Platform.version}"
76
- if Util.is_git_directory?(Path.platform)
77
- str << " (%s %s)" % [Util.current_git_branch(Path.platform), Util.current_git_commit(Path.platform)]
89
+ if LeapCli.leapfile.valid?
90
+ str = "leap platform v#{Leap::Platform.version}"
91
+ if Util.is_git_directory?(Path.platform)
92
+ str << " (%s %s)" % [Util.current_git_branch(Path.platform), Util.current_git_commit(Path.platform)]
93
+ end
94
+ log 2, str
78
95
  end
79
- log 2, str
80
96
  end
81
97
  end
82
98
 
83
-
84
99
  end; end
@@ -35,6 +35,33 @@ module LeapCli; module Commands
35
35
  end
36
36
  end
37
37
 
38
+ desc 'Secure copy from FILE1 to FILE2. Files are specified as NODE_NAME:FILE_PATH. For local paths, omit "NODE_NAME:".'
39
+ arg_name 'FILE1 FILE2'
40
+ command :scp do |c|
41
+ c.switch :r, :desc => 'Copy recursively'
42
+ c.action do |global_options, options, args|
43
+ if args.size != 2
44
+ bail!('You must specificy both FILE1 and FILE2')
45
+ end
46
+ from, to = args
47
+ if (from !~ /:/ && to !~ /:/) || (from =~ /:/ && to =~ /:/)
48
+ bail!('One FILE must be remote and the other local.')
49
+ end
50
+ src_node_name = src_file_path = src_node = nil
51
+ dst_node_name = dst_file_path = dst_node = nil
52
+ if from =~ /:/
53
+ src_node_name, src_file_path = from.split(':')
54
+ src_node = get_node_from_args([src_node_name], :include_disabled => true)
55
+ dst_file_path = to
56
+ else
57
+ dst_node_name, dst_file_path = to.split(':')
58
+ dst_node = get_node_from_args([dst_node_name], :include_disabled => true)
59
+ src_file_path = from
60
+ end
61
+ exec_scp(options, src_node, src_file_path, dst_node, dst_file_path)
62
+ end
63
+ end
64
+
38
65
  protected
39
66
 
40
67
  #
@@ -78,22 +105,7 @@ module LeapCli; module Commands
78
105
  def exec_ssh(cmd, cli_options, args)
79
106
  node = get_node_from_args(args, :include_disabled => true)
80
107
  port = node.ssh.port
81
- options = [
82
- "-o 'HostName=#{node.ip_address}'",
83
- # "-o 'HostKeyAlias=#{node.name}'", << oddly incompatible with ports in known_hosts file, so we must not use this or non-standard ports break.
84
- "-o 'GlobalKnownHostsFile=#{path(:known_hosts)}'",
85
- "-o 'UserKnownHostsFile=/dev/null'"
86
- ]
87
- if node.vagrant?
88
- options << "-i #{vagrant_ssh_key_file}" # use the universal vagrant insecure key
89
- options << "-o IdentitiesOnly=yes" # force the use of the insecure vagrant key
90
- options << "-o 'StrictHostKeyChecking=no'" # blindly accept host key and don't save it (since userknownhostsfile is /dev/null)
91
- else
92
- options << "-o 'StrictHostKeyChecking=yes'"
93
- end
94
- if !node.supported_ssh_host_key_algorithms.empty?
95
- options << "-o 'HostKeyAlgorithms=#{node.supported_ssh_host_key_algorithms}'"
96
- end
108
+ options = ssh_config(node)
97
109
  username = 'root'
98
110
  if LeapCli.log_level >= 3
99
111
  options << "-vv"
@@ -133,6 +145,62 @@ module LeapCli; module Commands
133
145
  end
134
146
  end
135
147
 
148
+ def exec_scp(cli_options, src_node, src_file_path, dst_node, dst_file_path)
149
+ node = src_node || dst_node
150
+ options = ssh_config(node)
151
+ port = node.ssh.port
152
+ username = 'root'
153
+ options << "-r" if cli_options[:r]
154
+ scp = "scp -P #{port} #{options.join(' ')}"
155
+ if src_node
156
+ command = "#{scp} #{username}@#{src_node.domain.full}:#{src_file_path} #{dst_file_path}"
157
+ elsif dst_node
158
+ command = "#{scp} #{src_file_path} #{username}@#{dst_node.domain.full}:#{dst_file_path}"
159
+ end
160
+ log 2, command
161
+
162
+ # exec the shell command in a subprocess
163
+ pid = fork { exec "#{command}" }
164
+
165
+ Signal.trap("SIGINT") do
166
+ Process.kill("KILL", pid)
167
+ Process.wait(pid)
168
+ exit(0)
169
+ end
170
+
171
+ # wait for shell to exit so we can grab the exit status
172
+ _, status = Process.waitpid2(pid)
173
+ exit(status.exitstatus)
174
+ end
175
+
176
+ #
177
+ # SSH command line -o options. See `man ssh_config`
178
+ #
179
+ # NOTES:
180
+ #
181
+ # The option 'HostKeyAlias=#{node.name}' is oddly incompatible with ports in
182
+ # known_hosts file, so we must not use this or non-standard ports break.
183
+ #
184
+ def ssh_config(node)
185
+ options = [
186
+ "-o 'HostName=#{node.ip_address}'",
187
+ "-o 'GlobalKnownHostsFile=#{path(:known_hosts)}'",
188
+ "-o 'UserKnownHostsFile=/dev/null'"
189
+ ]
190
+ if node.vagrant?
191
+ options << "-i #{vagrant_ssh_key_file}" # use the universal vagrant insecure key
192
+ options << "-o IdentitiesOnly=yes" # force the use of the insecure vagrant key
193
+ options << "-o 'StrictHostKeyChecking=no'" # blindly accept host key and don't save it
194
+ # (since userknownhostsfile is /dev/null)
195
+ else
196
+ options << "-o 'StrictHostKeyChecking=yes'"
197
+ end
198
+ if !node.supported_ssh_host_key_algorithms.empty?
199
+ options << "-o 'HostKeyAlgorithms=#{node.supported_ssh_host_key_algorithms}'"
200
+ end
201
+ return options
202
+ end
203
+
136
204
  def parse_tunnel_arg(arg)
137
205
  if arg.count(':') == 1
138
206
  node_name, remote = arg.split(':')
@@ -100,9 +100,9 @@ module LeapCli
100
100
  #
101
101
  def pick_pgp_key
102
102
  begin
103
- return unless `which gpg`.strip.any?
104
103
  require 'gpgme'
105
104
  rescue LoadError
105
+ log "Skipping OpenPGP setup because gpgme is not installed."
106
106
  return
107
107
  end
108
108
 
@@ -111,32 +111,23 @@ module LeapCli; module Commands
111
111
  def vagrant_setup
112
112
  assert_bin! 'vagrant', 'Vagrant is required for running local virtual machines. Run "sudo apt-get install vagrant".'
113
113
 
114
- version = vagrant_version
115
- case version
116
- when 0..1
117
- gem_path = assert_run!('vagrant gem which sahara')
118
- if gem_path.nil? || gem_path.empty? || gem_path =~ /^ERROR/
119
- log :installing, "vagrant plugin 'sahara'"
120
- assert_run! 'vagrant gem install sahara -v 0.0.13'
121
- # (sahara versions above 0.0.13 require vagrant > 1.0)
122
- end
123
- when 2
124
- unless assert_run!('vagrant plugin list | grep sahara | cat').chars.any?
125
- log :installing, "vagrant plugin 'sahara'"
126
- assert_run! 'vagrant plugin install sahara'
127
- end
114
+ if vagrant_version <= Gem::Version.new('1.0.0')
115
+ gem_path = assert_run!('vagrant gem which sahara')
116
+ if gem_path.nil? || gem_path.empty? || gem_path =~ /^ERROR/
117
+ log :installing, "vagrant plugin 'sahara'"
118
+ assert_run! 'vagrant gem install sahara -v 0.0.13'
119
+ end
120
+ else
121
+ unless assert_run!('vagrant plugin list | grep sahara | cat').chars.any?
122
+ log :installing, "vagrant plugin 'sahara'"
123
+ assert_run! 'vagrant plugin install sahara'
124
+ end
128
125
  end
129
126
  create_vagrant_file
130
127
  end
131
128
 
132
129
  def vagrant_version
133
- minor_version = `vagrant --version | rev | cut -d'.' -f 2`.to_i
134
- version = case minor_version
135
- when 1..9 then 2
136
- when 0 then 1
137
- else 0
138
- end
139
- return version
130
+ @vagrant_version ||= Gem::Version.new(assert_run!('vagrant --version').split(' ')[1])
140
131
  end
141
132
 
142
133
  def execute(cmd)
@@ -148,38 +139,34 @@ module LeapCli; module Commands
148
139
  lines = []
149
140
  netmask = IPAddr.new('255.255.255.255').mask(LeapCli.leapfile.vagrant_network.split('/').last).to_s
150
141
 
151
- version = vagrant_version
152
- case version
153
- when 0..1
154
- lines << %[Vagrant::Config.run do |config|]
155
- manager.each_node do |node|
156
- if node.vagrant?
157
- lines << %[ config.vm.define :#{node.name} do |config|]
158
- lines << %[ config.vm.box = "leap-wheezy"]
159
- lines << %[ config.vm.box_url = "https://downloads.leap.se/platform/vagrant/virtualbox/leap-wheezy.box"]
160
- lines << %[ config.vm.network :hostonly, "#{node.ip_address}", :netmask => "#{netmask}"]
161
- lines << %[ config.vm.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]]
162
- lines << %[ config.vm.customize ["modifyvm", :id, "--name", "#{node.name}"]]
163
- lines << %[ #{leapfile.custom_vagrant_vm_line}] if leapfile.custom_vagrant_vm_line
164
- lines << %[ end]
165
- end
142
+ if vagrant_version <= Gem::Version.new('1.1.0')
143
+ lines << %[Vagrant::Config.run do |config|]
144
+ manager.each_node do |node|
145
+ if node.vagrant?
146
+ lines << %[ config.vm.define :#{node.name} do |config|]
147
+ lines << %[ config.vm.box = "LEAP/wheezy"]
148
+ lines << %[ config.vm.network :hostonly, "#{node.ip_address}", :netmask => "#{netmask}"]
149
+ lines << %[ config.vm.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]]
150
+ lines << %[ config.vm.customize ["modifyvm", :id, "--name", "#{node.name}"]]
151
+ lines << %[ #{leapfile.custom_vagrant_vm_line}] if leapfile.custom_vagrant_vm_line
152
+ lines << %[ end]
166
153
  end
167
- when 2
168
- lines << %[Vagrant.configure("2") do |config|]
169
- manager.each_node do |node|
170
- if node.vagrant?
171
- lines << %[ config.vm.define :#{node.name} do |config|]
172
- lines << %[ config.vm.box = "leap-wheezy"]
173
- lines << %[ config.vm.box_url = "https://downloads.leap.se/platform/vagrant/virtualbox/leap-wheezy.box"]
174
- lines << %[ config.vm.network :private_network, ip: "#{node.ip_address}"]
175
- lines << %[ config.vm.provider "virtualbox" do |v|]
176
- lines << %[ v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]]
177
- lines << %[ v.name = "#{node.name}"]
178
- lines << %[ end]
179
- lines << %[ #{leapfile.custom_vagrant_vm_line}] if leapfile.custom_vagrant_vm_line
180
- lines << %[ end]
181
- end
154
+ end
155
+ else
156
+ lines << %[Vagrant.configure("2") do |config|]
157
+ manager.each_node do |node|
158
+ if node.vagrant?
159
+ lines << %[ config.vm.define :#{node.name} do |config|]
160
+ lines << %[ config.vm.box = "LEAP/wheezy"]
161
+ lines << %[ config.vm.network :private_network, ip: "#{node.ip_address}"]
162
+ lines << %[ config.vm.provider "virtualbox" do |v|]
163
+ lines << %[ v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]]
164
+ lines << %[ v.name = "#{node.name}"]
165
+ lines << %[ end]
166
+ lines << %[ #{leapfile.custom_vagrant_vm_line}] if leapfile.custom_vagrant_vm_line
167
+ lines << %[ end]
182
168
  end
169
+ end
183
170
  end
184
171
 
185
172
  lines << %[end]
@@ -133,9 +133,9 @@ module LeapCli
133
133
  next unless ename
134
134
  log 3, :loading, '%s environment...' % ename
135
135
  env(ename) do |e|
136
- e.services = load_all_json(Path.named_path([:service_env_config, '*', ename], @provider_dir), Config::Tag)
137
- e.tags = load_all_json(Path.named_path([:tag_env_config, '*', ename], @provider_dir), Config::Tag)
138
- e.provider = load_json( Path.named_path([:provider_env_config, ename], @provider_dir), Config::Provider)
136
+ e.services = load_all_json(Path.named_path([:service_env_config, '*', ename], @provider_dir), Config::Tag, :env => ename)
137
+ e.tags = load_all_json(Path.named_path([:tag_env_config, '*', ename], @provider_dir), Config::Tag, :env => ename)
138
+ e.provider = load_json( Path.named_path([:provider_env_config, ename], @provider_dir), Config::Provider, :env => ename)
139
139
  e.services.inherit_from! env('default').services
140
140
  e.tags.inherit_from! env('default').tags
141
141
  e.provider.inherit_from! env('default').provider
@@ -315,6 +315,9 @@ module LeapCli
315
315
  if obj
316
316
  name = File.basename(filename).force_encoding('utf-8').sub(/^([^\.]+).*\.json$/,'\1')
317
317
  obj['name'] ||= name
318
+ if options[:env]
319
+ obj.environment = options[:env]
320
+ end
318
321
  results[name] = obj
319
322
  end
320
323
  end
@@ -10,6 +10,34 @@ require 'ya2yaml' # pure ruby yaml
10
10
 
11
11
  module LeapCli
12
12
  module Config
13
+
14
+ #
15
+ # A proxy for Manager that binds to a particular object
16
+ # (so that we can bind to a particular environment)
17
+ #
18
+ class ManagerBinding
19
+ def initialize(manager, object)
20
+ @manager = manager
21
+ @object = object
22
+ end
23
+
24
+ def services
25
+ @manager.env(@object.environment).services
26
+ end
27
+
28
+ def tags
29
+ @manager.env(@object.environment).tags
30
+ end
31
+
32
+ def provider
33
+ @manager.env(@object.environment).provider
34
+ end
35
+
36
+ def method_missing(*args)
37
+ @manager.send(*args)
38
+ end
39
+ end
40
+
13
41
  #
14
42
  # This class represents the configuration for a single node, service, or tag.
15
43
  # Also, all the nested hashes are also of this type.
@@ -19,8 +47,6 @@ module LeapCli
19
47
  class Object < Hash
20
48
 
21
49
  attr_reader :node
22
- attr_reader :manager
23
- alias :global :manager
24
50
 
25
51
  def initialize(manager=nil, node=nil)
26
52
  # keep a global pointer around to the config manager. used a lot in the eval strings and templates
@@ -31,6 +57,19 @@ module LeapCli
31
57
  @node = node || self
32
58
  end
33
59
 
60
+ def manager
61
+ ManagerBinding.new(@manager, self)
62
+ end
63
+ alias :global :manager
64
+
65
+ def environment=(e)
66
+ self.store('environment', e)
67
+ end
68
+
69
+ def environment
70
+ self['environment']
71
+ end
72
+
34
73
  #
35
74
  # export YAML
36
75
  #