r-train 0.10.8 → 0.11.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
  SHA1:
3
- metadata.gz: 32ff41b8ff5978a069895749aa4552622ce66f83
4
- data.tar.gz: aee9c30afa4e65ad9ecce338aeb2b331a53fd35f
3
+ metadata.gz: 32041a577502de9c77d37eb40ff8fc7110693b7f
4
+ data.tar.gz: 8202709105de66980ebd1901ec4106ab32d9899e
5
5
  SHA512:
6
- metadata.gz: 672801e1cd8849041f0736eb53d52f473b027e9f046f9669d351b36804c6ea6a3ae956552f393e8df9fbfca6bee980f7a54da64bdb147899d178243bbb36c6e3
7
- data.tar.gz: 9fd2658924b66145474edb62497f5bab9790e00a1b0793f7f058a3cb2dca8bc7ea2694d77b20cfe2bbb06368cfaeea3ac8ca9d0f7e9477d1fbd9e3374c691fa9
6
+ metadata.gz: f19534cf0741a0c400eff0c151f9f056bdb1df3f6043ed9c61e968c7ff0a99c8f07b5a24376f95e52d321dc27619fe72476b66657ea3d6a29b7ec0e83fb03c9e
7
+ data.tar.gz: d758c0e2af5abbad797f94d93f7c0ada74dea5ab070d785fea79ac72ee53d0c67cd7013d98107e11625cf411c0572a13b1dfa01be45f681ffa29a254bb426c9e
data/CHANGELOG.md CHANGED
@@ -1,7 +1,18 @@
1
1
  # Change Log
2
2
 
