machinery-tool 1.13.0 → 1.14.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 -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
|
+
}
|