machinery-tool 1.8.2 → 1.9.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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/NEWS +13 -0
  3. data/helpers/changed_files.sh +105 -0
  4. data/helpers/filter-packages-for-build.yaml +2 -0
  5. data/lib/array.rb +8 -0
  6. data/lib/autoyast.rb +28 -26
  7. data/lib/build_task.rb +11 -2
  8. data/lib/changed_rpm_files_helper.rb +26 -7
  9. data/lib/cli.rb +12 -2
  10. data/lib/compare_task.rb +9 -2
  11. data/lib/config.rb +4 -0
  12. data/lib/element_filter.rb +4 -0
  13. data/lib/exceptions.rb +1 -0
  14. data/lib/exporter.rb +4 -0
  15. data/lib/file_validator.rb +3 -3
  16. data/lib/helper.rb +10 -4
  17. data/lib/inspector.rb +26 -0
  18. data/lib/kiwi_config.rb +36 -35
  19. data/lib/local_system.rb +19 -9
  20. data/lib/machinery.rb +2 -0
  21. data/lib/manifest.rb +1 -1
  22. data/lib/object.rb +8 -0
  23. data/lib/scope.rb +7 -1
  24. data/lib/scope_file_access.rb +67 -0
  25. data/lib/system.rb +2 -2
  26. data/lib/system_description.rb +26 -21
  27. data/lib/system_description_store.rb +1 -1
  28. data/lib/system_file.rb +44 -0
  29. data/lib/tarball.rb +1 -1
  30. data/lib/ui.rb +27 -0
  31. data/lib/upgrade_format_task.rb +1 -1
  32. data/lib/version.rb +1 -1
  33. data/lib/zypper.rb +1 -1
  34. data/man/generated/machinery.1.gz +0 -0
  35. data/man/generated/machinery.1.html +1 -1
  36. data/plugins/changed_managed_files/changed_managed_files_inspector.rb +27 -11
  37. data/plugins/changed_managed_files/changed_managed_files_model.rb +2 -1
  38. data/plugins/changed_managed_files/schema/system-description-changed-managed-files.schema-v4.json +126 -0
  39. data/plugins/config_files/config_files_inspector.rb +28 -23
  40. data/plugins/config_files/config_files_model.rb +2 -1
  41. data/plugins/config_files/schema/system-description-config-files.schema-v4.json +126 -0
  42. data/plugins/groups/schema/system-description-groups.schema-v4.json +30 -0
  43. data/plugins/os/os_inspector.rb +2 -2
  44. data/plugins/os/schema/system-description-os.schema-v4.json +21 -0
  45. data/plugins/packages/schema/system-description-packages.schema-v4.json +34 -0
  46. data/plugins/patterns/schema/system-description-patterns.schema-v4.json +24 -0
  47. data/plugins/repositories/schema/system-description-repositories.schema-v4.json +45 -0
  48. data/plugins/services/schema/system-description-services.schema-v4.json +30 -0
  49. data/plugins/unmanaged_files/schema/system-description-unmanaged-files.schema-v4.json +144 -0
  50. data/plugins/unmanaged_files/unmanaged_files_inspector.rb +34 -26
  51. data/plugins/unmanaged_files/unmanaged_files_model.rb +2 -1
  52. data/plugins/users/schema/system-description-users.schema-v4.json +61 -0
  53. data/schema/migrations/migrate3to4.rb +52 -0
  54. data/schema/system-description-global.schema-v4.json +43 -0
  55. metadata +17 -3
  56. data/helpers/changed_managed_files.sh +0 -46
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a9dfaf00e73b823dc8164aa283469baefd8e9ea4
4
- data.tar.gz: ccaf2f58602a73a1cac8a396c2134fc520ba48b6
3
+ metadata.gz: 7fa664c89d067292bb4703e9b8bba47651981831
4
+ data.tar.gz: 45c13a6f27432d9fc09ab306cdad7c07680df73f
5
5
  SHA512:
