lxd-common 0.7.0 → 0.8.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 031530f1093174e61e18c8a58e85805376bbc9b80368c800f7cc28cc3476e8ac
4
- data.tar.gz: 0dea2d0bdb81700c0b0c05b990620fef2ae999b0828d1ddee0714e54a914909f
3
+ metadata.gz: 43d858bed713fa5394649fad875abd9cbf868cf572fb02599e3811e170f8f9fc
4
+ data.tar.gz: 47e0b0fb09991a4ef903a0ec4b2ddb89308a057e28923e7a5647f50247305ce9
5
5
  SHA512:
6
- metadata.gz: 677d8545906f020bb8a485362cc39e8b7d809a96822fdeb05225d65f9da79bd1865c5105c3b3706946fc29ed09d3d0cbab4efb8c67c4acdd8724d6f41184f75f
7
- data.tar.gz: 18772ed5c102a70bc3e0fdacb0742c7de807d210e339ba83d1db5efc61fc529e36ef423f364f630a3215564e99225b2771572d3699e231617fb2dbad3643f8a4
6
+ metadata.gz: f3840c037a9364eec153a4f9cc67ff4a4c81e0df0efa5f1fe6eb6e6d89e9c976ef69ba59386bd58e9c8510b79d51180fc37469a1e4c623d8fc2d55579dad5e7e
7
+ data.tar.gz: b0c51acca0425ccedf6285d9da0c616baf5b25c9c73de12855e8f4652d5331572b107b93b0dd7a73fe0db682bf49ae72c52fedc9f6cac8e868168d118a146cf1
@@ -54,12 +54,17 @@ module NexusSW
54
54
  end
55
55
 
56
56
  def write_file(container_name, path, options)
57
- post "/1.0/containers/#{container_name}/files?path=#{path}", options[:content]
57
+ post "/1.0/containers/#{container_name}/files?path=#{path}", options[:content] do |req|
58
+ req.headers['Content-Type'] = 'application/octet-stream'
59
+ req.headers['X-LXD-uid'] = options[:uid] if options[:uid]
60
+ req.headers['X-LXD-gid'] = options[:gid] if options[:gid]
61
+ req.headers['X-LXD-mode'] = options[:file_mode] if options[:file_mode]
62
+ end
58
63
  end
59
64
 
60
- def push_file(local_path, container_name, remote_path)
61
- write_file container_name, remote_path, content: IO.binread(local_path)
62
- end
65
+ # def push_file(local_path, container_name, remote_path)
66
+ # write_file container_name, remote_path, content: IO.binread(local_path)
67
+ # end
63
68
 
64
69
  def pull_file(container_name, remote_path, local_path)
65
70
  IO.binwrite(local_path, read_file(container_name, remote_path))
@@ -74,11 +74,8 @@ module NexusSW
74
74
  if content.is_a? Hash
75
75
  req.headers['Content-Type'] = 'application/json'
76
76
  req.body = content.to_json
77
- elsif content # Only upon file upload
78
- req.headers['Content-Type'] = 'application/octet-stream'
79
- req.headers['X-LXD-uid'] = '0'
80
- req.headers['X-LXD-gid'] = '0'
81
- req.headers['X-LXD-mode'] = '0600'
77
+ elsif content # Only upon file upload at this time
78
+ yield req if block_given?
82
79
  req.body = content.to_s
83
80
  end
84
81
  end
@@ -87,7 +84,7 @@ module NexusSW
87
84
  case err['error_code']
88
85
  when 404 then raise RestAPI::Error::NotFound, err['error']
89
86
  when 400 then raise RestAPI::Error::BadRequest, err['error']
90
- else raise "Error #{err['error_code']}: #{err['error']}"
87
+ else raise RestAPI::Error, "Error #{err['error_code']}: #{err['error']}"
91
88
  end
92
89
  end
93
90
  block_given? ? yield(response) : parse_response(response)
@@ -7,11 +7,15 @@ module NexusSW
7
7
  raise "#{self.class}#execute not implemented"
8
8
  end
9
9
 
10
+ def user(_user, _options = {})
11
+ raise "#{self.class}#user not implemented"
12
+ end
13
+
10
14
  def read_file(_path)
11
15
  raise "#{self.class}#read_file not implemented"
12
16
  end
13
17
 
14
- def write_file(_path, _content)
18
+ def write_file(_path, _content, _options = {})
15
19
  raise "#{self.class}#write_file not implemented"
16
20
  end
17
21
 
