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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.git_revision +1 -0
  3. data/NEWS +10 -0
  4. data/bin/machinery +7 -1
  5. data/{helpers → filters}/default_filters.json +0 -0
  6. data/{helpers → filters}/filter-packages-for-build.yaml +0 -0
  7. data/{helpers → inspect_helpers}/changed_files.sh +0 -0
  8. data/{helpers → inspect_helpers}/yum_repositories.py +0 -0
  9. data/lib/cli.rb +136 -52
  10. data/lib/compare_task.rb +1 -1
  11. data/lib/constants.rb +1 -1
  12. data/lib/docker_system.rb +135 -0
  13. data/lib/exceptions.rb +3 -2
  14. data/lib/filter.rb +1 -1
  15. data/lib/hint.rb +8 -2
  16. data/lib/inspect_task.rb +3 -3
  17. data/lib/kiwi_config.rb +2 -2
  18. data/lib/list_task.rb +14 -7
  19. data/lib/local_system.rb +20 -2
  20. data/lib/machinery.rb +2 -0
  21. data/lib/machinery_helper.rb +17 -14
  22. data/lib/move_task.rb +22 -0
  23. data/lib/remote_system.rb +4 -0
  24. data/lib/scope_file_access_archive.rb +10 -0
  25. data/lib/scope_file_access_flat.rb +4 -0
  26. data/lib/show_task.rb +1 -1
  27. data/lib/system.rb +16 -2
  28. data/lib/system_description.rb +19 -9
  29. data/lib/system_description_store.rb +10 -0
  30. data/lib/version.rb +1 -1
  31. data/lib/workload_mapper.rb +2 -1
  32. data/machinery-helper/README.md +13 -0
  33. data/machinery-helper/Rakefile +124 -0
  34. data/machinery-helper/machinery_helper.go +228 -0
  35. data/machinery-helper/machinery_helper_test.go +192 -0
  36. data/machinery-helper/mountpoints.go +81 -0
  37. data/machinery-helper/mountpoints_test.go +74 -0
  38. data/machinery-helper/tar.go +142 -0
  39. data/machinery-helper/version.go +5 -0
  40. data/man/generated/machinery.1.gz +0 -0
  41. data/man/generated/machinery.1.html +43 -6
  42. data/plugins/changed_managed_files/changed_managed_files_inspector.rb +1 -1
  43. data/plugins/config_files/config_files_inspector.rb +1 -1
  44. data/plugins/config_files/config_files_renderer.rb +1 -1
  45. data/plugins/environment/environment_inspector.rb +2 -2
  46. data/plugins/environment/schema/system-description-environment.schema-v5.json +4 -0
  47. data/plugins/os/os_inspector.rb +23 -11
  48. data/plugins/repositories/repositories_inspector.rb +1 -1
  49. data/plugins/services/services_inspector.rb +6 -1
  50. data/plugins/unmanaged_files/unmanaged_files_inspector.rb +35 -30
  51. data/plugins/unmanaged_files/unmanaged_files_model.rb +12 -6
  52. data/workload_mapper/docker-registry/clue.rb +0 -2
  53. data/workload_mapper/docker-registry/compose-template.yml +2 -1
  54. data/workload_mapper/docker-registry/container/Dockerfile +1 -1
  55. data/workload_mapper/mariadb/clue.rb +1 -0
  56. data/workload_mapper/mariadb/compose-template.yml +2 -2
  57. data/workload_mapper/rails/compose-template.yml +0 -1
  58. data/workload_mapper/rails/container/Dockerfile +3 -4
  59. data/workload_mapper/wordpress/clue.rb +13 -0
  60. data/workload_mapper/wordpress/compose-template.yml +5 -0
  61. data/workload_mapper/wordpress/config/wp-config.php +38 -0
  62. data/workload_mapper/wordpress/container/Dockerfile +25 -0
  63. data/workload_mapper/wordpress/container/apache2/listen.conf +1 -0
  64. data/workload_mapper/wordpress/container/apache2/wordpress_vhost.conf +21 -0
  65. data/workload_mapper/wordpress/setup/setup.rb.erb +67 -0
  66. metadata +26 -8
  67. 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
 
@@ -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
- # /usr/share/machinery/helpers/<arch>/machinery-helper
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 = "/usr/share/machinery/helpers"
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(@local_helpers_path, @arch, "machinery-helper")
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::REMOTE_HELPERS_PATH)
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(File.join(Machinery::REMOTE_HELPERS_PATH, "machinery-helper"))
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
@@ -19,6 +19,10 @@ class RemoteSystem < System
19
19
  attr_accessor :host
20
20
  attr_accessor :remote_user
21
21
 
22
+ def type
23
+ "remote"
24
+ end
25
+
22
26
  def initialize(host, remote_user = "root")
23
27
  @host = host
24
28
  @remote_user = remote_user
@@ -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(
@@ -33,4 +33,8 @@ module ScopeFileAccessFlat
33
33
  output = Cheetah.run("file", path, stdout: :capture)
34
34
  !output.include?("ASCII")
35
35
  end
36
+
37
+ def has_file?(name)
38
+ return true if files.any? { |file| file.name == name }
39
+ end
36
40
  end
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.validate_existence_of_package("xdg-utils")
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, "helpers", args.shift))
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
@@ -276,19 +276,29 @@ class SystemDescription < Machinery::Object
276
276
  end
277
277
 
278
278
  def has_file?(name)
279
- self["config_files"].files.any? { |file| file.name == name } ||
280
- self["unmanaged_files"].files.any? { |file| file.name == name }
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
- if scope_extracted?("config_files")
285
- file = self["config_files"].files.find { |f| f.name == path }
286
- return parse_variable_assignment(file.content, key) if file
287
- end
288
- if scope_extracted?("unmanaged_files")
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
@@ -17,6 +17,6 @@
17
17
 
18
18
  module Machinery
19
19
 
20
- VERSION = "1.13.0"
20
+ VERSION = "1.14.0"
21
21
 
22
22
  end
@@ -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
- Cheetah.run("tar", "zxf", tgz_file, "-C", output_path, "--strip=1")
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
+ }