machinery-tool 1.0.2

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 (157) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +674 -0
  3. data/NEWS +143 -0
  4. data/bin/machinery +29 -0
  5. data/helpers/changed_managed_files.sh +32 -0
  6. data/helpers/filter-packages-for-build.yaml +6 -0
  7. data/html/assets/arrow_down.png +0 -0
  8. data/html/assets/arrow_up.png +0 -0
  9. data/html/assets/bootstrap-popover.js +113 -0
  10. data/html/assets/bootstrap-tooltip.js +457 -0
  11. data/html/assets/bootstrap.min.css +5 -0
  12. data/html/assets/collapse.js +174 -0
  13. data/html/assets/hogan-3.0.2.min.mustache.js +5 -0
  14. data/html/assets/jquery-2.1.1.min.js +4 -0
  15. data/html/assets/logo-changed-managed-files-small.png +0 -0
  16. data/html/assets/logo-changed-managed-files.png +0 -0
  17. data/html/assets/logo-config-files-small.png +0 -0
  18. data/html/assets/logo-config-files.png +0 -0
  19. data/html/assets/logo-groups-small.png +0 -0
  20. data/html/assets/logo-groups.png +0 -0
  21. data/html/assets/logo-os-small.png +0 -0
  22. data/html/assets/logo-os.png +0 -0
  23. data/html/assets/logo-packages-small.png +0 -0
  24. data/html/assets/logo-packages.png +0 -0
  25. data/html/assets/logo-patterns-small.png +0 -0
  26. data/html/assets/logo-patterns.png +0 -0
  27. data/html/assets/logo-repositories-small.png +0 -0
  28. data/html/assets/logo-repositories.png +0 -0
  29. data/html/assets/logo-services-small.png +0 -0
  30. data/html/assets/logo-services.png +0 -0
  31. data/html/assets/logo-unmanaged-files-small.png +0 -0
  32. data/html/assets/logo-unmanaged-files.png +0 -0
  33. data/html/assets/logo-users-small.png +0 -0
  34. data/html/assets/logo-users.png +0 -0
  35. data/html/assets/machinery-base.css +5767 -0
  36. data/html/assets/machinery.css +131 -0
  37. data/html/assets/machinery.js +148 -0
  38. data/html/assets/transition.js +59 -0
  39. data/html/assets/wheels_horizontal.png +0 -0
  40. data/html/index.html.haml +468 -0
  41. data/kiwi_helpers/kiwi_export_readme.md +22 -0
  42. data/kiwi_helpers/merge_users_and_groups.pl.erb +231 -0
  43. data/kiwi_helpers/unmanaged_files_build_excludes +5 -0
  44. data/lib/analyze_config_file_diffs_task.rb +130 -0
  45. data/lib/array.rb +98 -0
  46. data/lib/build_task.rb +124 -0
  47. data/lib/changed_rpm_files_helper.rb +96 -0
  48. data/lib/cli.rb +600 -0
  49. data/lib/compare_task.rb +68 -0
  50. data/lib/config.rb +33 -0
  51. data/lib/config_base.rb +117 -0
  52. data/lib/config_task.rb +56 -0
  53. data/lib/constants.rb +24 -0
  54. data/lib/copy_task.rb +22 -0
  55. data/lib/current_user.rb +23 -0
  56. data/lib/deploy_task.rb +89 -0
  57. data/lib/exceptions.rb +113 -0
  58. data/lib/generate_html_task.rb +22 -0
  59. data/lib/helper.rb +22 -0
  60. data/lib/hint.rb +39 -0
  61. data/lib/html.rb +103 -0
  62. data/lib/inspect_task.rb +93 -0
  63. data/lib/inspector.rb +65 -0
  64. data/lib/kiwi_config.rb +356 -0
  65. data/lib/kiwi_export_task.rb +36 -0
  66. data/lib/list_task.rb +64 -0
  67. data/lib/local_system.rb +127 -0
  68. data/lib/logged_cheetah.rb +25 -0
  69. data/lib/machinery.rb +85 -0
  70. data/lib/machinery_logger.rb +47 -0
  71. data/lib/migration.rb +128 -0
  72. data/lib/mountpoints.rb +72 -0
  73. data/lib/object.rb +138 -0
  74. data/lib/os.rb +78 -0
  75. data/lib/remote_system.rb +114 -0
  76. data/lib/remove_task.rb +43 -0
  77. data/lib/renderer.rb +243 -0
  78. data/lib/renderer_helper.rb +26 -0
  79. data/lib/rpm.rb +52 -0
  80. data/lib/scope_mixin.rb +38 -0
  81. data/lib/show_task.rb +65 -0
  82. data/lib/system.rb +81 -0
  83. data/lib/system_description.rb +228 -0
  84. data/lib/system_description_store.rb +167 -0
  85. data/lib/system_description_validator.rb +216 -0
  86. data/lib/tarball.rb +82 -0
  87. data/lib/ui.rb +74 -0
  88. data/lib/upgrade_format_task.rb +55 -0
  89. data/lib/validate_task.rb +23 -0
  90. data/lib/version.rb +22 -0
  91. data/lib/zypper.rb +70 -0
  92. data/man/generated/machinery.1.gz +0 -0
  93. data/man/generated/machinery.1.html +1056 -0
  94. data/plugins/docs/changed_managed_files.md +2 -0
  95. data/plugins/docs/config_files.md +5 -0
  96. data/plugins/docs/groups.md +2 -0
  97. data/plugins/docs/os.md +2 -0
  98. data/plugins/docs/packages.md +2 -0
  99. data/plugins/docs/patterns.md +5 -0
  100. data/plugins/docs/repositories.md +24 -0
  101. data/plugins/docs/services.md +6 -0
  102. data/plugins/docs/unmanaged_files.md +13 -0
  103. data/plugins/docs/users.md +3 -0
  104. data/plugins/inspect/changed_managed_files_inspector.rb +109 -0
  105. data/plugins/inspect/config_files_inspector.rb +117 -0
  106. data/plugins/inspect/groups_inspector.rb +46 -0
  107. data/plugins/inspect/os_inspector.rb +116 -0
  108. data/plugins/inspect/packages_inspector.rb +46 -0
  109. data/plugins/inspect/patterns_inspector.rb +67 -0
  110. data/plugins/inspect/repositories_inspector.rb +107 -0
  111. data/plugins/inspect/services_inspector.rb +88 -0
  112. data/plugins/inspect/unmanaged_files_inspector.rb +393 -0
  113. data/plugins/inspect/users_inspector.rb +87 -0
  114. data/plugins/model/changed_managed_files_model.rb +29 -0
  115. data/plugins/model/config_files_model.rb +29 -0
  116. data/plugins/model/groups_model.rb +26 -0
  117. data/plugins/model/os_model.rb +20 -0
  118. data/plugins/model/packages_model.rb +26 -0
  119. data/plugins/model/patterns_model.rb +26 -0
  120. data/plugins/model/repositories_model.rb +26 -0
  121. data/plugins/model/services_model.rb +48 -0
  122. data/plugins/model/unmanaged_files_model.rb +29 -0
  123. data/plugins/model/users_model.rb +26 -0
  124. data/plugins/schema/v1/system-description-changed-managed-files.schema.json +83 -0
  125. data/plugins/schema/v1/system-description-config-files.schema.json +83 -0
  126. data/plugins/schema/v1/system-description-groups.schema.json +30 -0
  127. data/plugins/schema/v1/system-description-os.schema.json +21 -0
  128. data/plugins/schema/v1/system-description-packages.schema.json +34 -0
  129. data/plugins/schema/v1/system-description-patterns.schema.json +24 -0
  130. data/plugins/schema/v1/system-description-repositories.schema.json +41 -0
  131. data/plugins/schema/v1/system-description-services.schema.json +30 -0
  132. data/plugins/schema/v1/system-description-unmanaged-files.schema.json +105 -0
  133. data/plugins/schema/v1/system-description-users.schema.json +61 -0
  134. data/plugins/schema/v2/system-description-changed-managed-files.schema.json +92 -0
  135. data/plugins/schema/v2/system-description-config-files.schema.json +92 -0
  136. data/plugins/schema/v2/system-description-groups.schema.json +30 -0
  137. data/plugins/schema/v2/system-description-os.schema.json +21 -0
  138. data/plugins/schema/v2/system-description-packages.schema.json +34 -0
  139. data/plugins/schema/v2/system-description-patterns.schema.json +24 -0
  140. data/plugins/schema/v2/system-description-repositories.schema.json +41 -0
  141. data/plugins/schema/v2/system-description-services.schema.json +30 -0
  142. data/plugins/schema/v2/system-description-unmanaged-files.schema.json +138 -0
  143. data/plugins/schema/v2/system-description-users.schema.json +61 -0
  144. data/plugins/show/changed_managed_files_renderer.rb +46 -0
  145. data/plugins/show/config_files_renderer.rb +62 -0
  146. data/plugins/show/groups_renderer.rb +36 -0
  147. data/plugins/show/os_renderer.rb +31 -0
  148. data/plugins/show/packages_renderer.rb +32 -0
  149. data/plugins/show/patterns_renderer.rb +32 -0
  150. data/plugins/show/repositories_renderer.rb +38 -0
  151. data/plugins/show/services_renderer.rb +32 -0
  152. data/plugins/show/unmanaged_files_renderer.rb +42 -0
  153. data/plugins/show/users_renderer.rb +35 -0
  154. data/schema/migrations/migrate1to2.rb +56 -0
  155. data/schema/v1/system-description-global.schema.json +31 -0
  156. data/schema/v2/system-description-global.schema.json +31 -0
  157. metadata +370 -0
