r-train 0.10.8 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -2
- data/lib/train.rb +19 -0
- data/lib/train/extras/file_common.rb +27 -18
- data/lib/train/extras/file_unix.rb +22 -16
- data/lib/train/extras/file_windows.rb +9 -18
- data/lib/train/extras/stat.rb +29 -20
- data/lib/train/transports/local_file.rb +16 -3
- data/lib/train/transports/mock.rb +4 -5
- data/lib/train/version.rb +1 -1
- data/test/integration/tests/path_symlink_test.rb +17 -5
- data/test/unit/extras/file_common_test.rb +7 -6
- data/test/unit/extras/linux_file_test.rb +66 -20
- data/test/unit/extras/stat_test.rb +13 -9
- data/test/unit/extras/windows_file_test.rb +5 -5
- data/test/unit/train_test.rb +24 -0
- data/test/unit/transports/local_file_test.rb +81 -9
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 32041a577502de9c77d37eb40ff8fc7110693b7f
|
4
|
+
data.tar.gz: 8202709105de66980ebd1901ec4106ab32d9899e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
4
|
-
[Full Changelog](https://github.com/chef/train/compare/v0.10.
|
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
|
14
|
-
|
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
|
-
|
53
|
+
type == :file
|
48
54
|
end
|
49
55
|
|
50
56
|
def block_device?
|
51
|
-
|
57
|
+
type == :block_device
|
52
58
|
end
|
53
59
|
|
54
60
|
def character_device?
|
55
|
-
|
61
|
+
type == :character_device
|
56
62
|
end
|
57
63
|
|
58
64
|
def socket?
|
59
|
-
|
65
|
+
type == :socket
|
60
66
|
end
|
61
67
|
|
62
68
|
def directory?
|
63
|
-
|
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
|
-
|
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
|
-
|
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
|
-
@
|
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
|
36
|
-
return @
|
37
|
-
|
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
|
-
|
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
|
data/lib/train/extras/stat.rb
CHANGED
@@ -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
|
-
|
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:
|
51
|
-
mode:
|
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:
|
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:
|
88
|
-
mode:
|
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:
|
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, $
|
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
|
-
|
119
|
-
|
120
|
-
|
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
|
-
|
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 =
|
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
|
-
|
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
|
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(
|
137
|
-
|
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
|
-
@
|
144
|
+
@backend.run_command("mount | grep -- ' on #{@path}'")
|
146
145
|
end
|
147
146
|
end
|
148
147
|
end
|
data/lib/train/version.rb
CHANGED
@@ -22,8 +22,8 @@ describe 'file interface' do
|
|
22
22
|
file.directory?.must_equal(false)
|
23
23
|
end
|
24
24
|
|
25
|
-
it 'has type :
|
26
|
-
file.type.must_equal(:
|
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?
|
46
|
-
file.mode?(
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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(
|
14
|
+
def mock_stat(args, out, err = '', code = 0)
|
15
15
|
backend.mock_command(
|
16
|
-
"stat
|
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\
|
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
|
-
|
117
|
+
f.type.must_equal :symlink
|
84
118
|
end
|
85
119
|
|
86
120
|
it 'retrieves the file mode' do
|
87
|
-
|
121
|
+
f.mode.must_equal 00777
|
88
122
|
end
|
89
123
|
|
90
124
|
it 'retrieves the file owner' do
|
91
|
-
|
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
|
-
|
141
|
+
f.mtime.must_equal 1444573475
|
96
142
|
end
|
97
143
|
|
98
144
|
it 'retrieves the file size' do
|
99
|
-
|
145
|
+
f.size.must_equal 13
|
100
146
|
end
|
101
147
|
|
102
148
|
it 'retrieves the file selinux_label' do
|
103
|
-
|
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\
|
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
|
-
|
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\
|
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
|
-
|
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|
|
data/test/unit/train_test.rb
CHANGED
@@ -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 :
|
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(:
|
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 :
|
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 :
|
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 :
|
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 :
|
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 :
|
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 :
|
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 :
|
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.
|
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-
|
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.
|
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.
|