machinery-tool 1.15.0 → 1.16.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 +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
|