3
- ## [0.10.8](https://github.com/chef/train/tree/0.10.8) (2016-04-25)
4
- [Full Changelog](https://github.com/chef/train/compare/v0.10.7...0.10.8)
3
+ ## [0.11.0](https://github.com/chef/train/tree/0.11.0) (2016-04-28)
4
+ [Full Changelog](https://github.com/chef/train/compare/v0.10.8...0.11.0)
5
+
6
+ **Implemented enhancements:**
7
+
8
+ - Overhault file\(...\) and stat\(...\); point to destination of symlinks [\#92](https://github.com/chef/train/pull/92) ([arlimus](https://github.com/arlimus))
9
+
10
+ **Fixed bugs:**
11
+
12
+ - validate the backend configuration [\#91](https://github.com/chef/train/pull/91) ([arlimus](https://github.com/arlimus))
13
+
14
+ ## [v0.10.8](https://github.com/chef/train/tree/v0.10.8) (2016-04-25)
15
+ [Full Changelog](https://github.com/chef/train/compare/v0.10.7...v0.10.8)
5
16
 
6
17
  **Implemented enhancements:**
7
18
 
data/lib/train.rb CHANGED
@@ -62,6 +62,7 @@ module Train
62
62
 
63
63
  return conf if conf[:target].to_s.empty?
64
64
 
65
+ # split up the target's host/scheme configuration
65
66
  uri = URI.parse(conf[:target].to_s)
66
67
  unless uri.host.nil? and uri.scheme.nil?
67
68
  conf[:backend] ||= uri.scheme
@@ -79,6 +80,24 @@ module Train
79
80
  conf
80
81
  end
81
82
 
83
+ def self.validate_backend(conf, default = :local)
84
+ return default if conf.nil?
85
+ res = conf[:backend]
86
+ return res if !res.nil?
87
+
88
+ if !conf[:target].nil?
89
+ fail Train::UserError, 'Cannot determine backend from target '\
90
+ "configuration #{conf[:target].inspect}. Valid example: ssh://192.168.0.1."
91
+ end
92
+
93
+ if !conf[:host].nil?
94
+ fail Train::UserError, 'Host configured, but no backend was provided. Please '\
95
+ 'specify how you want to connect. Valid example: ssh://192.168.0.1.'
96
+ end
97
+
98
+ conf[:backend] = default
99
+ end
100
+
82
101
  def self.group_keys_and_keyfiles(conf)
83
102
  # in case the user specified a key-file, register it that way
84
103
  # we will clear the list of keys and put keys and key_files separately
@@ -10,14 +10,20 @@ module Train::Extras
10
10
  # interface methods: these fields should be implemented by every
11
11
  # backend File
12
12
  %w{
13
- exist? mode owner group link_target link_path content mtime size
14
- selinux_label product_version file_version path
13
+ exist? mode owner group uid gid content mtime size selinux_label path
14
+ product_version file_version
15
15
  }.each do |m|
16
16
  define_method m.to_sym do
17
17
  fail NotImplementedError, "File must implement the #{m}() method."
18
18
  end
19
19
  end
20
20
 
21
+ def initialize(backend, path, follow_symlink = true)
22
+ @backend = backend
23
+ @path = path
24
+ @follow_symlink = follow_symlink
25
+ end
26
+
21
27
  def type
22
28
  :unknown
23
29
  end
@@ -44,31 +50,39 @@ module Train::Extras
44
50
  # Additional methods for convenience
45
51
 
46
52
  def file?
47
- target_type == :file
53
+ type == :file
48
54
  end
49
55
 
50
56
  def block_device?
51
- target_type == :block_device
57
+ type == :block_device
52
58
  end
53
59
 
54
60
  def character_device?
55
- target_type == :character_device
61
+ type == :character_device
56
62
  end
57
63
 
58
64
  def socket?
59
- target_type == :socket
65
+ type == :socket
60
66
  end
61
67
 
62
68
  def directory?
63
- target_type == :directory
69
+ type == :directory
64
70
  end
65
71
 
66
72
  def symlink?
67
- type == :symlink
73
+ source.type == :symlink
74
+ end
75
+
76
+ def source
77
+ if @follow_symlink
78
+ self.class.new(@backend, @path, false)
79
+ else
80
+ self
81
+ end
68
82
  end
69
83
 
70
84
  def pipe?
71
- target_type == :pipe
85
+ type == :pipe
72
86
  end
73
87
 
74
88
  def mode?(sth)
@@ -87,6 +101,10 @@ module Train::Extras
87
101
  link_path == dst
88
102
  end
89
103
 
104
+ def link_path
105
+ symlink? ? path : nil
106
+ end
107
+
90
108
  def version?(version)
91
109
  product_version == version or
92
110
  file_version == version
@@ -123,15 +141,6 @@ module Train::Extras
123
141
  path[idx..-1]
124
142
  end
125
143
 
126
- def target_type
127
- # Just return the type unless this is a symlink
128
- return type unless type == :symlink
129
- # Get the link's target type, i.e. the real destination's type
130
- return link_target.type unless link_target.nil?
131
- # Return unknown if we don't know where this is pointing to
132
- :unknown
133
- end
134
-
135
144
  UNIX_MODE_OWNERS = {
136
145
  all: 00777,
137
146
  owner: 00700,
@@ -8,9 +8,8 @@ require 'train/extras/stat'
8
8
  module Train::Extras
9
9
  class UnixFile < FileCommon
10
10
  attr_reader :path
11
- def initialize(backend, path)
12
- @backend = backend
13
- @path = path
11
+ def initialize(backend, path, follow_symlink = true)
12
+ super(backend, path, follow_symlink)
14
13
  @spath = Shellwords.escape(@path)
15
14
  end
16
15
 
@@ -27,21 +26,15 @@ module Train::Extras
27
26
 
28
27
  def exist?
29
28
  @exist ||= (
30
- @backend.run_command("test -e #{@spath}")
29
+ f = @follow_symlink ? '' : " || test -L #{@spath}"
30
+ @backend.run_command("test -e #{@spath}"+f)
31
31
  .exit_status == 0
32
32
  )
33
33
  end
34
34
 
35
- def link_target
36
- return @link_target if defined? @link_target
37
- return @link_target = nil if link_path.nil?
38
- @link_target = @backend.file(link_path)
39
- end
40
-
41
- def link_path
42
- return nil unless symlink?
43
- @link_path ||=
44
- @backend.run_command("readlink #{@spath}").stdout.chomp
35
+ def path
36
+ return @path unless @follow_symlink && symlink?
37
+ @link_path ||= read_target_path
45
38
  end
46
39
 
47
40
  def mounted
@@ -50,7 +43,7 @@ module Train::Extras
50
43
  end
51
44
 
52
45
  %w{
53
- type mode owner group mtime size selinux_label
46
+ type mode owner group uid gid mtime size selinux_label
54
47
  }.each do |field|
55
48
  define_method field.to_sym do
56
49
  stat[field.to_sym]
@@ -67,7 +60,20 @@ module Train::Extras
67
60
 
68
61
  def stat
69
62
  return @stat if defined?(@stat)
70
- @stat = Train::Extras::Stat.stat(@spath, @backend)
63
+ @stat = Train::Extras::Stat.stat(@spath, @backend, @follow_symlink)
64
+ end
65
+
66
+ private
67
+
68
+ # Returns full path of a symlink target(real dest) or '' on symlink loop
69
+ def read_target_path
70
+ full_path = @backend.run_command("readlink -n #{@spath} -f").stdout
71
+ # Needed for some OSes like OSX that returns relative path
72
+ # when the link and target are in the same directory
73
+ if !full_path.start_with?('/') && full_path != ''
74
+ full_path = File.expand_path("../#{full_path}", @spath)
75
+ end
76
+ full_path
71
77
  end
72
78
  end
73
79
  end
@@ -11,9 +11,8 @@ require 'train/extras/stat'
11
11
  module Train::Extras
12
12
  class WindowsFile < FileCommon
13
13
  attr_reader :path
14
- def initialize(backend, path)
15
- @backend = backend
16
- @path = path
14
+ def initialize(backend, path, follow_symlink)
15
+ super(backend, path, follow_symlink)
17
16
  @spath = sanitize_filename(@path)
18
17
  end
19
18
 
@@ -44,10 +43,6 @@ module Train::Extras
44
43
  "(Test-Path -Path \"#{@spath}\").ToString()").stdout.chomp == 'True'
45
44
  end
46
45
 
47
- def link_target
48
- nil
49
- end
50
-
51
46
  def link_path
52
47
  nil
53
48
  end
@@ -57,11 +52,16 @@ module Train::Extras
57
52
  end
58
53
 
59
54
  def type
55
+ if attributes.include?('Archive')
56
+ return :file
57
+ elsif attributes.include?('Directory')
58
+ return :directory
59
+ end
60
60
  :unknown
61
61
  end
62
62
 
63
63
  %w{
64
- mode owner group mtime size selinux_label
64
+ mode owner group uid gid mtime size selinux_label
65
65
  }.each do |field|
66
66
  define_method field.to_sym do
67
67
  nil
@@ -85,16 +85,7 @@ module Train::Extras
85
85
  def attributes
86
86
  return @attributes if defined?(@attributes)
87
87
  @attributes = @backend.run_command(
88
- "(Get-ItemProperty -Path \"#{@spath}\").attributes.ToString()").stdout.chomp.split(',')
89
- end
90
-
91
- def target_type
92
- if attributes.include?('Archive')
93
- return :file
94
- elsif attributes.include?('Directory')
95
- return :directory
96
- end
97
- :unknown
88
+ "(Get-ItemProperty -Path \"#{@spath}\").attributes.ToString()").stdout.chomp.split(/\s*,\s*/)
98
89
  end
99
90
  end
100
91
  end
@@ -19,22 +19,23 @@ module Train::Extras
19
19
  res.nil? ? :unknown : res[0]
20
20
  end
21
21
 
22
- def self.stat(shell_escaped_path, backend)
22
+ def self.stat(shell_escaped_path, backend, follow_symlink)
23
23
  # use perl scripts for aix and solaris 10
24
24
  if backend.os.aix? || (backend.os.solaris? && backend.os[:release].to_i < 11)
25
- return aix_stat(shell_escaped_path, backend)
25
+ return aix_stat(shell_escaped_path, backend, follow_symlink)
26
26
  end
27
- return bsd_stat(shell_escaped_path, backend) if backend.os.bsd?
27
+ return bsd_stat(shell_escaped_path, backend, follow_symlink) if backend.os.bsd?
28
28
  # linux and solaris 11 will use standard linux stats
29
- return linux_stat(shell_escaped_path, backend) if backend.os.unix?
29
+ return linux_stat(shell_escaped_path, backend, follow_symlink) if backend.os.unix?
30
30
  # all other cases we don't handle
31
31
  # TODO: print an error if we get here, as it shouldn't be invoked
32
32
  # on non-unix
33
33
  {}
34
34
  end
35
35
 
36
- def self.linux_stat(shell_escaped_path, backend)
37
- res = backend.run_command("stat #{shell_escaped_path} 2>/dev/null --printf '%s\n%f\n%U\n%u\n%G\n%g\n%X\n%Y\n%C'")
36
+ def self.linux_stat(shell_escaped_path, backend, follow_symlink)
37
+ lstat = follow_symlink ? ' -L' : ''
38
+ res = backend.run_command("stat#{lstat} #{shell_escaped_path} 2>/dev/null --printf '%s\n%f\n%U\n%u\n%G\n%g\n%X\n%Y\n%C'")
38
39
 
39
40
  # ignore the exit_code: it is != 0 if selinux labels are not supported
40
41
  # on the system.
@@ -47,17 +48,19 @@ module Train::Extras
47
48
  selinux = nil if selinux == '?' or selinux == '(null)'
48
49
 
49
50
  {
50
- type: find_type(tmask),
51
- mode: tmask & 07777,
51
+ type: find_type(tmask),
52
+ mode: tmask & 07777,
52
53
  owner: fields[2],
54
+ uid: fields[3].to_i,
53
55
  group: fields[4],
56
+ gid: fields[5].to_i,
54
57
  mtime: fields[7].to_i,
55
- size: fields[0].to_i,
58
+ size: fields[0].to_i,
56
59
  selinux_label: selinux,
57
60
  }
58
61
  end
59
62
 
60
- def self.bsd_stat(shell_escaped_path, backend)
63
+ def self.bsd_stat(shell_escaped_path, backend, follow_symlink)
61
64
  # From stat man page on FreeBSD:
62
65
  # z The size of file in bytes (st_size).
63
66
  # p File type and permissions (st_mode).
@@ -72,8 +75,9 @@ module Train::Extras
72
75
  # in combination with:
73
76
  # ...
74
77
  # gu Display group or user name.
78
+ lstat = follow_symlink ? ' -L' : ''
75
79
  res = backend.run_command(
76
- "stat -f '%z\n%p\n%Su\n%u\n%Sg\n%g\n%a\n%m' "\
80
+ "stat#{lstat} -f '%z\n%p\n%Su\n%u\n%Sg\n%g\n%a\n%m' "\
77
81
  "#{shell_escaped_path}")
78
82
 
79
83
  return {} if res.exit_status != 0
@@ -84,24 +88,27 @@ module Train::Extras
84
88
  tmask = fields[1].to_i(8)
85
89
 
86
90
  {
87
- type: find_type(tmask),
88
- mode: tmask & 07777,
91
+ type: find_type(tmask),
92
+ mode: tmask & 07777,
89
93
  owner: fields[2],
94
+ uid: fields[3].to_i,
90
95
  group: fields[4],
96
+ gid: fields[5].to_i,
91
97
  mtime: fields[7].to_i,
92
- size: fields[0].to_i,
98
+ size: fields[0].to_i,
93
99
  selinux_label: fields[8],
94
100
  }
95
101
  end
96
102
 
97
- def self.aix_stat(shell_escaped_path, backend)
103
+ def self.aix_stat(shell_escaped_path, backend, follow_symlink)
98
104
  # Perl here b/c it is default on AIX
105
+ lstat = follow_symlink ? 'lstat' : 'stat'
99
106
  stat_cmd = <<-EOP
100
107
  perl -e '
101
- @a = lstat(shift) or exit 2;
108
+ @a = #{lstat}(shift) or exit 2;
102
109
  $u = getpwuid($a[4]);
103
110
  $g = getgrgid($a[5]);
104
- printf("0%o\\n%s\\n%s\\n%d\\n%d\\n", $a[2], $u, $g, $a[9], $a[7])
111
+ printf("0%o\\n%s\\n%d\\n%s\\n%d\\n%d\\n%d\\n", $a[2], $u, $a[4], $u, $a[5], $a[9], $a[7])
105
112
  ' #{shell_escaped_path}
106
113
  EOP
107
114
 
@@ -115,9 +122,11 @@ module Train::Extras
115
122
  type: find_type(tmask),
116
123
  mode: tmask & 07777,
117
124
  owner: fields[1],
118
- group: fields[2],
119
- mtime: fields[3].to_i,
120
- size: fields[4].to_i,
125
+ uid: fields[2].to_i,
126
+ group: fields[3],
127
+ gid: fields[4].to_i,
128
+ mtime: fields[5].to_i,
129
+ size: fields[6].to_i,
121
130
  selinux_label: nil,
122
131
  }
123
132
  end
@@ -23,7 +23,12 @@ class Train::Transports::Local::Connection
23
23
 
24
24
  def link_path
25
25
  return nil unless symlink?
26
- @link_path ||= ::File.readlink(@path)
26
+ begin
27
+ @link_path ||= ::File.realpath(@path)
28
+ rescue Errno::ELOOP => _
29
+ # Leave it blank on symbolic loop, same as readlink
30
+ @link_path = ''
31
+ end
27
32
  end
28
33
 
29
34
  def block_device?
@@ -52,7 +57,12 @@ class Train::Transports::Local::Connection
52
57
  return @stat if defined? @stat
53
58
 
54
59
  begin
55
- file_stat = ::File.lstat(@path)
60
+ file_stat =
61
+ if @follow_symlink
62
+ ::File.stat(@path)
63
+ else
64
+ ::File.lstat(@path)
65
+ end
56
66
  rescue StandardError => _err
57
67
  return @stat = {}
58
68
  end
@@ -63,10 +73,13 @@ class Train::Transports::Local::Connection
63
73
  mtime: file_stat.mtime.to_i,
64
74
  size: file_stat.size,
65
75
  owner: pw_username(file_stat.uid),
76
+ uid: file_stat.uid,
66
77
  group: pw_groupname(file_stat.gid),
78
+ gid: file_stat.gid,
67
79
  }
68
80
 
69
- res = @backend.run_command("stat #{@spath} 2>/dev/null --printf '%C'")
81
+ lstat = @follow_symlink ? ' -L' : ''
82
+ res = @backend.run_command("stat#{lstat} #{@spath} 2>/dev/null --printf '%C'")
70
83
  if res.exit_status == 0 && !res.stdout.empty? && res.stdout != '?'
71
84
  @stat[:selinux_label] = res.stdout.strip
72
85
  end
@@ -127,22 +127,21 @@ end
127
127
  class Train::Transports::Mock::Connection
128
128
  class File < FileCommon
129
129
  %w{
130
- exist? mode owner group link_target link_path content mtime size
130
+ exist? mode owner group link_path content mtime size
131
131
  selinux_label product_version file_version path type
132
132
  }.each do |m|
133
133
  attr_accessor m.tr('?', '').to_sym
134
134
  end
135
135
 
136
- def initialize(runtime, path)
137
- @path = path
136
+ def initialize(backend, path, follow_symlink = true)
137
+ super(backend, path, follow_symlink)
138
138
  @type = :unknown
139
139
  @exist = false
140
- @runtime = runtime
141
140
  end
142
141
 
143
142
  def mounted
144
143
  @mounted ||=
145
- @runtime.run_command("mount | grep -- ' on #{@path}'")
144
+ @backend.run_command("mount | grep -- ' on #{@path}'")
146
145
  end
147
146
  end
148
147
  end
data/lib/train/version.rb CHANGED
@@ -3,5 +3,5 @@
3
3
  # Author:: Dominik Richter (<dominik.richter@gmail.com>)
4
4
 
5
5
  module Train
6
- VERSION = '0.10.8'.freeze
6
+ VERSION = '0.11.0'.freeze
7
7
  end
@@ -22,8 +22,8 @@ describe 'file interface' do
22
22
  file.directory?.must_equal(false)
23
23
  end
24
24
 
25
- it 'has type :symlink' do
26
- file.type.must_equal(:symlink)
25
+ it 'has type :file' do
26
+ file.type.must_equal(:file)
27
27
  end
28
28
 
29
29
  it 'has content' do
@@ -34,16 +34,28 @@ describe 'file interface' do
34
34
  file.owner.must_equal('root')
35
35
  end
36
36
 
37
+ it 'has uid 0' do
38
+ file.uid.must_equal(0)
39
+ end
40
+
37
41
  it 'has group name' do
38
42
  file.group.must_equal(Test.root_group(backend.os))
39
43
  end
40
44
 
45
+ it 'has gid 0' do
46
+ file.gid.must_equal(0)
47
+ end
48
+
41
49
  it 'has mode 0777' do
42
- file.mode.must_equal(00777)
50
+ file.source.mode.must_equal(00777)
51
+ end
52
+
53
+ it 'has mode 0765' do
54
+ file.mode.must_equal(00765)
43
55
  end
44
56
 
45
- it 'checks mode? 0777' do
46
- file.mode?(00777).must_equal(true)
57
+ it 'checks mode? 0765' do
58
+ file.mode?(00765).must_equal(true)
47
59
  end
48
60
 
49
61
  it 'has link_path' do
@@ -5,6 +5,7 @@ require 'train/extras/file_common'
5
5
 
6
6
  describe 'file common' do
7
7
  let(:cls) { Train::Extras::FileCommon }
8
+ let(:new_cls) { cls.new(nil, nil, false) }
8
9
 
9
10
  def mockup(stubs)
10
11
  Class.new(cls) do
@@ -13,35 +14,35 @@ describe 'file common' do
13
14
  v
14
15
  end
15
16
  end
16
- end.new
17
+ end.new(nil, nil, false)
17
18
  end
18
19
 
19
20
  it 'has the default type of unknown' do
20
- cls.new.type.must_equal :unknown
21
+ new_cls.type.must_equal :unknown
21
22
  end
22
23
 
23
24
  it 'calculates md5sum from content' do
24
25
  content = 'hello world'
25
- cls.new.stub :content, content do |i|
26
+ new_cls.stub :content, content do |i|
26
27
  i.md5sum.must_equal '5eb63bbbe01eeed093cb22bb8f5acdc3'
27
28
  end
28
29
  end
29
30
 
30
31
  it 'sets md5sum of nil content to nil' do
31
- cls.new.stub :content, nil do |i|
32
+ new_cls.stub :content, nil do |i|
32
33
  i.md5sum.must_be_nil
33
34
  end
34
35
  end
35
36
 
36
37
  it 'calculates md5sum from content' do
37
38
  content = 'hello world'
38
- cls.new.stub :content, content do |i|
39
+ new_cls.stub :content, content do |i|
39
40
  i.sha256sum.must_equal 'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9'
40
41
  end
41
42
  end
42
43
 
43
44
  it 'sets sha256sum of nil content to nil' do
44
- cls.new.stub :content, nil do |i|
45
+ new_cls.stub :content, nil do |i|
45
46
  i.sha256sum.must_be_nil
46
47
  end
47
48
  end
@@ -11,9 +11,9 @@ describe 'file common' do
11
11
  backend
12
12
  }
13
13
 
14
- def mock_stat(path, out, err = '', code = 0)
14
+ def mock_stat(args, out, err = '', code = 0)
15
15
  backend.mock_command(
16
- "stat path 2>/dev/null --printf '%s\n%f\n%U\n%u\n%G\n%g\n%X\n%Y\n%C'",
16
+ "stat #{args} 2>/dev/null --printf '%s\n%f\n%U\n%u\n%G\n%g\n%X\n%Y\n%C'",
17
17
  out, err, code,
18
18
  )
19
19
  end
@@ -34,7 +34,7 @@ describe 'file common' do
34
34
 
35
35
  it 'reads file contents' do
36
36
  backend.mock_command('cat path || echo -n', '')
37
- mock_stat('path', '', 'some error...', 1)
37
+ mock_stat('-L path', '', 'some error...', 1)
38
38
  cls.new(backend, 'path').content.must_equal nil
39
39
  end
40
40
 
@@ -51,16 +51,8 @@ describe 'file common' do
51
51
  it 'retrieves the link path' do
52
52
  out = rand.to_s
53
53
  mock_stat('path', "13\na1ff\nz\n1001\nz\n1001\n1444573475\n1444573475\n?")
54
- backend.mock_command('readlink path', out)
55
- cls.new(backend, 'path').link_path.must_equal out
56
- end
57
-
58
- it 'retrieves the linked file' do
59
- out = rand.to_s
60
- mock_stat('path', "13\na1ff\nz\n1001\nz\n1001\n1444573475\n1444573475\n?")
61
- backend.mock_command('readlink path', out)
62
- f = backend.file(out)
63
- cls.new(backend, 'path').link_target.must_equal f
54
+ backend.mock_command('readlink -n path -f', out)
55
+ cls.new(backend, 'path').link_path.must_equal File.join(Dir.pwd, out)
64
56
  end
65
57
 
66
58
  it 'checks a mounted path' do
@@ -77,30 +69,84 @@ describe 'file common' do
77
69
  end
78
70
 
79
71
  describe 'stat on a file' do
80
- before { mock_stat('path', "13\na1ff\nz\n1001\nz\n1001\n1444573475\n1444573475\nlabels") }
72
+ before { mock_stat('-L path', "13\na1ff\nz\n1001\nz2\n1002\n1444573475\n1444573475\nlabels") }
73
+ let(:f) { cls.new(backend, 'path') }
74
+
75
+ it 'retrieves the file type' do
76
+ f.type.must_equal :symlink
77
+ end
78
+
79
+ it 'retrieves the file mode' do
80
+ f.mode.must_equal 00777
81
+ end
82
+
83
+ it 'retrieves the file owner' do
84
+ f.owner.must_equal 'z'
85
+ end
86
+
87
+ it 'retrieves the file uid' do
88
+ f.uid.must_equal 1001
89
+ end
90
+
91
+ it 'retrieves the file group' do
92
+ f.group.must_equal 'z2'
93
+ end
94
+
95
+ it 'retrieves the file gid' do
96
+ f.gid.must_equal 1002
97
+ end
98
+
99
+ it 'retrieves the file mtime' do
100
+ f.mtime.must_equal 1444573475
101
+ end
102
+
103
+ it 'retrieves the file size' do
104
+ f.size.must_equal 13
105
+ end
106
+
107
+ it 'retrieves the file selinux_label' do
108
+ f.selinux_label.must_equal 'labels'
109
+ end
110
+ end
111
+
112
+ describe 'stat on the source file' do
113
+ before { mock_stat('path', "13\na1ff\nz\n1001\nz2\n1002\n1444573475\n1444573475\nlabels") }
114
+ let(:f) { cls.new(backend, 'path').source }
81
115
 
82
116
  it 'retrieves the file type' do
83
- cls.new(backend, 'path').type.must_equal :symlink
117
+ f.type.must_equal :symlink
84
118
  end
85
119
 
86
120
  it 'retrieves the file mode' do
87
- cls.new(backend, 'path').mode.must_equal 00777
121
+ f.mode.must_equal 00777
88
122
  end
89
123
 
90
124
  it 'retrieves the file owner' do
91
- cls.new(backend, 'path').owner.must_equal 'z'
125
+ f.owner.must_equal 'z'
126
+ end
127
+
128
+ it 'retrieves the file uid' do
129
+ f.uid.must_equal 1001
130
+ end
131
+
132
+ it 'retrieves the file group' do
133
+ f.group.must_equal 'z2'
134
+ end
135
+
136
+ it 'retrieves the file gid' do
137
+ f.gid.must_equal 1002
92
138
  end
93
139
 
94
140
  it 'retrieves the file mtime' do
95
- cls.new(backend, 'path').mtime.must_equal 1444573475
141
+ f.mtime.must_equal 1444573475
96
142
  end
97
143
 
98
144
  it 'retrieves the file size' do
99
- cls.new(backend, 'path').size.must_equal 13
145
+ f.size.must_equal 13
100
146
  end
101
147
 
102
148
  it 'retrieves the file selinux_label' do
103
- cls.new(backend, 'path').selinux_label.must_equal 'labels'
149
+ f.selinux_label.must_equal 'labels'
104
150
  end
105
151
  end
106
152
  end
@@ -48,19 +48,21 @@ describe 'stat' do
48
48
  res = Minitest::Mock.new
49
49
  res.expect :stdout, ''
50
50
  backend.expect :run_command, res, [String]
51
- cls.linux_stat('/path', backend).must_equal({})
51
+ cls.linux_stat('/path', backend, false).must_equal({})
52
52
  end
53
53
 
54
54
  it 'reads correct stat results' do
55
55
  res = Minitest::Mock.new
56
56
  # 43ff is 41777; linux_stat strips the 4
57
- res.expect :stdout, "360\n43ff\nroot\n0\nroot\n0\n1444520846\n1444522445\n?"
57
+ res.expect :stdout, "360\n43ff\nroot\n0\nrootz\n1\n1444520846\n1444522445\n?"
58
58
  backend.expect :run_command, res, [String]
59
- cls.linux_stat('/path', backend).must_equal({
59
+ cls.linux_stat('/path', backend, false).must_equal({
60
60
  type: :directory,
61
61
  mode: 01777,
62
62
  owner: 'root',
63
- group: 'root',
63
+ uid: 0,
64
+ group: 'rootz',
65
+ gid: 1,
64
66
  mtime: 1444522445,
65
67
  size: 360,
66
68
  selinux_label: nil,
@@ -76,7 +78,7 @@ describe 'stat' do
76
78
  res.expect :stdout, '.....'
77
79
  res.expect :exit_status, 1
78
80
  backend.expect :run_command, res, [String]
79
- cls.bsd_stat('/path', backend).must_equal({})
81
+ cls.bsd_stat('/path', backend, false).must_equal({})
80
82
  end
81
83
 
82
84
  it 'ignores wrong stat results' do
@@ -84,19 +86,21 @@ describe 'stat' do
84
86
  res.expect :stdout, ''
85
87
  res.expect :exit_status, 0
86
88
  backend.expect :run_command, res, [String]
87
- cls.bsd_stat('/path', backend).must_equal({})
89
+ cls.bsd_stat('/path', backend, false).must_equal({})
88
90
  end
89
91
 
90
92
  it 'reads correct stat results' do
91
93
  res = Minitest::Mock.new
92
- res.expect :stdout, "360\n41777\nroot\n0\nroot\n0\n1444520846\n1444522445"
94
+ res.expect :stdout, "360\n41777\nroot\n0\nrootz\n1\n1444520846\n1444522445"
93
95
  res.expect :exit_status, 0
94
96
  backend.expect :run_command, res, [String]
95
- cls.bsd_stat('/path', backend).must_equal({
97
+ cls.bsd_stat('/path', backend, false).must_equal({
96
98
  type: :directory,
97
99
  mode: 01777,
98
100
  owner: 'root',
99
- group: 'root',
101
+ uid: 0,
102
+ group: 'rootz',
103
+ gid: 1,
100
104
  mtime: 1444522445,
101
105
  size: 360,
102
106
  selinux_label: nil,
@@ -12,15 +12,15 @@ describe 'file common' do
12
12
  }
13
13
 
14
14
  it 'provides the full path' do
15
- cls.new(backend, 'C:\dir\file').path.must_equal 'C:\dir\file'
15
+ cls.new(backend, 'C:\dir\file', false).path.must_equal 'C:\dir\file'
16
16
  end
17
17
 
18
18
  it 'provides the basename to a unix path' do
19
- cls.new(backend, 'C:\dir\file').basename.must_equal 'file'
19
+ cls.new(backend, 'C:\dir\file', false).basename.must_equal 'file'
20
20
  end
21
21
 
22
22
  it 'provides the full path with whitespace' do
23
- wf = cls.new(backend, 'C:\Program Files\file name')
23
+ wf = cls.new(backend, 'C:\Program Files\file name', false)
24
24
  wf.path.must_equal 'C:\Program Files\file name'
25
25
  wf.basename.must_equal 'file name'
26
26
  end
@@ -28,11 +28,11 @@ describe 'file common' do
28
28
  it 'reads file contents' do
29
29
  out = rand.to_s
30
30
  backend.mock_command('Get-Content("path") | Out-String', out)
31
- cls.new(backend, 'path').content.must_equal out
31
+ cls.new(backend, 'path', false).content.must_equal out
32
32
  end
33
33
 
34
34
  it 'check escaping of invalid chars in path' do
35
- wf = cls.new(nil, nil)
35
+ wf = cls.new(nil, nil, false)
36
36
  wf.sanitize_filename('c:/test') .must_equal 'c:/test'
37
37
  wf.sanitize_filename('c:/test directory') .must_equal 'c:/test directory'
38
38
  %w{ < > " * ?}.each do |char|
@@ -129,4 +129,28 @@ describe Train do
129
129
  res.must_equal nu
130
130
  end
131
131
  end
132
+
133
+ describe '#validate_backend' do
134
+ it 'just returns the backend if it is provided' do
135
+ x = rand
136
+ Train.validate_backend({ backend: x }).must_equal x
137
+ end
138
+
139
+ it 'returns the local backend if nothing was provided' do
140
+ Train.validate_backend({}).must_equal :local
141
+ end
142
+
143
+ it 'returns the default backend if nothing was provided' do
144
+ x = rand
145
+ Train.validate_backend({}, x).must_equal x
146
+ end
147
+
148
+ it 'fails if no backend was given but a target is provided' do
149
+ proc { Train.validate_backend({ target: rand }) }.must_raise Train::UserError
150
+ end
151
+
152
+ it 'fails if no backend was given but a host is provided' do
153
+ proc { Train.validate_backend({ host: rand }) }.must_raise Train::UserError
154
+ end
155
+ end
132
156
  end
@@ -42,7 +42,7 @@ describe 'local file transport' do
42
42
 
43
43
  it 'checks a file\'s link path' do
44
44
  out = rand.to_s
45
- File.stub :readlink, out do
45
+ File.stub :realpath, out do
46
46
  File.stub :symlink?, true do
47
47
  connection.file(rand.to_s).link_path.must_equal out
48
48
  end
@@ -51,7 +51,9 @@ describe 'local file transport' do
51
51
 
52
52
  describe 'file metadata' do
53
53
  let(:stat) { Struct.new(:mode, :size, :mtime, :uid, :gid) }
54
- let(:statres) { stat.new(00140755, rand, (rand*100).to_i, rand, rand) }
54
+ let(:uid) { rand }
55
+ let(:gid) { rand }
56
+ let(:statres) { stat.new(00140755, rand, (rand*100).to_i, uid, gid) }
55
57
 
56
58
  def meta_stub(method, param, &block)
57
59
  pwres = Struct.new(:name)
@@ -63,43 +65,55 @@ describe 'local file transport' do
63
65
  end
64
66
 
65
67
  it 'recognizes type' do
66
- meta_stub :lstat, statres do
68
+ meta_stub :stat, statres do
67
69
  connection.file(rand.to_s).type.must_equal :socket
68
70
  end
69
71
  end
70
72
 
71
73
  it 'recognizes mode' do
72
- meta_stub :lstat, statres do
74
+ meta_stub :stat, statres do
73
75
  connection.file(rand.to_s).mode.must_equal 00755
74
76
  end
75
77
  end
76
78
 
77
79
  it 'recognizes mtime' do
78
- meta_stub :lstat, statres do
80
+ meta_stub :stat, statres do
79
81
  connection.file(rand.to_s).mtime.must_equal statres.mtime
80
82
  end
81
83
  end
82
84
 
83
85
  it 'recognizes size' do
84
- meta_stub :lstat, statres do
86
+ meta_stub :stat, statres do
85
87
  connection.file(rand.to_s).size.must_equal statres.size
86
88
  end
87
89
  end
88
90
 
89
91
  it 'recognizes owner' do
90
- meta_stub :lstat, statres do
92
+ meta_stub :stat, statres do
91
93
  connection.file(rand.to_s).owner.must_equal 'owner'
92
94
  end
93
95
  end
94
96
 
97
+ it 'recognizes uid' do
98
+ meta_stub :stat, statres do
99
+ connection.file(rand.to_s).uid.must_equal uid
100
+ end
101
+ end
102
+
95
103
  it 'recognizes group' do
96
- meta_stub :lstat, statres do
104
+ meta_stub :stat, statres do
97
105
  connection.file(rand.to_s).group.must_equal 'group'
98
106
  end
99
107
  end
100
108
 
109
+ it 'recognizes gid' do
110
+ meta_stub :stat, statres do
111
+ connection.file(rand.to_s).gid.must_equal gid
112
+ end
113
+ end
114
+
101
115
  it 'recognizes selinux label' do
102
- meta_stub :lstat, statres do
116
+ meta_stub :stat, statres do
103
117
  label = rand.to_s
104
118
  res = Train::Extras::CommandResult.new(label, nil, 0)
105
119
  connection.stub :run_command, res do
@@ -107,6 +121,64 @@ describe 'local file transport' do
107
121
  end
108
122
  end
109
123
  end
124
+
125
+ it 'recognizes source type' do
126
+ meta_stub :lstat, statres do
127
+ connection.file(rand.to_s).source.type.must_equal :socket
128
+ end
129
+ end
130
+
131
+ it 'recognizes source mode' do
132
+ meta_stub :lstat, statres do
133
+ connection.file(rand.to_s).source.mode.must_equal 00755
134
+ end
135
+ end
136
+
137
+ it 'recognizes source mtime' do
138
+ meta_stub :lstat, statres do
139
+ connection.file(rand.to_s).source.mtime.must_equal statres.mtime
140
+ end
141
+ end
142
+
143
+ it 'recognizes source size' do
144
+ meta_stub :lstat, statres do
145
+ connection.file(rand.to_s).source.size.must_equal statres.size
146
+ end
147
+ end
148
+
149
+ it 'recognizes source owner' do
150
+ meta_stub :lstat, statres do
151
+ connection.file(rand.to_s).source.owner.must_equal 'owner'
152
+ end
153
+ end
154
+
155
+ it 'recognizes source uid' do
156
+ meta_stub :lstat, statres do
157
+ connection.file(rand.to_s).source.uid.must_equal uid
158
+ end
159
+ end
160
+
161
+ it 'recognizes source owner' do
162
+ meta_stub :lstat, statres do
163
+ connection.file(rand.to_s).source.owner.must_equal 'owner'
164
+ end
165
+ end
166
+
167
+ it 'recognizes source gid' do
168
+ meta_stub :lstat, statres do
169
+ connection.file(rand.to_s).source.gid.must_equal gid
170
+ end
171
+ end
172
+
173
+ it 'recognizes source selinux label' do
174
+ meta_stub :lstat, statres do
175
+ label = rand.to_s
176
+ res = Train::Extras::CommandResult.new(label, nil, 0)
177
+ connection.stub :run_command, res do
178
+ connection.file(rand.to_s).source.selinux_label.must_equal label
179
+ end
180
+ end
181
+ end
110
182
  end
111
183
 
112
184
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: r-train
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.8
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dominik Richter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-25 00:00:00.000000000 Z
11
+ date: 2016-04-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -238,7 +238,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
238
238
  version: '0'
239
239
  requirements: []
240
240
  rubyforge_project:
241
- rubygems_version: 2.4.6
241
+ rubygems_version: 2.5.1
242
242
  signing_key:
243
243
  specification_version: 4
244
244
  summary: Transport interface to talk to different backends.