@@ -19,11 +23,11 @@ module NexusSW
19
23
  raise "#{self.class}#download_file not implemented"
20
24
  end
21
25
 
22
- def upload_file(_local_path, _path)
26
+ def upload_file(_local_path, _path, _options = {})
23
27
  raise "#{self.class}#upload_file not implemented"
24
28
  end
25
29
 
26
- def upload_folder(_local_path, _path)
30
+ def upload_folder(_local_path, _path, _options = {})
27
31
  raise "#{self.class}#upload_folder not implemented"
28
32
  end
29
33
  end
@@ -1,7 +1,8 @@
1
1
  require 'nexussw/lxd/transport/mixins/local'
2
+ require 'nexussw/lxd/transport/mixins/helpers/users'
2
3
  require 'nexussw/lxd/transport/mixins/helpers/upload_folder'
3
4
  require 'tempfile'
4
- require 'pp'
5
+ require 'shellwords'
5
6
 
6
7
  module NexusSW
7
8
  module LXD
@@ -18,17 +19,17 @@ module NexusSW
18
19
  attr_reader :inner_transport, :punt, :container_name, :config
19
20
 
20
21
  include Helpers::UploadFolder
22
+ include Helpers::UsersMixin
21
23
 
22
24
  def execute(command, options = {}, &block)
23
- mycommand = command.is_a?(Array) ? command.join(' ') : command
25
+ command = runas_command(command, options) unless options[:subcommand]
26
+ command = command.shelljoin if command.is_a?(Array)
24
27
  subcommand = options[:subcommand] || "exec #{container_name} --"
25
- mycommand = "lxc #{subcommand} #{mycommand}"
26
- # options = options.except :subcommand if options.key? :subcommand
27
- options = options.reject { |k, _| k == :subcommand }
28
- # We would have competing timeout logic depending on who the inner transport is
29
- # I'll just let rest & local do the timeouts, and if inner is a chef sourced transport, they have timeout logic of their own
30
- # with_timeout_and_retries(options) do
31
- inner_transport.execute mycommand, options, &block
28
+ command = "lxc #{subcommand} #{command}"
29
+
30
+ options.reject! { |k, _| [:subcommand, :runas].include? k }
31
+
32
+ inner_transport.execute command, options, &block
32
33
  end
33
34
 
34
35
  def read_file(path)
@@ -41,10 +42,12 @@ module NexusSW
41
42
  inner_transport.execute("rm -rf #{tfile}", capture: false) if tfile
42
43
  end
43
44
 
44
- def write_file(path, content)
45
+ def write_file(path, content, options = {})
46
+ perms = file_perms(options)
47
+
45
48
  tfile = inner_mktmp
46
49
  inner_transport.write_file tfile, content
47
- execute("#{tfile} #{container_name}#{path}", subcommand: 'file push', capture: false).error!
50
+ execute("#{tfile} #{container_name}#{path}", subcommand: "file push#{perms}", capture: false).error!
48
51
  ensure
49
52
  inner_transport.execute("rm -rf #{tfile}", capture: false) if tfile
50
53
  end
@@ -58,18 +61,22 @@ module NexusSW
58
61
  inner_transport.execute("rm -rf #{tfile}", capture: false) if tfile
59
62
  end
60
63
 
61
- def upload_file(local_path, path)
64
+ def upload_file(local_path, path, options = {})
65
+ perms = file_perms(options)
66
+
62
67
  tfile = inner_mktmp if punt
63
68
  localname = tfile || local_path
64
69
  inner_transport.upload_file local_path, tfile if tfile
65
- execute("#{localname} #{container_name}#{path}", subcommand: 'file push', capture: false).error!
70
+ execute("#{localname} #{container_name}#{path}", subcommand: "file push#{perms}", capture: false).error!
66
71
  ensure
67
72
  inner_transport.execute("rm -rf #{tfile}", capture: false) if tfile
68
73
  end
69
74
 
70
- def upload_folder(local_path, path)
75
+ def upload_folder(local_path, path, options = {})
71
76
  return super unless config[:info] && config[:info]['api_extensions'] && config[:info]['api_extensions'].include?('directory_manipulation')
72
- execute("-r #{local_path} #{container_name}#{path}", subcommand: 'file push', capture: false).error!
77
+
78
+ perms = file_perms(options)
79
+ execute("-r #{local_path} #{container_name}#{path}", subcommand: "file push#{perms}", capture: false).error!
73
80
  end
74
81
 
