leap_cli 1.6.2 → 1.7.3

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.
@@ -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
  #