lxdev 0.1.8 → 0.2.1

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 (4) hide show
  1. checksums.yaml +4 -4
  2. data/bin/lxdev +2 -5
  3. data/lib/lxdev/main.rb +69 -81
  4. metadata +6 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 509a1fe96d989df0a53f8a9234129ccce89504521adb358cfcd837053db5f1cd
4
- data.tar.gz: 12180a752fd2683f30cbfe15c853f6979fedbb3151547b3d7aefcc842c0d564e
3
+ metadata.gz: cb972fcc30452bfe0545f20313f64c1d81b0f55ee13355d4db2bc38feb9254ee
4
+ data.tar.gz: 27aeb08fc416c2eca7f34c773edb4204ff545103886034f9de01194666aa26b7
5
5
  SHA512:
6
- metadata.gz: 3925a9f8ecbeb0e7ba875bf3ce3d2501e4654fdb96a52a197928f8903d22d0daa2d8e1771f54d8c2c3060d3204a03e21f28cb0e50d8f536e386b607d2f7c94b9
7
- data.tar.gz: e091f4dda9ce752cf7f64b4f6464699189c9d3c380e995a63e390cf7f7b1223d9e6e9ea7143bce13c1236a01a364a7f0d0ce2b84cfa28a22b6c8483c83317618
6
+ metadata.gz: c02623cee7ee35815dc6846bbc3a9b313610612b0f5600620ca6d1d9fc6b0a4da7f02b5005ed6c180fe1b9283a7111a20c226ececd332d262213b16a43ff2433
7
+ data.tar.gz: 59d924897d54fe732b40c68f92c4c0fd03044ede08d0b672ef3c1f340b2ba64f42c7bb452563f332937b35e8923dfff4a77503ddc3763791159e9eb25d4233a7
data/bin/lxdev CHANGED
@@ -32,16 +32,15 @@ def option_parser
32
32
  Commands:
33
33
  up Bring container up
34
34
  status Show status of the container
35
- ssh Log into the container
35
+ ssh Log into the container
36
36
  halt Stop the container
37
- destroy Destroy the container
37
+ destroy Destroy the container
38
38
  provision Run provisioning commands from YAML file
39
39
  exec Run command as root in container
40
40
  snapshot Snapshots the container. Takes a snapshot name as parameter.
41
41
  rmsnapshot Deletes a snapshot. Takes a snapshot name as parameter.
42
42
  restore Restore container to a snapshot with a previous state. Takes a snapshot name as parameter.
43
43
  revert Restore container to the last snapshot taken.
44
- sudoers Create a sudoers file, allowing you to use lxdev without entering the sudo password
45
44
  EOS
46
45
  opt_parser.parse!()
47
46
 
@@ -74,8 +73,6 @@ def execute_main_command(lxdev)
74
73
  else
75
74
  lxdev.execute(command)
76
75
  end
77
- when 'sudoers'
78
- LxDev::Main.create_sudoers_file()
79
76
  when 'snapshot'
80
77
  snapshot_name = ARGV[1]
81
78
  if snapshot_name.nil?
data/lib/lxdev/main.rb CHANGED
@@ -3,15 +3,16 @@ require 'json'
3
3
  require 'terminal-table'
4
4
  require 'lxdev/system'
5
5
  require 'tempfile'
6
+ require 'etc'
6
7
 
7
8
  module LxDev
8
9
  class Main
9
- WHITELISTED_SUDO_COMMANDS = ["lxc", "redir", "kill"]
10
+ REQUIRED_COMMANDS = ["lxc", "redir", "kill"]
10
11
  SHELLS = ["bash", "zsh", "sh", "csh", "tcsh", "ash"]
11
12
  BOOT_TIMEOUT = 30
12
- VERSION = '0.1.8'
13
+ VERSION = '0.2.1'
13
14
 
14
- def initialize(config_file, state_file)
15
+ def initialize(config_file, state_file, lxc_command)
15
16
  @state_file = format(".lxdev/%s", state_file)
16
17
  @uid = System.exec("id -u").output.chomp
