machinery-tool 1.15.0 → 1.16.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.git_revision +1 -1
- data/NEWS +21 -0
- data/export_helpers/autoyast_export_readme.md +4 -4
- data/html/assets/compare/machinery.js +12 -0
- data/html/assets/machinery.css +1 -0
- data/html/partials/compare/services.html.haml +1 -1
- data/html/partials/compare/summary.html.haml +1 -1
- data/html/partials/compare/unmanaged_files.html.haml +1 -1
- data/inspect_helpers/changed_files.sh +1 -60
- data/lib/autoyast.rb +9 -4
- data/lib/cli.rb +18 -7
- data/lib/docker_system.rb +3 -7
- data/lib/helper.rb +1 -18
- data/lib/html.rb +1 -1
- data/lib/inspector.rb +0 -26
- data/lib/machinery.rb +1 -1
- data/lib/{changed_rpm_files_helper.rb → rpm_database.rb} +68 -15
- data/lib/scope_file_access_archive.rb +1 -1
- data/lib/scope_file_access_flat.rb +1 -1
- data/lib/system.rb +38 -0
- data/lib/tarball.rb +1 -1
- data/lib/version.rb +1 -1
- data/machinery-helper/README.md +1 -1
- data/machinery-helper/version.go +1 -1
- data/man/generated/machinery.1.gz +0 -0
- data/man/generated/machinery.1.html +7 -3
- data/plugins/changed_managed_files/changed_managed_files_inspector.rb +4 -64
- data/plugins/config_files/config_files_inspector.rb +8 -52
- data/plugins/os/os_inspector.rb +5 -0
- data/plugins/os/os_model.rb +14 -0
- data/plugins/users/users_inspector.rb +1 -0
- data/tools/helper_builder.rb +3 -2
- 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: a4b7c4fc1cda138bae3373d5557bb65bd30481a5
|
4
|
+
data.tar.gz: c9f706f3cb00c6eac9c8a3b01eaf0ba1a5345704
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3806f42441561cc01b2b6c927627ab69b673feb65194bacedd7d460507af95070cf8ae3d087560b40ea2d9c64f158825469030a129d78458555af6137435393
|
7
|
+
data.tar.gz: 2212be5d36387f90dbb05b66b31cc1af51ae829a39b9208d04b8218e93590c05ae6eb21bba91db285c3f45d8e2823f35706d6d460d69054432abcfa34235e8cd
|
data/.git_revision
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
f5f2a71bd21d86dcbc1b90e30b8af0f466b335bd
|
data/NEWS
CHANGED
@@ -1,6 +1,27 @@
|
|
1
1
|
# Machinery Release Notes
|
2
2
|
|
3
3
|
|
4
|
+
## Version 1.16.0 - Fri Nov 06 14:20:47 CET 2015 - thardeck@suse.de
|
5
|
+
|
6
|
+
* The error message "There is already a server with the same port running" has
|
7
|
+
been cleaned up and is no longer misleading (gh#SUSE/machinery#1541)
|
8
|
+
* Add OpenSUSE Leap as supported system
|
9
|
+
* HTML comparison is now available without the experimental feature flag
|
10
|
+
* Add error message to recommend using `--name` when a container with a slash is
|
11
|
+
inspected (gh#SUSE/machinery#1490)
|
12
|
+
* Fixed crash during inspection of a folder which contains special characters
|
13
|
+
in its name (gh#SUSE/machinery#1580)
|
14
|
+
* Speed up inspection of config and changed managed files
|
15
|
+
* Fix docker inspection when specifying a docker image with tag
|
16
|
+
(gh#SUSE/machinery#1491)
|
17
|
+
* GConf XML files are no longer treated as binary files in HTML view
|
18
|
+
(gh#SUSE/machinery#1154)
|
19
|
+
* Add current outgoing ip and export directory to Autoyast README.md during
|
20
|
+
export (gh#SUSE/machinery#1543)
|
21
|
+
* Handle invalid utf-8 characters in /etc/passwd
|
22
|
+
* Fix: "changed" link in compare view is visible while scope is collapsed but
|
23
|
+
doesn't do anything (gh#SUSE/machinery#1555)
|
24
|
+
|
4
25
|
## Version 1.15.0 - Mon Oct 26 17:42:25 CET 2015 - thardeck@suse.de
|
5
26
|
|
6
27
|
* No information is cut off in the HTML view
|
@@ -11,18 +11,18 @@ The export directory contains both the AutoYaST profile and additional data that
|
|
11
11
|
is required during installation. This directory needs to be made available to
|
12
12
|
the installer via network, e.g. by running:
|
13
13
|
|
14
|
-
cd
|
14
|
+
cd <path>; python -m SimpleHTTPServer
|
15
15
|
|
16
16
|
You can then point the installer to the profile by specifying the AutoYaST
|
17
17
|
option on the kernel command line.
|
18
18
|
|
19
19
|
For SLES12 and openSUSE 13.2:
|
20
20
|
|
21
|
-
autoyast2=http
|
21
|
+
autoyast2=http://<ip>:8000/autoinst.xml
|
22
22
|
|
23
23
|
For SLES11:
|
24
24
|
|
25
|
-
autoyast=http
|
25
|
+
autoyast=http://<ip>:8000/autoinst.xml netsetup=dhcp
|
26
26
|
|
27
27
|
## Changing permissions of the AutoYaST export
|
28
28
|
|
@@ -33,7 +33,7 @@ The installation via for example an HTTP server is only possible if all files
|
|
33
33
|
and sub directories are readable by the HTTP server user.
|
34
34
|
To make the export directory readable for all users run:
|
35
35
|
|
36
|
-
chmod -R a+rX
|
36
|
+
chmod -R a+rX <path>
|
37
37
|
|
38
38
|
## Reference
|
39
39
|
|
@@ -70,6 +70,18 @@ $(document).ready(function () {
|
|
70
70
|
return false;
|
71
71
|
});
|
72
72
|
|
73
|
+
$(".show-changed-elements").click(function(){
|
74
|
+
$scope = $(this).closest(".scope");
|
75
|
+
if ($scope.find(".toggle").hasClass("collapsed")){
|
76
|
+
$scope.find(".scope_content").collapse("show");
|
77
|
+
$scope.find(".toggle").removeClass("collapsed");
|
78
|
+
}
|
79
|
+
if ($(this).attr("href")){
|
80
|
+
$('html,body').animate({scrollTop: $($(this).attr("href")).offset().top}, 'slow');
|
81
|
+
}
|
82
|
+
return false;
|
83
|
+
});
|
84
|
+
|
73
85
|
// Unmanaged files diffs
|
74
86
|
$("#diff-unmanaged-files").on("show.bs.modal", function(e) {
|
75
87
|
var trigger = $(e.relatedTarget);
|
data/html/assets/machinery.css
CHANGED
@@ -25,7 +25,7 @@
|
|
25
25
|
(#{@diff["services"].only_in2.init_system})
|
26
26
|
- if @diff["services"].changed && @diff["services"].changed.length > 0
|
27
27
|
%span.summary-part
|
28
|
-
%a{ href: "#services_changed" }
|
28
|
+
%a.show-changed-elements{ href: "#services_changed" }
|
29
29
|
changed
|
30
30
|
= ": #{pluralize_scope(@diff["services"].changed, "service", "services")}"
|
31
31
|
- if @diff["services"].common && @diff["services"].common.length > 0
|
@@ -7,7 +7,7 @@
|
|
7
7
|
#{@description_b.name}: #{pluralize_scope(@diff[scope].only_in2, singular, plural)}
|
8
8
|
- if @diff[scope].changed && @diff[scope].changed.length > 0
|
9
9
|
%span.summary-part
|
10
|
-
%a{ href: "#" + scope + "_changed" }
|
10
|
+
%a.show-changed-elements{ href: "#" + scope + "_changed" }
|
11
11
|
changed
|
12
12
|
= ": #{pluralize_scope(@diff[scope].changed, singular, plural)}"
|
13
13
|
- if @diff[scope].common && @diff[scope].common.length > 0
|
@@ -23,7 +23,7 @@
|
|
23
23
|
= pluralize_scope(@diff["unmanaged_files"].only_in2, "file", "files")
|
24
24
|
- if @diff["unmanaged_files"].changed
|
25
25
|
%span.summary-part
|
26
|
-
%a{ href: "#unmanaged_files_changed" }
|
26
|
+
%a.show-changed-elements{ href: "#unmanaged_files_changed" }
|
27
27
|
changed
|
28
28
|
= ": #{pluralize_scope(@diff["unmanaged_files"].changed, "file", "files")}"
|
29
29
|
- if @diff["unmanaged_files"].common
|
@@ -27,24 +27,6 @@
|
|
27
27
|
# Exit immediately if a command exits with a non-zero status.
|
28
28
|
set -e
|
29
29
|
|
30
|
-
scope=$1
|
31
|
-
package=$2 # for scope config-files only
|
32
|
-
|
33
|
-
if [[ -z "$scope" || "$scope" != "config-files" && "$scope" != "changed-managed-files" ]]; then
|
34
|
-
echo "Error: Expect scope 'config-files' or 'changed-managed-files' as first argument." >&2
|
35
|
-
exit 1
|
36
|
-
fi
|
37
|
-
|
38
|
-
if [[ "$scope" == "changed-managed-files" && $# != 1 ]]; then
|
39
|
-
echo "Error: Expect no extra arguments after changed-managed-files " >&2
|
40
|
-
exit 1
|
41
|
-
fi
|
42
|
-
|
43
|
-
if [[ "$scope" == "config-files" && $# != 2 ]]; then
|
44
|
-
echo "Error: Expect one argument with package name after config-files" >&2
|
45
|
-
exit 1
|
46
|
-
fi
|
47
|
-
|
48
30
|
if [ $UID -ne "0" ]; then
|
49
31
|
sudoprefix="sudo -n"
|
50
32
|
fi
|
@@ -61,45 +43,4 @@ package_contains_verify_script () {
|
|
61
43
|
rpm --scripts -q $1 | grep "verify scriptlet" > /dev/null
|
62
44
|
}
|
63
45
|
|
64
|
-
|
65
|
-
# rpm returns 1 as exit code when modified config files are detected
|
66
|
-
# that's why we explicitly detect if sudo failed
|
67
|
-
regex="^sudo:.*password is required"
|
68
|
-
if [[ "$1" =~ $regex ]]; then
|
69
|
-
echo "$1" >&2
|
70
|
-
exit 1
|
71
|
-
fi
|
72
|
-
}
|
73
|
-
|
74
|
-
inspect_package () {
|
75
|
-
package=$1
|
76
|
-
output=`$sudoprefix rpm -V --nodeps --nodigest --nosignature --nomtime $noscripts $package 2>&1 || true`
|
77
|
-
|
78
|
-
check_output "$output"
|
79
|
-
|
80
|
-
if [ -z "$noscripts" ] && ( package_contains_verify_script $package ); then
|
81
|
-
# remove the lines printed by verify scripts, because we cannot parse these lines
|
82
|
-
# in certain rpm versions verify scripts cannot be turned off
|
83
|
-
lines=`$sudoprefix rpm -V --nodeps --nodigest --nosignature --nomtime --nofiles $package | wc -l`
|
84
|
-
output=`echo -e "$output" | head -n-${lines}`
|
85
|
-
fi
|
86
|
-
|
87
|
-
if [ -n "$output" ]; then
|
88
|
-
if [ $scope == "changed-managed-files" ]; then
|
89
|
-
echo -e "$package:\\n$output";
|
90
|
-
else
|
91
|
-
echo -e "$output"
|
92
|
-
fi
|
93
|
-
fi
|
94
|
-
}
|
95
|
-
|
96
|
-
if [ $scope == "changed-managed-files" ]; then
|
97
|
-
for package in `rpm -qa --queryformat "%{NAME}-%{VERSION}\\n"`; do
|
98
|
-
inspect_package $package
|
99
|
-
done
|
100
|
-
fi
|
101
|
-
|
102
|
-
if [ $scope == "config-files" ]; then
|
103
|
-
inspect_package $package
|
104
|
-
fi
|
105
|
-
|
46
|
+
$sudoprefix rpm -Va --nodeps --nodigest --nosignature --nomtime $noscripts || true
|
data/lib/autoyast.rb
CHANGED
@@ -43,10 +43,10 @@ class Autoyast < Exporter
|
|
43
43
|
output_dir
|
44
44
|
)
|
45
45
|
FileUtils.chmod(0600, File.join(output_dir, "unmanaged_files_#{@name}_excludes"))
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
)
|
46
|
+
readme = File.read(File.join(Machinery::ROOT, "export_helpers/autoyast_export_readme.md"))
|
47
|
+
readme.gsub!("<ip>", outgoing_ip)
|
48
|
+
readme.gsub!("<path>", output_dir)
|
49
|
+
File.write(File.join(output_dir, "README.md"), readme)
|
50
50
|
Dir["#{@system_description.description_path}/*"].each do |content|
|
51
51
|
FileUtils.cp_r(content, output_dir, preserve: true)
|
52
52
|
end
|
@@ -101,6 +101,11 @@ class Autoyast < Exporter
|
|
101
101
|
builder.to_xml
|
102
102
|
end
|
103
103
|
|
104
|
+
def outgoing_ip
|
105
|
+
output = Cheetah.run("ip", "route", "get", "8.8.8.8", stdout: :capture)
|
106
|
+
output[/ src ([\d\.:]+)\s*$/, 1] || "<ip>"
|
107
|
+
end
|
108
|
+
|
104
109
|
private
|
105
110
|
|
106
111
|
def apply_software_settings(xml)
|
data/lib/cli.rb
CHANGED
@@ -154,6 +154,16 @@ class Cli
|
|
154
154
|
true
|
155
155
|
end
|
156
156
|
|
157
|
+
def self.check_container_name!(image, name)
|
158
|
+
if image.include?("/") && name.include?("/")
|
159
|
+
raise Machinery::Errors::InvalidCommandLine.new(
|
160
|
+
"Error: System description name '#{name}' is invalid. By default Machinery" \
|
161
|
+
" uses the image name as description name if the parameter `--name` is not" \
|
162
|
+
" provided.\nIf the image name consist a slash the `--name=NAME` parameter" \
|
163
|
+
" is mandatory. Valid characters are 'a-zA-Z0-9_:.-'."
|
164
|
+
)
|
165
|
+
end
|
166
|
+
end
|
157
167
|
|
158
168
|
on_error do |e|
|
159
169
|
Cli.handle_error(e)
|
@@ -177,7 +187,8 @@ class Cli
|
|
177
187
|
if scopes
|
178
188
|
if exclude_scopes
|
179
189
|
# scope and exclude-scope
|
180
|
-
raise Machinery::Errors::InvalidCommandLine.new(
|
190
|
+
raise Machinery::Errors::InvalidCommandLine.new("You cannot provide the --scope and " \
|
191
|
+
"--exclude-scope option at the same time.")
|
181
192
|
else
|
182
193
|
# scope only
|
183
194
|
scope_list = parse_scopes(scopes)
|
@@ -274,8 +285,8 @@ class Cli
|
|
274
285
|
LONGDESC
|
275
286
|
arg "NAME"
|
276
287
|
command "analyze" do |c|
|
277
|
-
c.flag [:operation, :o], type: String, required:
|
278
|
-
desc: "The analyze operation to perform", arg_name: "OPERATION"
|
288
|
+
c.flag [:operation, :o], type: String, required: false,
|
289
|
+
desc: "The analyze operation to perform", arg_name: "OPERATION", default_value: "config-file-diffs"
|
279
290
|
|
280
291
|
c.action do |global_options,options,args|
|
281
292
|
name = shift_arg(args, "NAME")
|
@@ -347,10 +358,8 @@ class Cli
|
|
347
358
|
desc: "Exclude specified scopes", arg_name: "SCOPE_LIST"
|
348
359
|
c.switch "show-all", required: false, negatable: false,
|
349
360
|
desc: "Show also common properties"
|
350
|
-
|
351
|
-
|
352
|
-
desc: "Open comparison in HTML format in your web browser."
|
353
|
-
end
|
361
|
+
c.switch "html", required: false, negatable: false,
|
362
|
+
desc: "Open comparison in HTML format in your web browser."
|
354
363
|
c.switch "pager", required: false, default_value: true,
|
355
364
|
desc: "Pipe output into a pager"
|
356
365
|
|
@@ -637,6 +646,8 @@ class Cli
|
|
637
646
|
|
638
647
|
name, scope_list, inspect_options, filter = parse_inspect_command_options(image, options)
|
639
648
|
|
649
|
+
check_container_name!(image, name)
|
650
|
+
|
640
651
|
begin
|
641
652
|
system.start
|
642
653
|
inspector_task.inspect_system(
|
data/lib/docker_system.rb
CHANGED
@@ -124,12 +124,8 @@ class DockerSystem < System
|
|
124
124
|
private
|
125
125
|
|
126
126
|
def validate_image_name(image)
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
raise Machinery::Errors::InspectionFailed.new(
|
131
|
-
"Unknown docker image: '#{image}'"
|
132
|
-
)
|
133
|
-
end
|
127
|
+
LoggedCheetah.run("docker", "inspect", image)
|
128
|
+
rescue
|
129
|
+
raise Machinery::Errors::InspectionFailed.new("Unknown docker image: '#{image}'")
|
134
130
|
end
|
135
131
|
end
|
data/lib/helper.rb
CHANGED
@@ -21,24 +21,7 @@ module Machinery
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def self.content_is_binary?(content)
|
24
|
-
|
25
|
-
# Modified by SUSE Linux GmbH
|
26
|
-
ascii = 0
|
27
|
-
control = 0
|
28
|
-
binary = 0
|
29
|
-
|
30
|
-
content.each_byte do |byte|
|
31
|
-
case byte
|
32
|
-
when 0...32
|
33
|
-
control += 1
|
34
|
-
when 32...128
|
35
|
-
ascii += 1
|
36
|
-
else
|
37
|
-
binary += 1
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
control.to_f / ascii > 0.1 || binary.to_f / ascii > 0.05
|
24
|
+
!Cheetah.run("file", "-b", "-", stdout: :capture, stdin: content).include?(" text")
|
42
25
|
end
|
43
26
|
|
44
27
|
# Implementation of String#scrub for Ruby < 2.1. Assumes the string is in
|
data/lib/html.rb
CHANGED
@@ -51,7 +51,7 @@ EOF
|
|
51
51
|
rescue Errno::EADDRINUSE
|
52
52
|
servefailed_error = <<-EOF.chomp
|
53
53
|
Port #{Server.settings.port} is already in use.
|
54
|
-
|
54
|
+
You have to stop the already running server on port #{Server.settings.port} first or if you're serving a description with the `serve` command, you can also use the `--port` option.
|
55
55
|
EOF
|
56
56
|
raise Machinery::Errors::ServeFailed, servefailed_error
|
57
57
|
rescue SocketError => e
|
data/lib/inspector.rb
CHANGED
@@ -93,30 +93,4 @@ class Inspector
|
|
93
93
|
def scope
|
94
94
|
self.class.scope
|
95
95
|
end
|
96
|
-
|
97
|
-
# Runs the given script on the inspected machine asynchronously and calls the callback method
|
98
|
-
# periodically with new output when it occurs.
|
99
|
-
#
|
100
|
-
# Example:
|
101
|
-
#
|
102
|
-
# count = 0
|
103
|
-
# raw_list = run_script_with_progress("changed_managed_files.sh") do |chunk|
|
104
|
-
# count += chunk.lines.count
|
105
|
-
# Machinery::Ui.progress("Found #{count} changed files...")
|
106
|
-
# end
|
107
|
-
def run_script_with_progress(*script, &callback)
|
108
|
-
output = ""
|
109
|
-
write_io = StringIO.new(output, "a")
|
110
|
-
read_io = StringIO.new(output, "r")
|
111
|
-
|
112
|
-
inspect_thread = Thread.new { @system.run_script(*script, stdout: write_io) }
|
113
|
-
|
114
|
-
while inspect_thread.alive?
|
115
|
-
sleep 0.1
|
116
|
-
chunk = read_io.read
|
117
|
-
callback.call(chunk)
|
118
|
-
end
|
119
|
-
|
120
|
-
output
|
121
|
-
end
|
122
96
|
end
|
data/lib/machinery.rb
CHANGED
@@ -68,7 +68,6 @@ require_relative "list_task"
|
|
68
68
|
require_relative "system_description_store"
|
69
69
|
require_relative "logged_cheetah"
|
70
70
|
require_relative "renderer_helper"
|
71
|
-
require_relative "changed_rpm_files_helper"
|
72
71
|
require_relative "export_task"
|
73
72
|
require_relative "helper"
|
74
73
|
require_relative "deploy_task"
|
@@ -110,6 +109,7 @@ require_relative "workload_mapper_dsl"
|
|
110
109
|
require_relative "containerized_app"
|
111
110
|
require_relative "move_task"
|
112
111
|
require_relative "docker_system"
|
112
|
+
require_relative "rpm_database"
|
113
113
|
|
114
114
|
Dir[File.join(Machinery::ROOT, "plugins", "**", "*.rb")].each { |f| require(f) }
|
115
115
|
|
@@ -15,7 +15,60 @@
|
|
15
15
|
# To contact SUSE about this file by physical or electronic mail,
|
16
16
|
# you may find current contact information at www.suse.com
|
17
17
|
|
18
|
-
|
18
|
+
class RpmDatabase
|
19
|
+
class ChangedFile < Machinery::Object
|
20
|
+
attr_accessor :type
|
21
|
+
|
22
|
+
def initialize(type, attrs)
|
23
|
+
super(attrs)
|
24
|
+
@type = type
|
25
|
+
end
|
26
|
+
|
27
|
+
def config_file?
|
28
|
+
@type == "c"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize(system)
|
33
|
+
@system = system
|
34
|
+
end
|
35
|
+
|
36
|
+
def changed_files(&block)
|
37
|
+
return @changed_files if @changed_files
|
38
|
+
|
39
|
+
out = @system.run_script_with_progress("changed_files.sh", &block)
|
40
|
+
result = out.each_line.map do |line|
|
41
|
+
line.chomp!
|
42
|
+
next unless line.match(/^[^ ]+[ ]+. \/.*$/)
|
43
|
+
|
44
|
+
file, changes, type = parse_rpm_changes_line(line)
|
45
|
+
|
46
|
+
package = @system.run_command("rpm", "-qf", file, stdout: :capture).split.first
|
47
|
+
package_name, package_version = package.scan(/(.*)-([^-]*)-[^-]/).first
|
48
|
+
|
49
|
+
ChangedFile.new(
|
50
|
+
type,
|
51
|
+
name: file,
|
52
|
+
package_name: package_name,
|
53
|
+
package_version: package_version,
|
54
|
+
status: "changed",
|
55
|
+
changes: changes
|
56
|
+
)
|
57
|
+
end.compact.uniq
|
58
|
+
|
59
|
+
paths = result.reject { |f| f.changes == Machinery::Array.new(["deleted"]) }.map(&:name)
|
60
|
+
path_data = get_path_data(paths)
|
61
|
+
result.each do |pkg|
|
62
|
+
next unless path_data[pkg.name]
|
63
|
+
|
64
|
+
path_data[pkg.name].each do |key, value|
|
65
|
+
pkg[key] = value
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
@changed_files = result
|
70
|
+
end
|
71
|
+
|
19
72
|
def expected_tag?(character, position)
|
20
73
|
if @rpm_changes[position] == character
|
21
74
|
true
|
@@ -85,40 +138,40 @@ module ChangedRpmFilesHelper
|
|
85
138
|
|
86
139
|
[path.join(":").chomp,
|
87
140
|
{
|
88
|
-
mode:
|
89
|
-
user:
|
141
|
+
mode: mode,
|
142
|
+
user: user,
|
90
143
|
group: group,
|
91
|
-
type:
|
144
|
+
type: type
|
92
145
|
}
|
93
146
|
]
|
94
147
|
end
|
95
148
|
|
96
|
-
def get_link_target(
|
97
|
-
system.run_command(
|
149
|
+
def get_link_target(link)
|
150
|
+
@system.run_command(
|
98
151
|
"find", link, "-prune", "-printf", "%l",
|
99
|
-
stdout:
|
152
|
+
stdout: :capture,
|
100
153
|
privileged: true
|
101
154
|
).strip
|
102
155
|
end
|
103
156
|
|
104
157
|
# get path data for list of files
|
105
158
|
# cur_files is guaranteed to not exceed max command line length
|
106
|
-
def get_file_properties(
|
159
|
+
def get_file_properties(cur_files)
|
107
160
|
ret = {}
|
108
|
-
out = system.run_command(
|
161
|
+
out = @system.run_command(
|
109
162
|
"stat", "--printf", "%a:%U:%G:%u:%g:%F:%n\\n",
|
110
163
|
*cur_files,
|
111
164
|
stdout: :capture
|
112
165
|
)
|
113
166
|
out.each_line do |l|
|
114
|
-
path, values
|
115
|
-
ret[path]
|
116
|
-
ret[path][:target] = get_link_target(
|
167
|
+
path, values = parse_stat_line(l)
|
168
|
+
ret[path] = values
|
169
|
+
ret[path][:target] = get_link_target(path) if values[:type] == "link"
|
117
170
|
end
|
118
171
|
ret
|
119
172
|
end
|
120
173
|
|
121
|
-
def get_path_data(
|
174
|
+
def get_path_data(paths)
|
122
175
|
ret = {}
|
123
176
|
path_index = 0
|
124
177
|
# arbitrary number for maximum command line length that should always work
|
@@ -131,12 +184,12 @@ module ChangedRpmFilesHelper
|
|
131
184
|
cur_len += paths[path_index].size + 1
|
132
185
|
path_index += 1
|
133
186
|
else
|
134
|
-
ret.merge!(get_file_properties(
|
187
|
+
ret.merge!(get_file_properties(cur_files))
|
135
188
|
cur_files.clear
|
136
189
|
cur_len = 0
|
137
190
|
end
|
138
191
|
end
|
139
|
-
ret.merge!(get_file_properties(
|
192
|
+
ret.merge!(get_file_properties(cur_files)) unless cur_files.empty?
|
140
193
|
ret
|
141
194
|
end
|
142
195
|
end
|
data/lib/system.rb
CHANGED
@@ -102,6 +102,40 @@ class System
|
|
102
102
|
run_command("bash", "-c", script, *args)
|
103
103
|
end
|
104
104
|
|
105
|
+
# Runs the given script on the inspected machine asynchronously and calls the callback method
|
106
|
+
# periodically with new output when it occurs.
|
107
|
+
#
|
108
|
+
# Example:
|
109
|
+
#
|
110
|
+
# count = 0
|
111
|
+
# raw_list = run_script_with_progress("changed_managed_files.sh") do |chunk|
|
112
|
+
# count += chunk.lines.count
|
113
|
+
# Machinery::Ui.progress("Found #{count} changed files...")
|
114
|
+
# end
|
115
|
+
def run_script_with_progress(*script, &callback)
|
116
|
+
output = ""
|
117
|
+
error = ""
|
118
|
+
write_io = StringIO.new(output, "a")
|
119
|
+
error_io = StringIO.new(error, "a")
|
120
|
+
read_io = StringIO.new(output, "r")
|
121
|
+
|
122
|
+
inspect_thread = Thread.new do
|
123
|
+
run_script(*script, stdout: write_io, stderr: error_io)
|
124
|
+
end
|
125
|
+
|
126
|
+
while inspect_thread.alive?
|
127
|
+
sleep 0.1
|
128
|
+
chunk = read_io.read
|
129
|
+
callback.call(chunk) if callback
|
130
|
+
end
|
131
|
+
|
132
|
+
if error.include?("password is required")
|
133
|
+
raise Machinery::Errors::InsufficientPrivileges.new(remote_user, host)
|
134
|
+
end
|
135
|
+
|
136
|
+
output
|
137
|
+
end
|
138
|
+
|
105
139
|
def has_command?(command)
|
106
140
|
run_command("bash", "-c", "type -P #{command}", stdout: :capture)
|
107
141
|
true
|
@@ -116,4 +150,8 @@ class System
|
|
116
150
|
def locale
|
117
151
|
@locale || "C"
|
118
152
|
end
|
153
|
+
|
154
|
+
def rpm_database
|
155
|
+
@rpm_database ||= RpmDatabase.new(self)
|
156
|
+
end
|
119
157
|
end
|
data/lib/tarball.rb
CHANGED
@@ -22,7 +22,7 @@ class Tarball
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def list
|
25
|
-
output = LoggedCheetah.run("tar", "tvf", @file, stdout: :capture)
|
25
|
+
output = LoggedCheetah.run("tar", "tvf", @file, "--quoting-style=literal", stdout: :capture)
|
26
26
|
|
27
27
|
output.lines.map do |line|
|
28
28
|
mode, user_and_group, size, _date, _time, rest = line.split(" ", 6)
|
data/lib/version.rb
CHANGED
data/machinery-helper/README.md
CHANGED
data/machinery-helper/version.go
CHANGED
Binary file
|
@@ -520,7 +520,7 @@ image under the <code>/tmp/tux/</code> directory:</p>
|
|
520
520
|
|
521
521
|
<h3 id="SYNOPSIS">SYNOPSIS</h3>
|
522
522
|
|
523
|
-
<p><code>machinery compare</code> [-s SCOPE | --scope=SCOPE] [-e EXCLUDE-SCOPE | --exclude-scope=EXCLUDE-SCOPE] [--no-pager] [--show-all] NAME1 NAME2</p>
|
523
|
+
<p><code>machinery compare</code> [-s SCOPE | --scope=SCOPE] [-e EXCLUDE-SCOPE | --exclude-scope=EXCLUDE-SCOPE] [--no-pager] [--show-all] [--html] NAME1 NAME2</p>
|
524
524
|
|
525
525
|
<p><code>machinery</code> help compare</p>
|
526
526
|
|
@@ -546,6 +546,7 @@ See the <a href="#Scopes" data-bare-link="true">Scope section</a> for more infor
|
|
546
546
|
See the <a href="#Scopes" data-bare-link="true">Scope section</a> for more information.</p></dd>
|
547
547
|
<dt><code>--no-pager</code> (optional)</dt><dd><p>Do not pipe output into a pager.</p></dd>
|
548
548
|
<dt><code>--show-all</code> (optional)</dt><dd><p>Show also common properties of the descriptions (not only the differences).</p></dd>
|
549
|
+
<dt><code>--html</code> (optional)</dt><dd><p>Shows the comparison of two system descriptions in the web browser.</p></dd>
|
549
550
|
</dl>
|
550
551
|
|
551
552
|
|
@@ -562,6 +563,9 @@ See the <a href="#Scopes" data-bare-link="true">Scope section</a> for more infor
|
|
562
563
|
list:</p>
|
563
564
|
|
564
565
|
<p>$ <code>machinery</code> compare earth moon --scope=changed-managed-files --show-all</p></li>
|
566
|
+
<li><p>Compares system descriptions and shows the result in HTML format in your web browser:</p>
|
567
|
+
|
568
|
+
<p> $ machinery compare --html earth moon</p></li>
|
565
569
|
</ul>
|
566
570
|
|
567
571
|
|
@@ -901,7 +905,7 @@ via <code>ssh-copy-id</code> to the inspected host, e.g.: <code>ssh-copy-id root
|
|
901
905
|
<li><p>When inspecting as non-root the user has to have the following command
|
902
906
|
whitelist given in the sudoers file:</p>
|
903
907
|
|
904
|
-
<p>machinery ALL=(ALL) NOPASSWD: /usr/bin/find,/usr/bin/cat,/bin/cat,/usr/bin/rsync,/bin/rpm -
|
908
|
+
<p>machinery ALL=(ALL) NOPASSWD: /usr/bin/find,/usr/bin/cat,/bin/cat,/usr/bin/rsync,/bin/rpm -Va *,/bin/tar --create *</p></li>
|
905
909
|
<li><p>To add a remote <code>machinery</code> user run as root:</p>
|
906
910
|
|
907
911
|
<h1><code>useradd -m machinery -c "remote user for machinery"</code></h1>
|
@@ -1376,7 +1380,7 @@ manually editing it.</p>
|
|
1376
1380
|
|
1377
1381
|
<ol class='man-decor man-foot man foot'>
|
1378
1382
|
<li class='tl'></li>
|
1379
|
-
<li class='tc'>
|
1383
|
+
<li class='tc'>November 2015</li>
|
1380
1384
|
<li class='tr'>machinery(1)</li>
|
1381
1385
|
</ol>
|
1382
1386
|
|
@@ -16,7 +16,6 @@
|
|
16
16
|
# you may find current contact information at www.suse.com
|
17
17
|
|
18
18
|
class ChangedManagedFilesInspector < Inspector
|
19
|
-
include ChangedRpmFilesHelper
|
20
19
|
has_priority 90
|
21
20
|
|
22
21
|
def initialize(system, description)
|
@@ -69,73 +68,14 @@ class ChangedManagedFilesInspector < Inspector
|
|
69
68
|
|
70
69
|
private
|
71
70
|
|
72
|
-
def amend_file_attributes(changed_files)
|
73
|
-
existing_files = changed_files.reject { |f| f.changes.nil? || f.changes.include?("deleted") }
|
74
|
-
file_attributes = get_path_data(@system, existing_files.map(&:name))
|
75
|
-
changed_files.map do |changed_file|
|
76
|
-
if file_attributes[changed_file.name]
|
77
|
-
ChangedManagedFile.new(changed_file.attributes.merge(file_attributes[changed_file.name]))
|
78
|
-
else
|
79
|
-
changed_file
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
71
|
def changed_files
|
85
72
|
count = 0
|
86
|
-
|
73
|
+
files = @system.rpm_database.changed_files do |chunk|
|
87
74
|
count += chunk.lines.reject { |l| l.chomp.end_with?(":") || l.split(" ")[1] == "c" }.count
|
88
75
|
Machinery::Ui.progress(" -> Found #{count} changed #{Machinery::pluralize(count, "file")}...")
|
89
76
|
end
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
# libpulse0-4.0.git.270.g9490a:
|
94
|
-
# S.5...... c /etc/pulse/client.conf
|
95
|
-
# ntp-4.2.6p5:
|
96
|
-
# S.5...... c /etc/ntp.conf
|
97
|
-
#
|
98
|
-
# We map this to an array of files like this:
|
99
|
-
#
|
100
|
-
# [
|
101
|
-
# {
|
102
|
-
# name: "/etc/pulse/client.conf",
|
103
|
-
# package_name: "libpulse0",
|
104
|
-
# package_version: "4.0.git.270.g9490a"
|
105
|
-
# },
|
106
|
-
# ...
|
107
|
-
# ]
|
108
|
-
file_list = list.split("\n").slice_before(/(.*):\z/).flat_map do |package, *files|
|
109
|
-
package_name, package_version = package.scan(/(.*)-([^-]*):/).first
|
110
|
-
files.map do |changed_file|
|
111
|
-
if changed_file =~ /\A(\/\S+) (.*)/
|
112
|
-
ChangedManagedFile.new(
|
113
|
-
name: $1,
|
114
|
-
package_name: package_name,
|
115
|
-
package_version: package_version,
|
116
|
-
status: "error",
|
117
|
-
error_message: $2
|
118
|
-
)
|
119
|
-
else
|
120
|
-
file, changes, flag = parse_rpm_changes_line(changed_file)
|
121
|
-
|
122
|
-
# Config files (flagged as 'c') are handled by the ConfigFilesInspector
|
123
|
-
next if flag == "c"
|
124
|
-
|
125
|
-
ChangedManagedFile.new(
|
126
|
-
name: file,
|
127
|
-
package_name: package_name,
|
128
|
-
package_version: package_version,
|
129
|
-
status: "changed",
|
130
|
-
changes: changes
|
131
|
-
)
|
132
|
-
end
|
133
|
-
# Since errors are also recognized for config-files we have
|
134
|
-
# to filter them
|
135
|
-
end.compact.select { |item| item.changes }
|
136
|
-
end.uniq
|
137
|
-
|
138
|
-
amend_file_attributes(file_list)
|
77
|
+
files.reject(&:config_file?).map do |file|
|
78
|
+
ChangedManagedFile.new(file.attributes)
|
79
|
+
end
|
139
80
|
end
|
140
|
-
|
141
81
|
end
|
@@ -16,8 +16,6 @@
|
|
16
16
|
# you may find current contact information at www.suse.com
|
17
17
|
|
18
18
|
class ConfigFilesInspector < Inspector
|
19
|
-
include ChangedRpmFilesHelper
|
20
|
-
|
21
19
|
has_priority 80
|
22
20
|
# checks if all required binaries are present
|
23
21
|
def check_requirements(check_rsync)
|
@@ -27,47 +25,15 @@ class ConfigFilesInspector < Inspector
|
|
27
25
|
@system.check_retrieve_files_dependencies if check_rsync
|
28
26
|
end
|
29
27
|
|
30
|
-
# returns list of packages containing configfiles
|
31
|
-
def packages_with_config_files
|
32
|
-
# first determine packages that have config files at all
|
33
|
-
# rpm command provides lines with package names and subsequent
|
34
|
-
# lines with pathes of config files for that package
|
35
|
-
# e.g.
|
36
|
-
# apache2
|
37
|
-
# /etc/apache2/charset.conv
|
38
|
-
# /etc/apache2/default-server.conf
|
39
|
-
#
|
40
|
-
output = @system.run_command(
|
41
|
-
"rpm", "-qa", "--configfiles", "--queryformat",
|
42
|
-
"%{NAME}-%{VERSION}\n",
|
43
|
-
:stdout => :capture
|
44
|
-
)
|
45
|
-
# use leading slash to decide between lines containing package names
|
46
|
-
# and lines containing config files
|
47
|
-
chunks = output.split("\n").slice_before { |l| !l.start_with?("/") }
|
48
|
-
chunks.reject { |_pkg, *cfiles| cfiles.empty? }.map(&:first).uniq
|
49
|
-
end
|
50
|
-
|
51
28
|
# returns a hash with entries for changed config files
|
52
29
|
def config_file_changes(pkg)
|
53
|
-
|
54
|
-
parts = pkg.split("-")
|
55
|
-
package_name = parts[0..-2].join("-")
|
56
|
-
package_version = parts.last
|
57
|
-
|
58
|
-
paths_and_changes = out.lines.map { |line| parse_rpm_changes_line(line) }
|
59
|
-
paths_and_changes.reject! do |path, changes, type|
|
60
|
-
# only consider config files and only those with changes
|
61
|
-
type != "c" || changes.empty?
|
62
|
-
end
|
63
|
-
|
64
|
-
paths_and_changes.map do |path, changes|
|
30
|
+
@system.changed_files.select(&:config_file?).map do |file|
|
65
31
|
ConfigFile.new(
|
66
|
-
name: path,
|
32
|
+
name: file.path,
|
67
33
|
package_name: package_name,
|
68
34
|
package_version: package_version,
|
69
35
|
status: "changed",
|
70
|
-
changes: changes
|
36
|
+
changes: file.changes
|
71
37
|
)
|
72
38
|
end.uniq
|
73
39
|
end
|
@@ -82,12 +48,12 @@ class ConfigFilesInspector < Inspector
|
|
82
48
|
check_requirements(do_extract)
|
83
49
|
|
84
50
|
count = 0
|
85
|
-
|
86
|
-
|
87
|
-
count += files.length
|
51
|
+
files = @system.rpm_database.changed_files do |chunk|
|
52
|
+
count += chunk.lines.count { |l| !l.chomp.end_with?(":") && l.split(" ")[1] == "c" }
|
88
53
|
Machinery::Ui.progress(" -> Found #{count} config #{Machinery::pluralize(count, "file")}...")
|
89
|
-
|
90
|
-
|
54
|
+
end
|
55
|
+
result = files.select(&:config_file?).map do |file|
|
56
|
+
ConfigFile.new(file.attributes)
|
91
57
|
end
|
92
58
|
|
93
59
|
if filter
|
@@ -95,16 +61,6 @@ class ConfigFilesInspector < Inspector
|
|
95
61
|
result.delete_if { |e| file_filter.matches?(e.name) } if file_filter
|
96
62
|
end
|
97
63
|
|
98
|
-
paths = result.reject { |f| f.changes == Machinery::Array.new(["deleted"]) }.map(&:name)
|
99
|
-
path_data = get_path_data(@system, paths)
|
100
|
-
key_list = [:user, :group, :mode, :type, :target]
|
101
|
-
result.each do |pkg|
|
102
|
-
pname = pkg.name
|
103
|
-
if path_data.has_key?(pname)
|
104
|
-
key_list.each { |k| pkg[k] = path_data[pname][k] if path_data[pname][k] }
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
64
|
scope = ConfigFilesScope.new
|
109
65
|
file_store = @description.scope_file_store("config_files")
|
110
66
|
scope.scope_file_store = file_store
|
data/plugins/os/os_inspector.rb
CHANGED
@@ -94,6 +94,11 @@ class OsInspector < Inspector
|
|
94
94
|
if result["pretty_name"] =~ /^openSUSE.*Tumbleweed/
|
95
95
|
result["pretty_name"] = "openSUSE Tumbleweed"
|
96
96
|
end
|
97
|
+
|
98
|
+
# remove Leap version from pretty name
|
99
|
+
if result["pretty_name"] =~ /^openSUSE.*Leap/
|
100
|
+
result["pretty_name"] = "openSUSE Leap"
|
101
|
+
end
|
97
102
|
end
|
98
103
|
# return pretty_name as name as it contains the actual full length
|
99
104
|
# name instead of an abbreviation
|
data/plugins/os/os_model.rb
CHANGED
@@ -192,6 +192,20 @@ class OsOpenSuseTumbleweed < Os
|
|
192
192
|
end
|
193
193
|
end
|
194
194
|
|
195
|
+
class OsOpenSuseLeap < Os
|
196
|
+
def display_name
|
197
|
+
"#{name} (#{architecture})"
|
198
|
+
end
|
199
|
+
|
200
|
+
def self.canonical_name
|
201
|
+
"openSUSE Leap"
|
202
|
+
end
|
203
|
+
|
204
|
+
def self.buildable_systems
|
205
|
+
[OsOpenSuse13_2, OsOpenSuseLeap]
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
195
209
|
class Rhel < Os
|
196
210
|
def self.canonical_name
|
197
211
|
"Red Hat Enterprise Linux Server"
|
data/tools/helper_builder.rb
CHANGED
@@ -22,6 +22,7 @@ class HelperBuilder
|
|
22
22
|
def initialize(helper_dir)
|
23
23
|
@helper_dir = helper_dir
|
24
24
|
@git_revision_file = File.join(helper_dir, "..", ".git_revision")
|
25
|
+
@go_version_file = File.join(helper_dir, "version.go")
|
25
26
|
end
|
26
27
|
|
27
28
|
def run_build
|
@@ -30,7 +31,7 @@ class HelperBuilder
|
|
30
31
|
return false if !go_available?
|
31
32
|
|
32
33
|
# handle changed branches (where go files are older than the helper)
|
33
|
-
if runs_in_git? && changed_revision?
|
34
|
+
if runs_in_git? && (changed_revision? || !File.exist?(@go_version_file))
|
34
35
|
write_go_version_file
|
35
36
|
if build_machinery_helper
|
36
37
|
write_git_revision_file
|
@@ -56,7 +57,7 @@ package main
|
|
56
57
|
|
57
58
|
const VERSION = "#{git_revision}"
|
58
59
|
EOF
|
59
|
-
File.write(
|
60
|
+
File.write(@go_version_file, file)
|
60
61
|
end
|
61
62
|
|
62
63
|
def build_machinery_helper
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: machinery-tool
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.16.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- SUSE
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-11-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cheetah
|
@@ -304,7 +304,6 @@ files:
|
|
304
304
|
- lib/array.rb
|
305
305
|
- lib/autoyast.rb
|
306
306
|
- lib/build_task.rb
|
307
|
-
- lib/changed_rpm_files_helper.rb
|
308
307
|
- lib/cli.rb
|
309
308
|
- lib/compare_task.rb
|
310
309
|
- lib/comparison.rb
|
@@ -352,6 +351,7 @@ files:
|
|
352
351
|
- lib/renderer.rb
|
353
352
|
- lib/renderer_helper.rb
|
354
353
|
- lib/rpm.rb
|
354
|
+
- lib/rpm_database.rb
|
355
355
|
- lib/scope.rb
|
356
356
|
- lib/scope_file_access_archive.rb
|
357
357
|
- lib/scope_file_access_flat.rb
|