75
82
  def add_remote(host_name)
@@ -101,6 +108,15 @@ module NexusSW
101
108
  ensure
102
109
  tfile.unlink
103
110
  end
111
+
112
+ def file_perms(options = {})
113
+ perms = ''
114
+ perms += " --uid=#{options[:uid] || uid || 0}"
115
+ perms += " --gid=#{options[:gid] || gid || 0}"
116
+ fmode = options[:file_mode] || file_mode
117
+ perms += " --mode=#{fmode}" if fmode
118
+ perms
119
+ end
104
120
  end
105
121
  end
106
122
  end
@@ -26,12 +26,14 @@ module NexusSW
26
26
  def error!
27
27
  return self if exitstatus == 0
28
28
  msg = "Error: '#{command}' failed with exit code #{exitstatus}.\n"
29
+ # msg += (" while running as '#{username}'.\n" if username) || ".\n"
29
30
  msg += "STDOUT: #{stdout}" if stdout.is_a?(String) && !stdout.empty?
30
31
  msg += "STDERR: #{stderr}" if stderr.is_a?(String) && !stderr.empty?
31
32
  raise ::NexusSW::LXD::RestAPI::Error, msg
32
33
  end
33
34
  end
34
35
 
36
+ # LocalTransport does not have the users mixin, so code the `su` command on the rest & cli transports directly
35
37
  def execute(command, options = {}, &block)
36
38
  options ||= {}
37
39
  return execute_chunked(command, options) if options[:capture] == false && !block_given?
@@ -4,19 +4,19 @@ module NexusSW
4
4
  module Mixins
5
5
  module Helpers
6
6
  module UploadFolder
7
- def upload_folder(local_path, path)
8
- upload_using_tarball(local_path, path) || upload_files_individually(local_path, path)
7
+ def upload_folder(local_path, path, options = {})
8
+ upload_using_tarball(local_path, path, options) || upload_files_individually(local_path, path, options)
9
9
  end
10
10
 
11
- def upload_files_individually(local_path, path)
11
+ def upload_files_individually(local_path, path, options = {})
12
12
  Dir.entries(local_path).map { |f| (f == '.' || f == '..') ? nil : File.join(local_path, f) }.compact.each do |f|
13
13
  dest = File.join(path, File.basename(local_path))
14
- upload_files_individually f, dest if File.directory? f
15
- upload_file f, File.join(dest, File.basename(f)) if File.file? f
14
+ upload_files_individually f, dest, options if File.directory? f
15
+ upload_file f, File.join(dest, File.basename(f)), options if File.file? f
16
16
  end
17
17
  end
18
18
 
19
- def upload_using_tarball(local_path, path)
19
+ def upload_using_tarball(local_path, path, options = {})
20
20
  return false unless can_archive?
21
21
  # TODO: should I return false upon error? i.e. retry with individual file uploads if this fails?
22
22
  # lets see how this does in the wild before deciding
@@ -32,12 +32,16 @@ module NexusSW
32
32
  return false
33
33
  end
34
34
  fname = '/tmp/' + File.basename(tfile.path) + ".tar#{ext}"
35
- upload_file tfile.path, fname
35
+ upload_file tfile.path, fname, options
36
36
  # TODO: serious: make sure the tar extract does an overwrite of existing files
37
37
  # multiple converge support as well as CI cycle/dev updated files get updated instead of .1 suffixed (?)
38
38
  # I think I need a flag (it's been a while)
39
- # TODO: explore the chmod & make the same as if uploaded via CLI
40
- execute("bash -c 'mkdir -p #{path} && cd #{path} && tar -xf #{fname} && rm -rf #{fname} && chmod -R 600 #{File.basename(local_path)}'").error!
39
+ myuid = options[:uid] || uid || (0 if is_a?(Mixins::CLI))
40
+ mygid = options[:gid] || gid || (0 if is_a?(Mixins::CLI))
41
+ mymode = options[:file_mode] || file_mode
42
+ chown = " && chown -R #{myuid}:#{mygid} #{File.basename(local_path)}" if myuid
43
+ chmod = " && chmod -R #{mymode} #{File.basename(local_path)}" if mymode
44
+ execute("bash -c 'mkdir -p #{path} && cd #{path} && tar -xf #{fname} && rm -rf #{fname}#{chmod}#{chown}'").error!
41
45
  ensure
42
46
  tfile.unlink
43
47
  end