17
18
  @gid = System.exec("id -g").output.chomp
@@ -20,6 +21,7 @@ module LxDev
20
21
  @image = @config['box']['image']
21
22
  @user = @config['box']['user']
22
23
  @ports = @config['box']['ports'] || {}
24
+ @lxc_command = lxc_command
23
25
  Dir.mkdir('.lxdev') unless File.directory?('.lxdev')
24
26
  begin
25
27
  @state = YAML.load_file(@state_file)
@@ -37,7 +39,14 @@ module LxDev
37
39
  puts "Please run 'lxd init' and configure LXD first"
38
40
  return false
39
41
  end
40
- lxdev = Main.new(config_file, state_file)
42
+ user_in_group = Main.user_in_lxd_group?
43
+ lxc_command = (user_in_group ? "lxc" : "sudo lxc")
44
+ unless(user_in_group)
45
+ puts "Add yourself to the 'lxd' group to avoid entering the sudo password :"
46
+ puts "(You need to login again afterwards, or start a new login shell)"
47
+ puts " sudo usermod -a -G lxd #{Etc.getlogin}"
48
+ end
49
+ lxdev = Main.new(config_file, state_file, lxc_command)
41
50
  unless lxdev.set_ssh_keys
42
51
  puts "No ssh keys detected. Make sure you have an ssh key, a running agent, and the key added to the agent, e.g. with ssh-add."
43
52
  return false
@@ -45,6 +54,15 @@ module LxDev
45
54
  return lxdev
46
55
  end
47
56
 
57
+ def self.user_in_lxd_group?
58
+ begin
59
+ Etc.getgrnam("lxd").mem.include?(Etc.getlogin)
60
+ rescue ArgumentError
61
+ puts "There seems to be no 'lxd' group. Is LXD installed?"
62
+ false
63
+ end
64
+ end
65
+
48
66
  def save_state
49
67
  File.open(@state_file, 'w') {|f| f.write @state.to_yaml} unless @state.empty?
50
68
  end
@@ -114,14 +132,14 @@ module LxDev
114
132
 
115
133
  def halt
116
134
  ensure_container_created
117
- System.exec("sudo lxc stop #{@name}")
135
+ System.exec("#{@lxc_command} stop #{@name}")
118
136
  cleanup_forwarded_ports
119
137
  remove_state
120
138
  end
121
139
 
122
140
  def destroy
123
141
  ensure_container_created
124
- System.exec("sudo lxc delete #{@name}")
142
+ System.exec("#{@lxc_command} delete #{@name}")
125
143
  end
126
144
 
127
145
  def ssh(args)
@@ -137,9 +155,9 @@ module LxDev
137
155
 
138
156
  def execute(command, interactive: false)
139
157
  if interactive
140
- exec("sudo lxc exec #{@name} #{command}") # execution stops here and gives control to exec
158
+ exec("#{@lxc_command} exec #{@name} #{command}") # execution stops here and gives control to exec
141
159
  end
142
- IO.popen("sudo lxc exec #{@name} -- /bin/sh -c '#{command}'", err: [:child, :out]) do |cmd_output|
160
+ IO.popen("#{@lxc_command} exec #{@name} -- /bin/sh -c '#{command}'", err: [:child, :out]) do |cmd_output|
143
161
  cmd_output.each do |line|
144
162
  puts line
145
163
  end
@@ -171,18 +189,18 @@ module LxDev
171
189
 
172
190
  def snapshot(snapshot_name)
173
191
  puts "Creating snapshot #{snapshot_name}"
174
- System.exec("sudo lxc snapshot #{@name} #{snapshot_name}")
192
+ System.exec("#{@lxc_command} snapshot #{@name} #{snapshot_name}")
175
193
  end
176
194
 
177
195
  def restore(snapshot_name)
178
196
  puts "Restoring snapshot #{snapshot_name}"
179
- exitstatus = System.exec("sudo lxc restore #{@name} #{snapshot_name}").exitstatus
197
+ exitstatus = System.exec("#{@lxc_command} restore #{@name} #{snapshot_name}").exitstatus
180
198
  exitstatus == 0
