machinery-tool 1.13.0 → 1.14.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 -0
- data/NEWS +10 -0
- data/bin/machinery +7 -1
- data/{helpers → filters}/default_filters.json +0 -0
- data/{helpers → filters}/filter-packages-for-build.yaml +0 -0
- data/{helpers → inspect_helpers}/changed_files.sh +0 -0
- data/{helpers → inspect_helpers}/yum_repositories.py +0 -0
- data/lib/cli.rb +136 -52
- data/lib/compare_task.rb +1 -1
- data/lib/constants.rb +1 -1
- data/lib/docker_system.rb +135 -0
- data/lib/exceptions.rb +3 -2
- data/lib/filter.rb +1 -1
- data/lib/hint.rb +8 -2
- data/lib/inspect_task.rb +3 -3
- data/lib/kiwi_config.rb +2 -2
- data/lib/list_task.rb +14 -7
- data/lib/local_system.rb +20 -2
- data/lib/machinery.rb +2 -0
- data/lib/machinery_helper.rb +17 -14
- data/lib/move_task.rb +22 -0
- data/lib/remote_system.rb +4 -0
- data/lib/scope_file_access_archive.rb +10 -0
- data/lib/scope_file_access_flat.rb +4 -0
- data/lib/show_task.rb +1 -1
- data/lib/system.rb +16 -2
- data/lib/system_description.rb +19 -9
- data/lib/system_description_store.rb +10 -0
- data/lib/version.rb +1 -1
- data/lib/workload_mapper.rb +2 -1
- data/machinery-helper/README.md +13 -0
- data/machinery-helper/Rakefile +124 -0
- data/machinery-helper/machinery_helper.go +228 -0
- data/machinery-helper/machinery_helper_test.go +192 -0
- data/machinery-helper/mountpoints.go +81 -0
- data/machinery-helper/mountpoints_test.go +74 -0
- data/machinery-helper/tar.go +142 -0
- data/machinery-helper/version.go +5 -0
- data/man/generated/machinery.1.gz +0 -0
- data/man/generated/machinery.1.html +43 -6
- data/plugins/changed_managed_files/changed_managed_files_inspector.rb +1 -1
- data/plugins/config_files/config_files_inspector.rb +1 -1
- data/plugins/config_files/config_files_renderer.rb +1 -1
- data/plugins/environment/environment_inspector.rb +2 -2
- data/plugins/environment/schema/system-description-environment.schema-v5.json +4 -0
- data/plugins/os/os_inspector.rb +23 -11
- data/plugins/repositories/repositories_inspector.rb +1 -1
- data/plugins/services/services_inspector.rb +6 -1
- data/plugins/unmanaged_files/unmanaged_files_inspector.rb +35 -30
- data/plugins/unmanaged_files/unmanaged_files_model.rb +12 -6
- data/workload_mapper/docker-registry/clue.rb +0 -2
- data/workload_mapper/docker-registry/compose-template.yml +2 -1
- data/workload_mapper/docker-registry/container/Dockerfile +1 -1
- data/workload_mapper/mariadb/clue.rb +1 -0
- data/workload_mapper/mariadb/compose-template.yml +2 -2
- data/workload_mapper/rails/compose-template.yml +0 -1
- data/workload_mapper/rails/container/Dockerfile +3 -4
- data/workload_mapper/wordpress/clue.rb +13 -0
- data/workload_mapper/wordpress/compose-template.yml +5 -0
- data/workload_mapper/wordpress/config/wp-config.php +38 -0
- data/workload_mapper/wordpress/container/Dockerfile +25 -0
- data/workload_mapper/wordpress/container/apache2/listen.conf +1 -0
- data/workload_mapper/wordpress/container/apache2/wordpress_vhost.conf +21 -0
- data/workload_mapper/wordpress/setup/setup.rb.erb +67 -0
- metadata +26 -8
- data/helpers/inspector_files.rb +0 -52
data/lib/machinery.rb
CHANGED
@@ -107,6 +107,8 @@ require_relative "workload_mapper"
|
|
107
107
|
require_relative "containerize_task"
|
108
108
|
require_relative "workload_mapper_dsl"
|
109
109
|
require_relative "containerized_app"
|
110
|
+
require_relative "move_task"
|
111
|
+
require_relative "docker_system"
|
110
112
|
|
111
113
|
Dir[File.join(Machinery::ROOT, "plugins", "**", "*.rb")].each { |f| require(f) }
|
112
114
|
|
data/lib/machinery_helper.rb
CHANGED
@@ -22,45 +22,48 @@
|
|
22
22
|
# The inspection checks, if a binary helper is available on the machine where
|
23
23
|
# the inspection is started. It looks at the location
|
24
24
|
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
# where <arch> is the hardware architecture of the target system. Valid values
|
28
|
-
# are x86_64, i586, s390x, and ppcle.
|
25
|
+
# <machinery-installation-path>/machinery-helper/machinery-helper
|
29
26
|
|
30
27
|
class MachineryHelper
|
31
28
|
attr_accessor :local_helpers_path
|
29
|
+
attr_accessor :remote_helper_path
|
32
30
|
|
33
31
|
def initialize(s)
|
34
32
|
@system = s
|
35
33
|
@arch = @system.arch
|
36
34
|
|
37
|
-
@local_helpers_path = "
|
35
|
+
@local_helpers_path = File.join(Machinery::ROOT, "machinery-helper")
|
36
|
+
@remote_helper_path = File.join(Machinery::HELPER_REMOTE_PATH, "machinery-helper")
|
38
37
|
end
|
39
38
|
|
40
39
|
def local_helper_path
|
41
|
-
File.join(
|
40
|
+
File.join(local_helpers_path, "machinery-helper")
|
42
41
|
end
|
43
42
|
|
44
43
|
# Returns true, if there is a helper binary matching the architecture of the
|
45
44
|
# inspected system. Return false, if not.
|
46
45
|
def can_help?
|
47
|
-
File.exist?(local_helper_path)
|
46
|
+
File.exist?(local_helper_path) && LocalSystem.matches_architecture?(@arch)
|
48
47
|
end
|
49
48
|
|
50
49
|
def inject_helper
|
51
|
-
@system.inject_file(local_helper_path, Machinery::
|
50
|
+
@system.inject_file(local_helper_path, Machinery::HELPER_REMOTE_PATH)
|
52
51
|
end
|
53
52
|
|
54
53
|
def run_helper(scope)
|
55
|
-
json = @system.run_command(
|
56
|
-
File.join(
|
57
|
-
Machinery::REMOTE_HELPERS_PATH, "machinery-helper"
|
58
|
-
), stdout: :capture, stderr: STDERR
|
59
|
-
)
|
54
|
+
json = @system.run_command(remote_helper_path, stdout: :capture, stderr: STDERR)
|
60
55
|
scope.set_attributes(JSON.parse(json))
|
61
56
|
end
|
62
57
|
|
63
58
|
def remove_helper
|
64
|
-
@system.remove_file(
|
59
|
+
@system.remove_file(remote_helper_path)
|
60
|
+
end
|
61
|
+
|
62
|
+
def has_compatible_version?
|
63
|
+
output = @system.run_command(remote_helper_path, "--version", stdout: :capture).chomp
|
64
|
+
|
65
|
+
version = output[/^Version: ([a-f0-9]{40})$/, 1]
|
66
|
+
|
67
|
+
version == File.read(File.join(Machinery::ROOT, ".git_revision"))
|
65
68
|
end
|
66
69
|
end
|
data/lib/move_task.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Copyright (c) 2013-2015 SUSE LLC
|
2
|
+
#
|
3
|
+
# This program is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of version 3 of the GNU General Public License as
|
5
|
+
# published by the Free Software Foundation.
|
6
|
+
#
|
7
|
+
# This program is distributed in the hope that it will be useful,
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
10
|
+
# GNU General Public License for more details.
|
11
|
+
#
|
12
|
+
# You should have received a copy of the GNU General Public License
|
13
|
+
# along with this program; if not, contact SUSE LLC.
|
14
|
+
#
|
15
|
+
# To contact SUSE about this file by physical or electronic mail,
|
16
|
+
# you may find current contact information at www.suse.com
|
17
|
+
|
18
|
+
class MoveTask
|
19
|
+
def move(store, from, to)
|
20
|
+
store.move(from, to)
|
21
|
+
end
|
22
|
+
end
|
data/lib/remote_system.rb
CHANGED
@@ -32,6 +32,16 @@ module ScopeFileAccessArchive
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
+
def has_file?(name)
|
36
|
+
return true if files.any? { |file| file.name == name }
|
37
|
+
if files.any? { |file| file.name == File.join(File.dirname(name), "") }
|
38
|
+
tgz_file = File.join(scope_file_store.path, "trees", "#{File.dirname(name)}.tgz")
|
39
|
+
return Cheetah.run("tar", "ztf", tgz_file, stdout: :capture).split(/\n/).
|
40
|
+
any? { |f| "/#{f}" == name }
|
41
|
+
end
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
35
45
|
def tarball_path(system_file)
|
36
46
|
if system_file.directory?
|
37
47
|
File.join(
|
data/lib/show_task.rb
CHANGED
@@ -30,7 +30,7 @@ class ShowTask
|
|
30
30
|
|
31
31
|
def show_html(description, options)
|
32
32
|
begin
|
33
|
-
LocalSystem.
|
33
|
+
LocalSystem.validate_existence_of_command("xdg-open", "xdg-utils")
|
34
34
|
|
35
35
|
url = "http://#{options[:ip]}:#{options[:port]}/#{CGI.escape(description.name)}"
|
36
36
|
|
data/lib/system.rb
CHANGED
@@ -29,6 +29,7 @@ class System
|
|
29
29
|
abstract_method :read_file
|
30
30
|
abstract_method :inject_file
|
31
31
|
abstract_method :remove_file
|
32
|
+
abstract_method :type
|
32
33
|
|
33
34
|
attr_writer :locale
|
34
35
|
|
@@ -55,10 +56,22 @@ class System
|
|
55
56
|
)
|
56
57
|
end
|
57
58
|
|
59
|
+
def check_retrieve_files_dependencies
|
60
|
+
check_requirement("rsync", "--version")
|
61
|
+
end
|
62
|
+
|
63
|
+
def check_create_archive_dependencies
|
64
|
+
check_requirement("tar", "--version")
|
65
|
+
check_requirement("gzip", "--version")
|
66
|
+
end
|
67
|
+
|
58
68
|
# Retrieves files specified in filelist from the remote system and create an archive.
|
59
69
|
# To be able to deal with arbitrary filenames we use zero-terminated
|
60
70
|
# filelist and the --null option of tar
|
61
71
|
def create_archive(file_list, archive, exclude = [])
|
72
|
+
Machinery.logger.info(
|
73
|
+
"The following files are packaged in #{archive}: " + Array(file_list).join(", ")
|
74
|
+
)
|
62
75
|
created = !File.exists?(archive)
|
63
76
|
out = File.open(archive, "w")
|
64
77
|
begin
|
@@ -67,7 +80,8 @@ class System
|
|
67
80
|
*exclude.flat_map { |f| ["--exclude", f]},
|
68
81
|
stdout: out,
|
69
82
|
stdin: Array(file_list).join("\0"),
|
70
|
-
privileged: true
|
83
|
+
privileged: true,
|
84
|
+
disable_logging: true
|
71
85
|
)
|
72
86
|
rescue Cheetah::ExecutionFailed => e
|
73
87
|
if e.status.exitstatus == 1
|
@@ -83,7 +97,7 @@ class System
|
|
83
97
|
end
|
84
98
|
|
85
99
|
def run_script(*args)
|
86
|
-
script = File.read(File.join(Machinery::ROOT, "
|
100
|
+
script = File.read(File.join(Machinery::ROOT, "inspect_helpers", args.shift))
|
87
101
|
|
88
102
|
run_command("bash", "-c", script, *args)
|
89
103
|
end
|
data/lib/system_description.rb
CHANGED
@@ -276,19 +276,29 @@ class SystemDescription < Machinery::Object
|
|
276
276
|
end
|
277
277
|
|
278
278
|
def has_file?(name)
|
279
|
-
|
280
|
-
|
279
|
+
EXTRACTABLE_SCOPES.each do |scope|
|
280
|
+
if scope_extracted?(scope)
|
281
|
+
return true if self[scope] && self[scope].has_file?(name)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
false
|
281
285
|
end
|
282
286
|
|
283
287
|
def read_config(path, key)
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
file = self["unmanaged_files"].files.find { |f| f.name == path }
|
290
|
-
return parse_variable_assignment(file.content, key) if file
|
288
|
+
EXTRACTABLE_SCOPES.each do |scope|
|
289
|
+
if scope_extracted?(scope)
|
290
|
+
file = self[scope].files.find { |f| f.name == path }
|
291
|
+
return parse_variable_assignment(file.content, key) if file
|
292
|
+
end
|
291
293
|
end
|
294
|
+
# if scope_extracted?("config_files")
|
295
|
+
# file = self["config_files"].files.find { |f| f.name == path }
|
296
|
+
# return parse_variable_assignment(file.content, key) if file
|
297
|
+
# end
|
298
|
+
# if scope_extracted?("unmanaged_files")
|
299
|
+
# file = self["unmanaged_files"].files.find { |f| f.name == path }
|
300
|
+
# return parse_variable_assignment(file.content, key) if file
|
301
|
+
# end
|
292
302
|
end
|
293
303
|
|
294
304
|
private
|
@@ -74,6 +74,16 @@ class SystemDescriptionStore
|
|
74
74
|
FileUtils.cp_r(description_path(from), description_path(to))
|
75
75
|
end
|
76
76
|
|
77
|
+
def move(from, to)
|
78
|
+
SystemDescription.validate_name(from)
|
79
|
+
SystemDescription.validate_name(to)
|
80
|
+
|
81
|
+
validate_existence_of_description(from)
|
82
|
+
validate_nonexistence_of_description(to)
|
83
|
+
|
84
|
+
FileUtils.mv(description_path(from), description_path(to))
|
85
|
+
end
|
86
|
+
|
77
87
|
def backup(description_name)
|
78
88
|
SystemDescription.validate_name(description_name)
|
79
89
|
validate_existence_of_description(description_name)
|
data/lib/version.rb
CHANGED
data/lib/workload_mapper.rb
CHANGED
@@ -104,7 +104,8 @@ class WorkloadMapper
|
|
104
104
|
tgz_file = File.join(dir, "trees", "#{origin.chop}.tgz")
|
105
105
|
output_path = File.join(path, workload, destination)
|
106
106
|
FileUtils.mkdir_p(output_path)
|
107
|
-
|
107
|
+
strip_number = origin.split("/").size - 1
|
108
|
+
Cheetah.run("tar", "zxf", tgz_file, "-C", output_path, "--strip=#{strip_number}")
|
108
109
|
copy_workload_config_files(workload, output_path)
|
109
110
|
end
|
110
111
|
if file && file.file?
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# machinery-helper
|
2
|
+
|
3
|
+
A helper binary used by Machinery - http://machinery-project.org
|
4
|
+
|
5
|
+
It inspects a system for unmanaged-files (files not tracked by rpm)
|
6
|
+
and outputs the result in the
|
7
|
+
[Machinery json format](https://github.com/SUSE/machinery/blob/master/docs/System-Description-Format.md).
|
8
|
+
|
9
|
+
## Build
|
10
|
+
|
11
|
+
Make sure that the official Go Development environment is installed.
|
12
|
+
|
13
|
+
To build the helper binary just run `go build`.
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# Copyright (c) 2013-2015 SUSE LLC
|
2
|
+
#
|
3
|
+
# This program is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of version 3 of the GNU General Public License as
|
5
|
+
# published by the Free Software Foundation.
|
6
|
+
#
|
7
|
+
# This program is distributed in the hope that it will be useful,
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
10
|
+
# GNU General Public License for more details.
|
11
|
+
#
|
12
|
+
# You should have received a copy of the GNU General Public License
|
13
|
+
# along with this program; if not, contact SUSE LLC.
|
14
|
+
#
|
15
|
+
# To contact SUSE about this file by physical or electronic mail,
|
16
|
+
# you may find current contact information at www.suse.com
|
17
|
+
|
18
|
+
task :default => "try_build"
|
19
|
+
|
20
|
+
HELPER_DIR = File.expand_path(File.dirname(__FILE__))
|
21
|
+
GIT_REVISION_FILE = File.join(HELPER_DIR, "..", ".git_revision")
|
22
|
+
|
23
|
+
def write_go_version_file
|
24
|
+
file = <<-EOF
|
25
|
+
// This is a generated file and shouldn't be changed
|
26
|
+
|
27
|
+
package main
|
28
|
+
|
29
|
+
const VERSION = "#{git_revision}"
|
30
|
+
EOF
|
31
|
+
File.write(File.join(HELPER_DIR, "version.go"), file)
|
32
|
+
end
|
33
|
+
|
34
|
+
def build_machinery_helper
|
35
|
+
FileUtils.rm_f(File.join(HELPER_DIR, "machinery-helper"))
|
36
|
+
Dir.chdir(HELPER_DIR) do
|
37
|
+
puts("Building machinery-helper binary.")
|
38
|
+
if !system("go build")
|
39
|
+
STDERR.puts("Warning: Building of the machinery-helper failed!")
|
40
|
+
false
|
41
|
+
else
|
42
|
+
true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def go_available?
|
48
|
+
if !system("which go > /dev/null 2>&1")
|
49
|
+
STDERR.puts(
|
50
|
+
"Warning: The Go compiler is not available on this system. Skipping building the" \
|
51
|
+
" machinery-helper.\nThe machinery-helper increases the inspection speed significantly."
|
52
|
+
)
|
53
|
+
false
|
54
|
+
else
|
55
|
+
true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def arch_supported?
|
60
|
+
arch = `uname -p`.chomp
|
61
|
+
if !["x86_64"].include?(arch)
|
62
|
+
STDERR.puts(
|
63
|
+
"Warning: The hardware architecture #{arch} is not yet supported by the machinery-helper."
|
64
|
+
)
|
65
|
+
false
|
66
|
+
else
|
67
|
+
true
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def runs_in_git?
|
72
|
+
Dir.exist?(File.join(HELPER_DIR, "..", ".git")) && system("which git > /dev/null 2>&1")
|
73
|
+
end
|
74
|
+
|
75
|
+
def git_revision
|
76
|
+
`git rev-parse HEAD`.chomp
|
77
|
+
end
|
78
|
+
|
79
|
+
def write_git_revision_file
|
80
|
+
File.write(GIT_REVISION_FILE, git_revision)
|
81
|
+
end
|
82
|
+
|
83
|
+
def changed_revision?
|
84
|
+
if File.exist?(GIT_REVISION_FILE)
|
85
|
+
old_revision = File.read(GIT_REVISION_FILE)
|
86
|
+
else
|
87
|
+
old_revision = "unknown"
|
88
|
+
end
|
89
|
+
git_revision != old_revision
|
90
|
+
end
|
91
|
+
|
92
|
+
def run_build
|
93
|
+
# An unsupported architecture is no error
|
94
|
+
return true if !arch_supported?
|
95
|
+
return false if !go_available?
|
96
|
+
|
97
|
+
# handle changed branches (where go files are older than the helper)
|
98
|
+
if runs_in_git? && changed_revision?
|
99
|
+
write_go_version_file
|
100
|
+
if build_machinery_helper
|
101
|
+
write_git_revision_file
|
102
|
+
return true
|
103
|
+
else
|
104
|
+
return false
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
if !FileUtils.uptodate?(
|
109
|
+
File.join(HELPER_DIR, "machinery-helper"), Dir.glob(File.join(HELPER_DIR, "*.go"))
|
110
|
+
)
|
111
|
+
return build_machinery_helper
|
112
|
+
end
|
113
|
+
true
|
114
|
+
end
|
115
|
+
|
116
|
+
desc "Build the helper if needed"
|
117
|
+
task :build do
|
118
|
+
raise "Error: Build of Machinery helper failed" if !run_build
|
119
|
+
end
|
120
|
+
|
121
|
+
desc "Don't fail on build errors"
|
122
|
+
task :try_build do
|
123
|
+
run_build
|
124
|
+
end
|
@@ -0,0 +1,228 @@
|
|
1
|
+
// Copyright (c) 2015 SUSE LLC
|
2
|
+
//
|
3
|
+
// This program is free software; you can redistribute it and/or
|
4
|
+
// modify it under the terms of version 3 of the GNU General Public License as
|
5
|
+
// published by the Free Software Foundation.
|
6
|
+
//
|
7
|
+
// This program is distributed in the hope that it will be useful,
|
8
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
9
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
10
|
+
// GNU General Public License for more details.
|
11
|
+
//
|
12
|
+
// You should have received a copy of the GNU General Public License
|
13
|
+
// along with this program; if not, contact SUSE LLC.
|
14
|
+
//
|
15
|
+
// To contact SUSE about this file by physical or electronic mail,
|
16
|
+
// you may find current contact information at www.suse.com
|
17
|
+
|
18
|
+
package main
|
19
|
+
|
20
|
+
import (
|
21
|
+
"os/exec"
|
22
|
+
"bytes"
|
23
|
+
"log"
|
24
|
+
"strings"
|
25
|
+
"io/ioutil"
|
26
|
+
"encoding/json"
|
27
|
+
"os"
|
28
|
+
"sort"
|
29
|
+
"fmt"
|
30
|
+
"path/filepath"
|
31
|
+
"unicode/utf8"
|
32
|
+
"flag"
|
33
|
+
)
|
34
|
+
|
35
|
+
func getRpms() []string {
|
36
|
+
cmd := exec.Command("rpm", "-qlav")
|
37
|
+
var out bytes.Buffer
|
38
|
+
cmd.Stdout = &out
|
39
|
+
err := cmd.Run()
|
40
|
+
if err != nil {
|
41
|
+
log.Fatal(err)
|
42
|
+
}
|
43
|
+
|
44
|
+
f := func(c rune) bool {
|
45
|
+
return c == '\n'
|
46
|
+
}
|
47
|
+
packages := strings.FieldsFunc(out.String(), f)
|
48
|
+
return packages
|
49
|
+
}
|
50
|
+
|
51
|
+
func parseRpmLine(line string) (fileType string, fileName string, linkTarget string) {
|
52
|
+
fileType = line[0:1]
|
53
|
+
|
54
|
+
index := strings.Index(line, "/")
|
55
|
+
if index < 0 {
|
56
|
+
panic(line)
|
57
|
+
}
|
58
|
+
file := line[index:]
|
59
|
+
fields := strings.Split(file, " -> ")
|
60
|
+
if len(fields) == 2 {
|
61
|
+
fileName = fields[0]
|
62
|
+
linkTarget = fields[1]
|
63
|
+
} else {
|
64
|
+
fileName = file
|
65
|
+
}
|
66
|
+
return
|
67
|
+
}
|
68
|
+
|
69
|
+
func addImplicitlyManagedDirs(dirs map[string]bool, files map[string]string) {
|
70
|
+
for file, target := range files {
|
71
|
+
for i:= 1; i < len(file); i++ {
|
72
|
+
if file[i] == '/' {
|
73
|
+
topdir := file[:i]
|
74
|
+
if _, ok := dirs[topdir]; !ok {
|
75
|
+
dirs[topdir] = false
|
76
|
+
}
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
if target != "" {
|
81
|
+
if _, ok := dirs[target]; ok {
|
82
|
+
dirs[file] = false
|
83
|
+
}
|
84
|
+
}
|
85
|
+
}
|
86
|
+
return
|
87
|
+
}
|
88
|
+
|
89
|
+
func getManagedFiles() (map[string]string, map[string]bool) {
|
90
|
+
files := make(map[string]string)
|
91
|
+
dirs := make(map[string]bool)
|
92
|
+
|
93
|
+
for _, pkg := range getRpms() {
|
94
|
+
if pkg != "(contains no files)" {
|
95
|
+
fileType, fileName, linkTarget := parseRpmLine(pkg)
|
96
|
+
switch fileType {
|
97
|
+
case "-":
|
98
|
+
files[fileName] = ""
|
99
|
+
case "d":
|
100
|
+
dirs[fileName] = true
|
101
|
+
case "l":
|
102
|
+
files[fileName] = linkTarget
|
103
|
+
}
|
104
|
+
}
|
105
|
+
}
|
106
|
+
|
107
|
+
addImplicitlyManagedDirs(dirs, files)
|
108
|
+
|
109
|
+
return files, dirs
|
110
|
+
}
|
111
|
+
|
112
|
+
func assembleJSON(unmanagedFilesMap interface{}) string {
|
113
|
+
jsonMap := map[string]interface{}{ "extracted": false, "files": unmanagedFilesMap }
|
114
|
+
json, _ := json.MarshalIndent(jsonMap, " ", " ")
|
115
|
+
return string(json)
|
116
|
+
}
|
117
|
+
|
118
|
+
var readDir = func(dir string) ([]os.FileInfo, error) {
|
119
|
+
return ioutil.ReadDir(dir)
|
120
|
+
}
|
121
|
+
|
122
|
+
func hasManagedDirs(dir string, rpmDirs map[string]bool) bool {
|
123
|
+
for rpmDir := range rpmDirs {
|
124
|
+
if strings.HasPrefix(rpmDir, dir + "/") {
|
125
|
+
return true
|
126
|
+
}
|
127
|
+
}
|
128
|
+
return false
|
129
|
+
}
|
130
|
+
|
131
|
+
func findUnmanagedFiles(dir string, rpmFiles map[string]string, rpmDirs map[string]bool,
|
132
|
+
unmanagedFiles map[string]string, ignoreList map[string]bool) {
|
133
|
+
files, _ := readDir(dir)
|
134
|
+
for _, f := range files {
|
135
|
+
fileName := dir + f.Name()
|
136
|
+
if !utf8.ValidString(fileName) {
|
137
|
+
fmt.Fprintln(os.Stderr, fileName, "contains invalid UTF-8 characters. Skipping.")
|
138
|
+
} else {
|
139
|
+
if _, ok := ignoreList[fileName]; !ok {
|
140
|
+
if f.IsDir() {
|
141
|
+
if _, ok := rpmDirs[fileName]; ok {
|
142
|
+
findUnmanagedFiles(fileName + "/", rpmFiles, rpmDirs, unmanagedFiles, ignoreList)
|
143
|
+
} else {
|
144
|
+
if !hasManagedDirs(fileName, rpmDirs) {
|
145
|
+
unmanagedFiles[fileName + "/"] = "dir"
|
146
|
+
}
|
147
|
+
}
|
148
|
+
} else {
|
149
|
+
if _, ok := rpmFiles[fileName]; !ok {
|
150
|
+
if f.Mode() &
|
151
|
+
(os.ModeSocket | os.ModeNamedPipe | os.ModeDevice | os.ModeCharDevice) != 0 {
|
152
|
+
// Ignore sockets, named pipes and devices
|
153
|
+
} else if f.Mode() & os.ModeSymlink == os.ModeSymlink {
|
154
|
+
unmanagedFiles[fileName] = "link"
|
155
|
+
} else {
|
156
|
+
unmanagedFiles[fileName] = "file"
|
157
|
+
}
|
158
|
+
}
|
159
|
+
}
|
160
|
+
}
|
161
|
+
}
|
162
|
+
}
|
163
|
+
}
|
164
|
+
|
165
|
+
func printVersion() {
|
166
|
+
fmt.Println("Version:", VERSION)
|
167
|
+
os.Exit(0)
|
168
|
+
}
|
169
|
+
|
170
|
+
func main() {
|
171
|
+
// check for tar extraction
|
172
|
+
if len(os.Args) >= 2 {
|
173
|
+
switch os.Args[1] {
|
174
|
+
case "tar":
|
175
|
+
Tar(os.Args[2:])
|
176
|
+
os.Exit(0)
|
177
|
+
}
|
178
|
+
}
|
179
|
+
|
180
|
+
// parse CLI arguments
|
181
|
+
var versionFlag = flag.Bool("version", false, "shows the version number")
|
182
|
+
flag.Parse()
|
183
|
+
|
184
|
+
// show version
|
185
|
+
if (*versionFlag == true) {
|
186
|
+
printVersion()
|
187
|
+
}
|
188
|
+
|
189
|
+
// fetch unmanaged files
|
190
|
+
unmanagedFiles := make(map[string]string)
|
191
|
+
thisBinary, _ := filepath.Abs(os.Args[0])
|
192
|
+
|
193
|
+
ignoreList := map[string]bool{
|
194
|
+
thisBinary: true,
|
195
|
+
}
|
196
|
+
for _, mount := range RemoteMounts() {
|
197
|
+
ignoreList[mount] = true
|
198
|
+
}
|
199
|
+
for _, mount := range SpecialMounts() {
|
200
|
+
ignoreList[mount] = true
|
201
|
+
}
|
202
|
+
|
203
|
+
for _, mount := range RemoteMounts() {
|
204
|
+
unmanagedFiles[mount + "/"] = "remote_dir"
|
205
|
+
}
|
206
|
+
|
207
|
+
rpmFiles, rpmDirs := getManagedFiles()
|
208
|
+
findUnmanagedFiles("/", rpmFiles, rpmDirs, unmanagedFiles, ignoreList)
|
209
|
+
|
210
|
+
files := make([]string, len(unmanagedFiles))
|
211
|
+
i := 0
|
212
|
+
for k := range unmanagedFiles {
|
213
|
+
files[i] = k
|
214
|
+
i++
|
215
|
+
}
|
216
|
+
sort.Strings(files)
|
217
|
+
|
218
|
+
unmanagedFilesMap := make([]map[string]string, len(unmanagedFiles))
|
219
|
+
for j := range files {
|
220
|
+
entry := make(map[string]string)
|
221
|
+
entry["name"] = files[j]
|
222
|
+
entry["type"] = unmanagedFiles[files[j]]
|
223
|
+
unmanagedFilesMap[j] = entry
|
224
|
+
}
|
225
|
+
|
226
|
+
json := assembleJSON(unmanagedFilesMap)
|
227
|
+
fmt.Println(json)
|
228
|
+
}
|