@@ -0,0 +1,37 @@
1
+ require 'shellwords'
2
+
3
+ module NexusSW
4
+ module LXD
5
+ class Transport
6
+ module Mixins
7
+ module Helpers
8
+ module UsersMixin
9
+ def user(user_nameorid, options = {})
10
+ passwd = read_file options[:passwd_file] || '/etc/passwd'
11
+
12
+ # rework into .split(':') if this gets more complicated
13
+ @uid = user_nameorid.is_a?(String) ? passwd[/^#{user_nameorid}:[^:]*:([^:]*):/, 1] : user_nameorid
14
+ @username = user_nameorid.is_a?(String) ? user_nameorid : passwd[/^([^:]*):[^:]*:#{user_nameorid}:/, 1]
15
+
16
+ # gotcha: we're always setting the default group here, but it's changeable by the user, afterwards
17
+ # so if `user` gets called again, and the caller wants an alternative gid, the caller will need to re-set the gid
18
+ @gid = passwd[/^[^:]*:[^:]*:#{uid}:([^:]*):/, 1]
19
+ end
20
+
21
+ attr_accessor :file_mode, :gid
22
+ attr_reader :uid, :username
23
+
24
+ private
25
+
26
+ def runas_command(command, options = {})
27
+ uname = options[:runas] || username
28
+ return command unless uname
29
+ command = command.shelljoin if command.is_a? Array
30
+ ['su', uname, '-c', command]
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -1,8 +1,10 @@
1
1
  require 'nexussw/lxd/transport/mixins/helpers/execute'
2
+ require 'nexussw/lxd/transport/mixins/helpers/users'
2
3
  require 'nexussw/lxd/transport/mixins/helpers/upload_folder'
3
4
  require 'nio/websocket'
4
5
  require 'tempfile'
5
6
  require 'json'
7
+ require 'shellwords'
6
8
 
7
9
  module NexusSW
8
10
  module LXD
@@ -22,6 +24,7 @@ module NexusSW
22
24
 
23
25
  include Helpers::ExecuteMixin
24
26
  include Helpers::UploadFolder
27
+ include Helpers::UsersMixin
25
28
 
26
29
  class StdinStub
27
30
  # return self as an IO (un)like object
@@ -62,6 +65,7 @@ module NexusSW
62
65
  opid = nil
63
66
  backchannel = nil
64
67
  getlogs = false
68
+ command = runas_command(command, options)
65
69
  if block_given? && (options[:capture] || !config[:info][:api_extensions].include?('container_exec_recording'))
66
70
  apiopts = { :'wait-for-websocket' => true, interactive: false, sync: false }
67
71
  apiopts[:interactive] = true if options[:capture] == :interactive
@@ -114,17 +118,21 @@ module NexusSW
114
118
  api.read_file container_name, path
115
119
  end
116
120
 
117
- def write_file(path, content)
118
- api.write_file container_name, path, content: content
121
+ def write_file(path, content, options = {})
122
+ options = options.merge content: content
123
+ options[:uid] ||= uid if uid
124
+ options[:gid] ||= gid if gid
125
+ options[:file_mode] ||= file_mode if file_mode
126
+ api.write_file container_name, path, options
119
127
  end
120
128
 
121
129
  def download_file(path, local_path)
122
130
  api.pull_file container_name, path, local_path
123
131
  end
124
132
 
125
- def upload_file(local_path, path)
133
+ def upload_file(local_path, path, options = {})
126
134
  # return api.push_file(local_path, container_name, path)
127
- write_file(path, IO.binread(local_path))
135
+ write_file(path, IO.binread(local_path), options)
128
136
  end
129
137
 
130
138
  protected
@@ -1,5 +1,5 @@
1
1
  module NexusSW
2
2
  module LXD
3
- VERSION = '0.7.0'.freeze
3
+ VERSION = '0.8.0'.freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lxd-common
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Zachariasen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-01-15 00:00:00.000000000 Z
11
+ date: 2018-01-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -127,6 +127,7 @@ files:
127
127
  - lib/nexussw/lxd/transport/mixins/cli.rb
128
128
  - lib/nexussw/lxd/transport/mixins/helpers/execute.rb
129
129
  - lib/nexussw/lxd/transport/mixins/helpers/upload_folder.rb
130
+ - lib/nexussw/lxd/transport/mixins/helpers/users.rb
130
131
  - lib/nexussw/lxd/transport/mixins/local.rb
131
132
  - lib/nexussw/lxd/transport/mixins/rest.rb
132
133
  - lib/nexussw/lxd/transport/rest.rb