6
- metadata.gz: 493bf1127ae653467d41ea5c4c3ab20ac28b546cd10e1018b8de2c881faf57ca5c1c2e488cdd32679477452ed7a748ff2013a0e655a001cf9eb7ec8c842c78d3
7
- data.tar.gz: 397f7be936594a0b1b0eabadc988a360e4b561407787b4e809c8b16842e5e7d05f814d5548a0e9baaca98cbc2af870973d0037049067fe8a2fe7b86b6a481e3e
6
+ metadata.gz: f31cd802cb1ce442b18bb0dd374f9308371bf7376e168d97608d5a0ff94e10164d11c6cc0ed58c40aaad230338430c966f5013f3a8c49c940ab1ed45f810ca32
7
+ data.tar.gz: b1d69fc9f41836dab29ff51e449861517527b9b205ad5e6ccc6ef981199b40386518a93c02a98d3454b25bf842914c8da9f106a15179aa71e11c2e8b8b924908
data/NEWS CHANGED
@@ -1,6 +1,19 @@
1
1
  # Machinery Release Notes
2
2
 
3
3
 
4
+ ## Version 1.9.0 - Wed Jun 17 09:57:07 CEST 2015 - thardeck@suse.de
5
+
6
+ * Support links in changed managed files scope
7
+ * Support links in config files scope
8
+ * Introduce system description format version 4 (see https://github.com/SUSE/machinery/wiki/System-Description-Format#version-4)
9
+ * Show progress during inspection of scopes
10
+ * Show message when compared scopes are identical (gh#SUSE/machinery#631)
11
+ * Check number of parameters in all commands (gh#SUSE/machinery#898)
12
+ * Do not abort when Machinery is run on unsupported platforms, but just show a warning
13
+ * Apply all filters on file extraction (gh#SUSE/machinery#887)
14
+ * Consistent output of filters with --verbose option (gh#SUSE/machinery#797)
15
+ * Fix export of changed managed files with quotes in file name (gh#SUSE/machinery#913)
16
+
4
17
  ## Version 1.8.2 - Wed May 27 16:56:44 CEST 2015 - thardeck@suse.de
5
18
 
6
19
  * Fixed repository inspection using a non-root user
@@ -0,0 +1,105 @@
1
+ #!/bin/bash
2
+ # Copyright (c) 2013-2015 SUSE LLC
3
+ #
4
+ # This program is free software; you can redistribute it and/or
5
+ # modify it under the terms of version 3 of the GNU General Public License as
6
+ # published by the Free Software Foundation.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program; if not, contact SUSE LLC.
15
+ #
16
+ # To contact SUSE about this file by physical or electronic mail,
17
+ # you may find current contact information at www.suse.com
18
+
19
+ # Print a list of each package with changed managed files followed by a list of
20
+ # the changed files, e.g.
21
+ #
22
+ # libpulse0-4.0.git.270.g9490a:
23
+ # S.5...... c /etc/pulse/client.conf
24
+ # ntp-4.2.6p5:
25
+ # S.5...... c /etc/ntp.conf
26
+
27
+ # Exit immediately if a command exits with a non-zero status.
28
+ set -e
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
+ if [ $UID -ne "0" ]; then
49
+ sudoprefix="sudo -n"
50
+ fi
51
+
52
+ rpm_supports_noscripts_option () {
53
+ rpm -V --noscripts rpm > /dev/null
54
+ }
55
+
56
+ if rpm_supports_noscripts_option; then
57
+ noscripts="--noscripts"
58
+ fi
59
+
60
+ package_contains_verify_script () {
61
+ rpm --scripts -q $1 | grep "verify scriptlet" > /dev/null
62
+ }
63
+
64
+ check_output () {
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
+
@@ -1,4 +1,6 @@
1
1
  # check lib/os.rb for the distribution names
2
+ "openSUSE 13.2 (Harlequin)":
3
+ - openSUSE-release-dvd
2
4
  "openSUSE 13.1 (Bottle)":
3
5
  - openSUSE-release-dvd
4
6
  "SUSE Linux Enterprise Server 12":
@@ -49,11 +49,19 @@ module Machinery
49
49
  end
50
50
 
51
51
  attr_reader :elements
52
+ attr_accessor :scope
52
53
 
53
54
  def initialize(elements = [])
54
55
  @elements = self.class.convert_raw_array(elements)
55
56
  end
56
57
 
58
+ def scope=(scope)
59
+ @scope = scope
60
+ @elements.each do |child|
61
+ child.scope = @scope if child.respond_to?(:scope=)
62
+ end
63
+ end
64
+
57
65
  def ==(other)
58
66
  self.class == other.class && @elements == other.elements
59
67
  end
@@ -82,8 +82,8 @@ class Autoyast < Exporter
82
82
  apply_groups(xml)
83
83
  apply_services(xml)
84
84
 
85
- apply_extracted_files("config_files")
86
- apply_extracted_files("changed_managed_files")
85
+ apply_changed_files("config_files")
86
+ apply_changed_files("changed_managed_files")
87
87
  apply_unmanaged_files
88
88
  xml.scripts do
89
89
  apply_url_extraction(xml)
@@ -262,33 +262,37 @@ class Autoyast < Exporter
262
262
  end
263
263
  end
264
264
 
265
- def apply_extracted_files(scope)
266
- return if !@system_description[scope] || !@system_description[scope].extracted
267
-
268
- base = Pathname(@system_description.scope_file_store(scope).path)
269
- Dir["#{base}/**/*"].sort.each do |path|
270
- next if File.directory?(path)
271
-
272
- relative_path = Pathname(path).relative_path_from(base).to_s
273
- url = "`cat /tmp/description_url`/#{URI.escape(File.join(scope, relative_path))}"
274
-
275
- @chroot_scripts << "mkdir -p '#{File.join("/mnt", File.dirname(relative_path))}'"
276
- @chroot_scripts << "curl -o '#{File.join("/mnt", relative_path)}' \"#{url}\""
277
- end
278
-
279
- @system_description[scope].files.map do |file|
280
- if file.user
281
- @chroot_scripts << "chown #{file.user}:#{file.group} '#{File.join("/mnt", file.name)}'"
282
- end
283
- if file.mode
284
- @chroot_scripts << "chmod #{file.mode} '#{File.join("/mnt", file.name)}'"
265
+ def apply_changed_files(scope)
266
+ return if !@system_description.scope_extracted?(scope)
267
+
268
+ @system_description[scope].files.each do |file|
269
+ if file.deleted?
270
+ @chroot_scripts << "rm -rf '#{quote(file.name)}'"
271
+ elsif file.directory?
272
+ @chroot_scripts << <<EOF.strip
273
+ chmod #{file.mode} '#{File.join("/mnt", quote(file.name))}'
274
+ chown #{file.user}:#{file.group} '#{File.join("/mnt", quote(file.name))}'
275
+ EOF
276
+ elsif file.file?
277
+ url = "`cat /tmp/description_url`/#{URI.escape(File.join(scope, quote(file.name)))}"
278
+ @chroot_scripts << <<EOF.strip
279
+ mkdir -p '#{File.join("/mnt", File.dirname(quote(file.name)))}'
280
+ curl -o '#{File.join("/mnt", quote(file.name))}' \"#{url}\"
281
+ chmod #{file.mode} '#{File.join("/mnt", quote(file.name))}'
282
+ chown #{file.user}:#{file.group} '#{File.join("/mnt", quote(file.name))}'
283
+ EOF
284
+ elsif file.link?
285
+ @chroot_scripts << <<EOF.strip
286
+ rm -rf '#{File.join("/mnt", quote(file.name))}'
287
+ ln -s '#{quote(file.target)}' '#{File.join("/mnt", quote(file.name))}'
288
+ chown --no-dereference #{file.user}:#{file.group} '#{File.join("/mnt", quote(file.name))}'
289
+ EOF
285
290
  end
286
291
  end
287
292
  end
288
293
 
289
294
  def apply_unmanaged_files
290
- return if !@system_description.unmanaged_files ||
291
- !@system_description.unmanaged_files.extracted
295
+ return if !@system_description.scope_extracted?("unmanaged_files")
292
296
 
293
297
  base = Pathname(@system_description.scope_file_store("unmanaged_files").path)
294
298
  @chroot_scripts << <<-EOF.chomp.gsub(/^\s+/, "")
@@ -296,8 +300,6 @@ class Autoyast < Exporter
296
300
  EOF
297
301
 
298
302
  Dir["#{base}/**/*.tgz"].sort.each do |path|
299
- next if !path.end_with?(".tgz")
300
-
301
303
  relative_path = Pathname(path).relative_path_from(base).to_s
302
304
  tarball_name = File.basename(path)
303
305
  url = "`cat /tmp/description_url`#{URI.escape(File.join("/unmanaged_files", relative_path))}"
@@ -35,8 +35,12 @@ class BuildTask
35
35
  output_path, img_extension)
36
36
 
37
37
  begin
38
- LoggedCheetah.run("sudo", tmp_script.path, :stdout => $stdout,
39
- :stderr => $stderr)
38
+ LoggedCheetah.run(
39
+ "sudo",
40
+ tmp_script.path,
41
+ stdout: $stdout,
42
+ stderr: $stderr
43
+ )
40
44
  rescue SignalException => e
41
45
  # Handle SIGHUP(1), SIGINT(2) and SIGTERM(15) gracefully
42
46
  if [1, 2, 15].include?(e.signo)
@@ -62,6 +66,11 @@ class BuildTask
62
66
  end
63
67
  end
64
68
  raise
69
+ rescue Cheetah::ExecutionFailed
70
+ raise(
71
+ Machinery::Errors::BuildFailed,
72
+ "The execution of the build script failed."
73
+ )
65
74
  ensure
66
75
  tmp_script.delete
67
76
  end
@@ -69,32 +69,51 @@ module ChangedRpmFilesHelper
69
69
  end
70
70
 
71
71
  def parse_stat_line(line)
72
- mode, user, group, uid, gid, *path = line.split(":")
72
+ mode, user, group, uid, gid, type, *path = line.split(":")
73
73
 
74
74
  user = uid if user == "UNKNOWN"
75
75
  group = gid if group == "UNKNOWN"
76
76
 
77
+ type = case type
78
+ when "directory"
79
+ "dir"
80
+ when "symbolic link"
81
+ "link"
82
+ when "regular file"
83
+ "file"
84
+ end
85
+
77
86
  [path.join(":").chomp,
78
87
  {
79
- :mode => mode,
80
- :user => user,
81
- :group => group
88
+ mode: mode,
89
+ user: user,
90
+ group: group,
91
+ type: type
82
92
  }
83
93
  ]
84
94
  end
85
95
 
96
+ def get_link_target(system, link)
97
+ system.run_command(
98
+ "find", link, "-prune", "-printf", "%l",
99
+ stdout: :capture,
100
+ privileged: true
101
+ ).strip
102
+ end
103
+
86
104
  # get path data for list of files
87
105
  # cur_files is guaranteed to not exceed max command line length
88
106
  def get_file_properties(system, cur_files)
89
107
  ret = {}
90
108
  out = system.run_command(
91
- "stat", "--printf", "%a:%U:%G:%u:%g:%n\\n",
92
- *cur_files,
93
- :stdout => :capture
109
+ "stat", "--printf", "%a:%U:%G:%u:%g:%F:%n\\n",
110
+ *cur_files,
111
+ stdout: :capture
94
112
  )
95
113
  out.each_line do |l|
96
114
  path, values = parse_stat_line(l)
97
115
  ret[path] = values
116
+ ret[path][:target] = get_link_target(system, path) if values[:type] == "link"
98
117
  end
99
118
  ret
100
119
  end
data/lib/cli.rb CHANGED
@@ -37,6 +37,7 @@ class Cli
37
37
  else
38
38
  Machinery.logger.level = Logger::INFO
39
39
  end
40
+ check_exceeding_arguments(command.arguments, args)
40
41
  end
41
42
 
42
43
  post do |global_options,command,options,args|
@@ -56,6 +57,14 @@ class Cli
56
57
 
57
58
  GLI::Commands::Help.skips_post = false
58
59
 
60
+ def self.check_exceeding_arguments(defined, parsed)
61
+ if parsed.size > defined.size
62
+ message = "The given arguments don't match the command's specified arguments."
63
+ raise GLI::BadCommandLine.new(message)
64
+ end
65
+ true
66
+ end
67
+
59
68
  def self.buildable_distributions
60
69
  distribution_string = ""
61
70
  Os.supported_host_systems.each do |distribution|
@@ -74,7 +83,7 @@ class Cli
74
83
  GLI::UnknownCommand, GLI::BadCommandLine, OptionParser::MissingArgument
75
84
  Machinery::Ui.error e.to_s + "\n\n"
76
85
  command = ARGV & @commands.keys.map(&:to_s)
77
- run(command << "--help")
86
+ Machinery::Ui.error "Run '#{$0} #{command.first} --help' for more information."
78
87
  exit 1
79
88
  when Machinery::Errors::MachineryError
80
89
  Machinery.logger.error(e.message)
@@ -331,7 +340,8 @@ class Cli
331
340
 
332
341
  The system description is copied and stored under the provided name.
333
342
  LONGDESC
334
- arg_name "FROM_NAME TO_NAME"
343
+ arg "FROM_NAME"
344
+ arg "TO_NAME"
335
345
  command :copy do |c|
336
346
  c.action do |global_options,options,args|
337
347
  from = shift_arg(args, "FROM_NAME")
@@ -41,7 +41,7 @@ class CompareTask
41
41
  end
42
42
  end
43
43
 
44
- target = "/tmp/machinery-html-comparison"
44
+ target = File.join(Machinery::DEFAULT_CONFIG_DIR, "html-comparison")
45
45
  FileUtils.rm_r(target) if Dir.exists?(target)
46
46
  FileUtils.mkdir_p(target)
47
47
 
@@ -52,6 +52,7 @@ class CompareTask
52
52
  def render_comparison(description1, description2, scopes, options = {})
53
53
  output = ""
54
54
  identical = true
55
+ identical_scopes = []
55
56
  common_scopes = false
56
57
  store = description1.store
57
58
  scopes.each do |scope|
@@ -85,6 +86,8 @@ class CompareTask
85
86
 
86
87
  if partial_description1[scope] || partial_description2[scope]
87
88
  identical = false
89
+ else
90
+ identical_scopes << scope
88
91
  end
89
92
  common_scopes = true
90
93
  else
@@ -94,9 +97,13 @@ class CompareTask
94
97
  identical = false if description1[scope] || description2[scope]
95
98
  end
96
99
  end
97
-
98
100
  output = "Compared descriptions are identical.\n" + output if identical && common_scopes
99
101
 
102
+ if !identical_scopes.empty?
103
+ phrase = Machinery::pluralize(identical_scopes.count, "scope is", "scopes are")
104
+ output += "Following #{phrase} identical in both descriptions: " + identical_scopes.join(",")
105
+ end
106
+
100
107
  output
101
108
  end
102
109
  end
@@ -38,6 +38,10 @@ module Machinery
38
38
  description: "Enable experimental features. See " \
39
39
  "https://github.com/SUSE/machinery/wiki/Experimental-Features for more details"
40
40
  )
41
+ entry("perform_support_check",
42
+ default: true,
43
+ description: "Check whether the current platform is supported by Machinery"
44
+ )
41
45
  end
42
46
  end
43
47
  end
@@ -29,6 +29,10 @@ class ElementFilter
29
29
  add_matchers(operator, matchers) if operator && matchers
30
30
  end
31
31
 
32
+ def initialize_copy(other)
33
+ @matchers = other.matchers.dup
34
+ end
35
+
32
36
  def add_matchers(operator, matchers)
33
37
  if ![Filter::OPERATOR_EQUALS, Filter::OPERATOR_EQUALS_NOT].include?(operator)
34
38
  raise Machinery::Errors::InvalidFilter.new("Wrong filter operator '#{operator}'")
@@ -118,6 +118,7 @@ module Machinery
118
118
  class ElementFilterTypeMismatch < MachineryError
119
119
  attr_accessor :failed_matcher
120
120
  end
121
+ class FileUtilsError < MachineryError; end
121
122
 
122
123
  class BuildFailed < MachineryError; end
123
124
  class DeployFailed < MachineryError; end
@@ -21,4 +21,8 @@ class Exporter
21
21
 
22
22
  abstract_method :write
23
23
  abstract_method :export_name
24
+
25
+ def quote(name)
26
+ name.gsub("'", "'\\\\''")
27
+ end
24
28
  end