181
199
  end
182
200
 
183
201
  def rmsnapshot(snapshot_name)
184
202
  puts "Deleting snapshot #{snapshot_name}"
185
- exitstatus = System.exec("sudo lxc delete #{@name}/#{snapshot_name}").exitstatus
203
+ exitstatus = System.exec("#{@lxc_command} delete #{@name}/#{snapshot_name}").exitstatus
186
204
  exitstatus == 0
187
205
  end
188
206
 
@@ -199,7 +217,7 @@ module LxDev
199
217
  private
200
218
 
201
219
  def self.lxd_initialized?
202
- exitstatus = System.exec("sudo lxc info | grep 'lxd init'").exitstatus
220
+ exitstatus = System.exec("#{@lxc_command} info | grep 'lxd init'").exitstatus
203
221
  exitstatus != 0
204
222
  end
205
223
 
@@ -212,17 +230,17 @@ module LxDev
212
230
  end
213
231
 
214
232
  def remove_state
215
- File.delete(@state_file) if File.exists?(@state_file)
233
+ File.delete(@state_file) if File.exist?(@state_file)
216
234
  @state = {}
217
235
  end
218
236
 
219
237
  def create_container
220
238
  add_subuid_and_subgid
221
239
  puts "Launching #{@name}..."
222
- System.exec("sudo lxc init #{@image} #{@name}")
223
- System.exec(%{printf "uid #{@uid} 1001\ngid #{@gid} 1001"| sudo lxc config set #{@name} raw.idmap -})
224
- System.exec(%{sudo lxc config set #{@name} boot.autostart false})
225
- System.exec("sudo lxc start #{@name}")
240
+ System.exec("#{@lxc_command} init #{@image} #{@name}")
241
+ System.exec(%{printf "uid #{@uid} 1001\ngid #{@gid} 1001"| #{@lxc_command} config set #{@name} raw.idmap -})
242
+ System.exec(%{#{@lxc_command} config set #{@name} boot.autostart false})
243
+ System.exec("#{@lxc_command} start #{@name}")
226
244
  puts "Creating user #{@user}..."
227
245
  create_container_user(@user)
228
246
  puts "Mapping folders.."
@@ -231,12 +249,12 @@ module LxDev
231
249
 
232
250
  def start_container
233
251
  puts "Starting #{@name}..."
234
- System.exec("sudo lxc start #{@name}")
252
+ System.exec("#{@lxc_command} start #{@name}")
235
253
  end
236
254
 
237
255
  def get_container_status
238
256
  return @status unless @status.nil?
239
- command_result = System.exec("sudo lxc list ^#{@name}$ --format=json")
257
+ command_result = System.exec("#{@lxc_command} list ^#{@name}$ --format=json")
240
258
  @status = JSON.parse(command_result.output)
241
259
  end
242
260
 
@@ -266,16 +284,16 @@ module LxDev
266
284
  end
267
285
 
268
286
  def create_container_user(user)
269
- System.exec("sudo lxc exec #{@name} -- groupadd --gid 1001 #{user}")
270
- System.exec("sudo lxc exec #{@name} -- useradd --uid 1001 --gid 1001 -s /bin/bash -m #{user}")
271
- System.exec("sudo lxc exec #{@name} -- mkdir /home/#{user}/.ssh")
272
- System.exec("sudo lxc exec #{@name} -- chmod 0700 /home/#{user}/.ssh")
273
- System.exec("printf '#{@ssh_keys}' | sudo lxc exec #{@name} tee /home/#{user}/.ssh/authorized_keys")
274
- System.exec("sudo lxc exec #{@name} -- chown -R #{user} /home/#{user}/.ssh")
275
- System.exec("sudo lxc exec #{@name} -- touch /home/#{@user}/.hushlogin")
276
- System.exec("sudo lxc exec #{@name} -- chown #{user} /home/#{user}/.hushlogin")
277
- System.exec(%{printf "#{user} ALL=(ALL) NOPASSWD: ALL\n" | sudo lxc exec #{@name} -- tee -a /etc/sudoers})
278
- System.exec("sudo lxc exec #{@name} -- chmod 0440 /etc/sudoers")
287
+ System.exec("#{@lxc_command} exec #{@name} -- groupadd --gid 1001 #{user}")
288
+ System.exec("#{@lxc_command} exec #{@name} -- useradd --uid 1001 --gid 1001 -s /bin/bash -m #{user}")
289
+ System.exec("#{@lxc_command} exec #{@name} -- mkdir /home/#{user}/.ssh")
290
+ System.exec("#{@lxc_command} exec #{@name} -- chmod 0700 /home/#{user}/.ssh")
291
+ System.exec("printf '#{@ssh_keys}' | #{@lxc_command} exec #{@name} tee /home/#{user}/.ssh/authorized_keys")
292
+ System.exec("#{@lxc_command} exec #{@name} -- chown -R #{user} /home/#{user}/.ssh")
293
+ System.exec("#{@lxc_command} exec #{@name} -- touch /home/#{@user}/.hushlogin")
294
+ System.exec("#{@lxc_command} exec #{@name} -- chown #{user} /home/#{user}/.hushlogin")
295
+ System.exec(%{printf "#{user} ALL=(ALL) NOPASSWD: ALL\n" | #{@lxc_command} exec #{@name} -- tee -a /etc/sudoers})
296
+ System.exec("#{@lxc_command} exec #{@name} -- chmod 0440 /etc/sudoers")
279
297
  end
280
298
 
281
299
  def wait_for_boot
@@ -289,20 +307,33 @@ module LxDev
289
307
 
290
308
  def forward_ports(ports)
291
309
  redir_pids = []
310
+ sudo_redir_pids = []
292
311
  ports.each do |guest, host|
293
- puts "Forwarding #{get_container_ip}:#{guest} to local port #{host}"
294
- pid = System.spawn_exec("sudo redir --caddr=#{get_container_ip} --cport=#{guest} --lport=#{host}", silent: true)
295
- redir_pids << pid
296
- Process.detach(pid)
312
+ if (host > 1024)
313
+ puts "Forwarding #{get_container_ip}:#{guest} to local port #{host}"
314
+ pid = System.spawn_exec("redir --caddr=#{get_container_ip} --cport=#{guest} --lport=#{host}", silent: true)
315
+ redir_pids << pid
316
+ Process.detach(pid)
317
+ else
318
+ puts "Forwarding #{get_container_ip}:#{guest} to local port #{host} (root privileges needed)"
319
+ pid = System.spawn_exec("sudo redir --caddr=#{get_container_ip} --cport=#{guest} --lport=#{host}", silent: true)
320
+ sudo_redir_pids << pid
321
+ Process.detach(pid)
322
+ end
297
323
  end
298
324
  @state['redir_pids'] = redir_pids
325
+ @state['sudo_redir_pids'] = sudo_redir_pids
299
326
  end
300
327
 
301
328
  def cleanup_forwarded_ports
302
329
  if @state.empty?
303
330
  return
304
331
  end
305
- @state['redir_pids'].each do |pid|
332
+ @state['redir_pids']&.each do |pid|
333
+ System.exec("kill #{pid}")
334
+ end
335
+ @state['sudo_redir_pids']&.each do |pid|
336
+ puts "Killing pid #{pid} started with sudo privileges"
306
337
  System.exec("sudo kill #{pid}")
307
338
  end
308
339
  end
@@ -313,7 +344,7 @@ module LxDev
313
344
  counter = counter + 1
314
345
  puts "Mounting #{host} in #{guest}"
315
346
  absolute_path = System.exec("readlink -f #{host}").output.chomp
316
- System.exec("sudo lxc config device add #{@name} shared_folder_#{counter} disk source=#{absolute_path} path=#{guest}")
347
+ System.exec("#{@lxc_command} config device add #{@name} shared_folder_#{counter} disk source=#{absolute_path} path=#{guest}")
317
348
  end
318
349
  end
319
350
 
@@ -334,9 +365,9 @@ module LxDev
334
365
  end
335
366
 
336
367
  def lxd_service_name
337
- if System.exec("sudo systemctl status lxd.service").exitstatus == 0
368
+ if System.exec("systemctl status lxd.service").exitstatus == 0
338
369
  'lxd.service'
339
- elsif System.exec("sudo systemctl status snap.lxd.daemon.service").exitstatus == 0
370
+ elsif System.exec("systemctl status snap.lxd.daemon.service").exitstatus == 0
340
371
  'snap.lxd.daemon.service'
341
372
  else
342
373
  raise 'There seems to be no LXD service on the system!'
@@ -344,7 +375,7 @@ module LxDev
344
375
  end
345
376
 
346
377
  def self.check_requirements
347
- WHITELISTED_SUDO_COMMANDS.each do |cmd|
378
+ REQUIRED_COMMANDS.each do |cmd|
348
379
  unless System.exec("which #{cmd}").exitstatus == 0
349
380
  puts "The command '#{cmd}' is not installed or not available."
350
381
  puts "Please install it before continuing."
@@ -352,48 +383,5 @@ module LxDev
352
383
  end
353
384
  end
354
385
  end
355
-
356
- def self.create_sudoers_file
357
- self.check_requirements
358
- user = System.exec("whoami").output.chomp
359
- content = []
360
- content << "# Created by lxdev #{Time.now}"
361
- WHITELISTED_SUDO_COMMANDS.each do |cmd|
362
- cmd_with_path = System.exec("which #{cmd}").output.chomp
363
- content << "#{user} ALL=(root) NOPASSWD: #{cmd_with_path}"
364
- end
365
- content << "\n"
366
- puts <<-EOS
367
- !! WARNING !!
368
- This will create a file, /etc/sudoers.d/lxdev,
369
- which will give your user #{user} access to running
370
- the following commands :
371
- #{WHITELISTED_SUDO_COMMANDS.join(" ")}
372
- with superuser privileges. If you do not know what you're
373
- doing, this can be dangerous and insecure.
374
-
375
- EOS
376
- puts "The following content will be created in /etc/sudoers.d/lxdev :"
377
- puts
378
- puts content
379
- puts "\nIf you want to do this, type 'yesplease'"
380
- action = STDIN.gets.chomp
381
- unless action == 'yesplease'
382
- puts "Not creating sudoers file"
383
- return
384
- end
385
- temp_file = Tempfile.create('lxdev-sudoers')
386
- temp_file.write(content.join("\n"))
387
- temp_file_name = temp_file.to_path
388
- temp_file.close
389
- unless System.exec("visudo -c -f #{temp_file_name}").exitstatus == 0
390
- puts "Generated sudoers file contains errors, aborting."
391
- exit 1
392
- end
393
- System.exec("sudo chown root:root #{temp_file_name}")
394
- System.exec("sudo chmod 0440 #{temp_file_name}")
395
- System.exec("sudo mv #{temp_file_name} /etc/sudoers.d/lxdev")
396
- puts "Created sudoers file."
397
- end
398
386
  end
399
387
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lxdev
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christian Lønaas
8
8
  - Eivind Mork
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-03-03 00:00:00.000000000 Z
12
+ date: 2024-05-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json
@@ -53,7 +53,7 @@ homepage: https://github.com/GyldendalDigital/lxdev
53
53
  licenses:
54
54
  - MIT
55
55
  metadata: {}
56
- post_install_message:
56
+ post_install_message:
57
57
  rdoc_options: []
58
58
  require_paths:
59
59
  - lib
@@ -68,9 +68,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
68
68
  - !ruby/object:Gem::Version
69
69
  version: '0'
70
70
  requirements: []
71
- rubyforge_project:
72
- rubygems_version: 2.7.6
73
- signing_key:
71
+ rubygems_version: 3.4.20
72
+ signing_key:
74
73
  specification_version: 4
75
74
  summary: Automagic development environment with LXD
76
75
  test_files: []