@@ -0,0 +1,22 @@
1
+ # README for Kiwi export from Machinery
2
+
3
+ This directory contains a Kiwi configuration that was exported by
4
+ Machinery.
5
+
6
+ The user is expected to be familiar with using Kiwi, otherwise
7
+ `machinery build` is recommended.
8
+ Details on Kiwi can be found at http://opensuse.github.com/kiwi
9
+
10
+
11
+ ## Creating the image
12
+
13
+ The following command builds the image:
14
+
15
+ `sudo /usr/sbin/kiwi --build EXPORTED_DIRECTORY --destdir OUTPUT_DIRECTORY`
16
+
17
+
18
+ For example if the exported kiwi description is stored under "/tmp/export"
19
+ and the image should be saved under "/tmp/image" the command would look like
20
+ this:
21
+
22
+ `sudo /usr/sbin/kiwi --build /tmp/export --destdir /tmp/image`
@@ -0,0 +1,231 @@
1
+ #!/usr/bin/env perl
2
+
3
+ # Copyright (c) 2013-2014 SUSE LLC
4
+ #
5
+ # This program is free software; you can redistribute it and/or
6
+ # modify it under the terms of version 3 of the GNU General Public License as
7
+ # published by the Free Software Foundation.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, contact SUSE LLC.
16
+ #
17
+ # To contact SUSE about this file by physical or electronic mail,
18
+ # you may find current contact information at www.suse.com
19
+
20
+ use strict;
21
+ use warnings;
22
+ use FileHandle;
23
+
24
+ my $local_passwd_path = shift;
25
+ my $local_shadow_path = shift;
26
+ my $local_group_path = shift;
27
+
28
+
29
+ my $fh = FileHandle->new;
30
+ my @existing_passwd_entries = [];
31
+ if($fh->open("< $local_passwd_path")) {
32
+ @existing_passwd_entries = <$fh>;
33
+ $fh->close;
34
+ }
35
+ my @existing_shadow_entries = [];
36
+ if($fh->open("< $local_shadow_path")) {
37
+ @existing_shadow_entries = <$fh>;
38
+ $fh->close;
39
+ }
40
+ my @existing_group_entries = [];
41
+ if($fh->open("< $local_group_path")) {
42
+ @existing_group_entries = <$fh>;
43
+ $fh->close;
44
+ }
45
+
46
+ # This array contains arrays of the new passwd and shadow entries, e.g.
47
+ # (
48
+ # ["svn:x:482:476:user for Apache Subversion svnserve:/srv/svn:/sbin/nologin", "svn:!:16058::::::"],
49
+ # ["nscd:x:484:478:User for nscd:/var/run/nscd:/sbin/nologin", "nscd:!:16058::::::"]
50
+ # )
51
+ my @new_passwd_entries = (
52
+ <%= passwd_entries %>
53
+ );
54
+
55
+ # This array contains the new group entries, e.g.:
56
+ # (
57
+ # "ntp:x:102:",
58
+ # "at:x:103:"
59
+ # )
60
+ my @new_group_entries = (
61
+ <%= group_entries %>
62
+ );
63
+
64
+ # Takes a passwd entry as the argument and replaces the uid with an id that is
65
+ # not already used in the existing passwd file
66
+ sub fix_ids {
67
+ my $passwd_entry = shift;
68
+ my @fields = split(/:/x, $passwd_entry);
69
+ my $uid = $fields[2];
70
+ my $gid = $fields[3];
71
+
72
+ while(grep { /^[^:]*:[^:]*:$uid:/x } @existing_passwd_entries) {
73
+ $uid = $uid + 1;
74
+ }
75
+
76
+ $passwd_entry =~ s/^([^:]*:[^:]*):\d+:/$1:$uid:/x;
77
+
78
+ return $passwd_entry;
79
+ }
80
+
81
+ # Takes a group entry as the argument and replaces the uid with an id that is
82
+ # not already used in the existing group file
83
+ sub fix_group_id {
84
+ my $group = shift;
85
+ my @fields = split(/:/x, $group);
86
+ my $original_gid = $fields[2];
87
+ my $gid = $original_gid;
88
+
89
+ while(grep { /^[^:]*:[^:]*:$gid:/x } @existing_group_entries) {
90
+ $gid = $gid + 1;
91
+ }
92
+ $group =~ s/^([^:]*:[^:]*):$original_gid:/$1:$gid:/x;
93
+
94
+ # Also update the gid in the list of new passwd entries
95
+ foreach my $entry_ref (@new_passwd_entries) {
96
+ $entry_ref->[0] =~ s/^([^:]*:[^:]*:[^:]*):$original_gid:/$1:$gid:/x;
97
+ }
98
+
99
+ return $group;
100
+ }
101
+
102
+ # Takes two comma-separated lists and merges them. Duplicates are removed.
103
+ sub merge_group_users {
104
+ my $a = $_[0] // "";
105
+ my $b = $_[1] // "";
106
+ chomp($a);
107
+ chomp($b);
108
+ my @users = (split(",", $a), split(",", $b));
109
+
110
+ #remove duplicate entries
111
+ my %hash = ();
112
+ foreach my $item (@users) {
113
+ $hash{$item} = '';
114
+ }
115
+ @users = sort keys %hash;
116
+
117
+ return join(",", @users);
118
+ }
119
+
120
+ # Iterate over the new group entries and fix ids where necessary.
121
+ #
122
+ # If the group already exists on the system the group is not added again, but the
123
+ # new passwd entries that reference this group are adjusted to the other gid.
124
+ #
125
+ # If the group does not exist yet, the group is appended to /etc/group. If
126
+ # necessary, the gid will be adjusted to not conflict with the existing ones.
127
+ my %gid_changes = ();
128
+ foreach my $entry (@new_group_entries) {
129
+ my @fields = split(/:/x, $entry);
130
+ my $id = $fields[0];
131
+ my $gid = $fields[2];
132
+
133
+ my @match = grep { /^$id:.*/x } @existing_group_entries;
134
+ if(scalar @match > 0) {
135
+ print "Group match: $match[0]\n";
136
+
137
+ my @original_fields = split(/:/x, $match[0]);
138
+ my $original_gid = $original_fields[2];
139
+
140
+ # Store all gid transformations in the %gid_changes hash
141
+ $gid_changes{$gid} = $original_gid;
142
+
143
+
144
+ # Update existing entry with the new attributes
145
+ $fields[2] = $original_gid;
146
+ my $users = merge_group_users($original_fields[3], $fields[3]);
147
+ $entry =~ s/^([^:]*:[^:]*):.*/$1:$original_gid:$users/x;
148
+ foreach my $group_ref (@existing_group_entries) {
149
+ $group_ref =~ s/\Q$match[0]\E/$entry/x;
150
+ }
151
+ } else {
152
+ print "No match for group ", $id, " (", $entry,")\n";
153
+
154
+ my $fixed_entry = fix_group_id($entry);
155
+ print " -> Writing: ", $fixed_entry, "\n";
156
+
157
+ push(@existing_group_entries, $fixed_entry);
158
+ }
159
+ }
160
+
161
+ # Apply all gid transformations to the list of new passwd entries
162
+ foreach my $entry_ref (@new_passwd_entries) {
163
+ my @fields = split(/:/x, $entry_ref->[0]);
164
+ my $gid = $fields[3];
165
+
166
+ if($gid_changes{$gid}) {
167
+ $entry_ref->[0] =~ s/^([^:]*:[^:]*:[^:]*):$gid:/$1:$gid_changes{$gid}:/x;
168
+ }
169
+ }
170
+
171
+ # Iterate over the new passwd and shadow entries and fix ids where necessary.
172
+ #
173
+ # If the user already exists on the system the existing uid/gid is reused.
174
+ #
175
+ # If the user does not exist yet it is appended to /etc/passwd and /etc/shadow
176
+ # with a uid that does not conflict.
177
+ foreach my $entry (@new_passwd_entries) {
178
+ my @fields = split(/:/x, @$entry[0]);
179
+ my $id = $fields[0];
180
+
181
+ my @match = grep { /^$id:.*/x } @existing_passwd_entries;
182
+ if(scalar @match > 0) {
183
+ my @original_fields = split(/:/x, $match[0]);
184
+ my $uid = $original_fields[2];
185
+
186
+ $fields[2] = $uid;
187
+ my $new_entry = join(":", @fields);
188
+
189
+ foreach my $passwd_ref (@existing_passwd_entries) {
190
+ $passwd_ref =~ s/\Q$match[0]\E/$new_entry/x;
191
+ }
192
+ if(@$entry[1]) {
193
+ foreach my $shadow_ref (@existing_shadow_entries) {
194
+ $shadow_ref =~ s/^$id.*/@$entry[1]/x;
195
+ }
196
+ }
197
+ } else {
198
+ print "No match for user $id (@$entry[0])\n";
199
+
200
+ my $fixed_entry = fix_ids(@$entry[0]);
201
+ print " ->Writing: ", $fixed_entry, "\n";
202
+
203
+ push(@existing_passwd_entries, $fixed_entry);
204
+ if(@$entry[1]) {
205
+ push(@existing_shadow_entries, @$entry[1]);
206
+ }
207
+ }
208
+ }
209
+
210
+ # Write final lists to files
211
+ if($fh->open("> $local_passwd_path")) {
212
+ foreach(@existing_passwd_entries) {
213
+ chomp;
214
+ $fh->print("$_\n");
215
+ }
216
+ $fh->close;
217
+ }
218
+ if($fh->open("> $local_shadow_path")) {
219
+ foreach(@existing_shadow_entries) {
220
+ chomp;
221
+ $fh->print("$_\n");
222
+ }
223
+ $fh->close;
224
+ }
225
+ if($fh->open("> $local_group_path")) {
226
+ foreach(@existing_group_entries) {
227
+ chomp;
228
+ $fh->print("$_\n");
229
+ }
230
+ $fh->close;
231
+ }
@@ -0,0 +1,5 @@
1
+ var/lib/rpm/*
2
+ etc/passwd*
3
+ etc/shadow*
4
+ etc/group*
5
+ etc/fstab
@@ -0,0 +1,130 @@
1
+ # Copyright (c) 2013-2014 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 AnalyzeConfigFileDiffsTask
19
+ def analyze(description)
20
+ description.assert_scopes("repositories", "config_files")
21
+ if !description.scope_extracted?("config_files")
22
+ raise Machinery::Errors::MissingExtractedFiles.new(description, ["config_files"])
23
+ end
24
+
25
+ with_repositories(description) do |zypper|
26
+ description.initialize_file_store("config-file-diffs")
27
+ diffs_path = description.file_store("config-file-diffs")
28
+ extracted_files_path = description.file_store("config_files")
29
+
30
+ Machinery::Ui.puts "Generating diffs..."
31
+ cnt = 1
32
+ list = files_by_package(description)
33
+ total = list.map(&:files).flatten.length.to_s
34
+ list.each do |package|
35
+ path = zypper.download_package("#{package.name}-#{package.version}")
36
+
37
+ if !path || path.empty?
38
+ Machinery::Ui.warn "Warning: Could not download package #{package.name}-#{package.version}."
39
+ cnt += package.files.length
40
+ next
41
+ end
42
+
43
+ package.files.each do |file|
44
+ diff = Rpm.new(path).diff(file, File.join(extracted_files_path, file))
45
+
46
+ if !diff || diff.empty?
47
+ Machinery::Ui.warn "Warning: Could not generate diff for #{file}."
48
+ else
49
+ diff_path = File.join(diffs_path, file + ".diff")
50
+ FileUtils.mkdir_p(File.dirname(diff_path))
51
+ File.write(diff_path, diff)
52
+ Machinery::Ui.puts "[#{cnt.to_s.rjust(total.length)}/#{total}] #{file}"
53
+ end
54
+
55
+ cnt += 1
56
+ end
57
+ end
58
+ Machinery::Ui.puts "done"
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ # Creates an array of hashes with the RPM names, version and the list of
65
+ # changed file paths, e.g.
66
+ #
67
+ # [
68
+ # {
69
+ # "name" => "aaa_base",
70
+ # "version" => "3.11.1",
71
+ # "files" => ["/etc/modprobe.d/unsupported-modules", "/etc/inittab"]
72
+ # }
73
+ # ]
74
+ def files_by_package(description)
75
+ files = description["config_files"].files.
76
+ select { |f| f.changes.include?("md5") }
77
+
78
+ files.inject({}) do |result, file|
79
+ key = "#{file.package_name}-#{file.package_version}"
80
+ result[key] ||= Package.new(
81
+ "name" => file["package_name"],
82
+ "version" => file["package_version"],
83
+ "files" => []
84
+ )
85
+
86
+ result[key]["files"] << file.name
87
+ result
88
+ end.values
89
+ end
90
+
91
+ def with_repositories(description, &block)
92
+ Machinery::Ui.puts "Setting up repository access..."
93
+ Zypper.isolated do |zypper|
94
+ begin
95
+ remote_repos = description.repositories.reject do |repo|
96
+ repo.url.start_with?("cd://") || repo.url.start_with?("dvd://")
97
+ end
98
+ remote_repos.each do |repo|
99
+ uri = URI.parse(repo.url)
100
+
101
+ if repo.username && repo.password
102
+ uri.user = repo.username
103
+ uri.password = repo.password
104
+ end
105
+
106
+ zypper.add_repo(uri.to_s, repo.alias)
107
+ end
108
+
109
+ begin
110
+ zypper.refresh
111
+ rescue Cheetah::ExecutionFailed => e
112
+ # If zypper is locked (exit code 7) we're doomed. Bail out completeley.
113
+ raise if e.status.exitstatus == 7
114
+
115
+ # Refreshing repositories might fail for various other reasons, but we
116
+ # still stick to the plan because the relevant config files might be
117
+ # available from the other repositories.
118
+ # If they aren't an error message will then be generated for each of
119
+ # the actually missing files instead.
120
+ Machinery.logger.error(e.message)
121
+ Machinery.logger.debug(e.backtrace.join("\n"))
122
+ Machinery.logger.debug("Standard output:\n #{e.stdout}")
123
+ Machinery.logger.debug("Error output:\n #{e.stderr}")
124
+ end
125
+
126
+ block.call(zypper)
127
+ end
128
+ end
129
+ end
130
+ end
data/lib/array.rb ADDED
@@ -0,0 +1,98 @@
1
+ # Copyright (c) 2013-2014 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
+ module Machinery
19
+ class Array
20
+ class << self
21
+ def has_elements(options)
22
+ @element_class = options[:class]
23
+ end
24
+
25
+ def from_json(json)
26
+ elements = json.map do |element|
27
+ if @element_class
28
+ @element_class.from_json(element)
29
+ else
30
+ case element
31
+ when ::Array
32
+ Machinery::Array.from_json(element)
33
+ when Hash
34
+ Machinery::Object.from_json(element)
35
+ else
36
+ element
37
+ end
38
+ end
39
+ end
40
+
41
+ new(elements)
42
+ end
43
+ end
44
+
45
+ attr_reader :elements
46
+
47
+ def initialize(elements = [])
48
+ @elements = elements
49
+ end
50
+
51
+ def ==(other)
52
+ self.class == other.class && @elements == other.elements
53
+ end
54
+
55
+ # Various Array operators such as "-" and "&" use #eql? and #hash to compare
56
+ # array elements, which is why we need to make sure they work properly.
57
+
58
+ alias eql? ==
59
+
60
+ def hash
61
+ @elements.hash
62
+ end
63
+
64
+ def -(other)
65
+ self.class.new(@elements - other.elements)
66
+ end
67
+
68
+ def &(other)
69
+ self.class.new(@elements & other.elements)
70
+ end
71
+
72
+ def as_json
73
+ @elements.map do |element|
74
+ if element.is_a?(Machinery::Array) || element.is_a?(Machinery::Object)
75
+ element.as_json
76
+ else
77
+ element
78
+ end
79
+ end
80
+ end
81
+
82
+ def compare_with(other)
83
+ [
84
+ self - other,
85
+ other - self,
86
+ self & other
87
+ ].map { |e| !e.empty? ? e : nil }
88
+ end
89
+
90
+ def method_missing(name, *args, &block)
91
+ @elements.send(name, *args, &block)
92
+ end
93
+
94
+ def respond_to?(name, include_all = false)
95
+ super || @elements.respond_to?(name, include_all)
96
+ end
97
+ end
98
+ end