machinery-tool 1.9.1 → 1.10.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/NEWS +7 -0
- data/html/assets/compare/machinery-compare.js +43 -0
- data/html/assets/compare/machinery.js +17 -0
- data/html/assets/machinery.css +13 -0
- data/html/assets/modal.js +280 -0
- data/html/assets/show/machinery-show.js +16 -9
- data/html/assets/show/machinery.js +61 -25
- data/html/comparison.html.haml +194 -116
- data/html/index.html.haml +46 -12
- data/lib/array.rb +2 -1
- data/lib/cli.rb +44 -23
- data/lib/compare_task.rb +5 -29
- data/lib/comparison.rb +69 -0
- data/lib/config.rb +4 -0
- data/lib/export_task.rb +7 -0
- data/lib/file_scope.rb +2 -2
- data/lib/html.rb +165 -88
- data/lib/machinery.rb +6 -2
- data/lib/{generate_html_task.rb → man_task.rb} +8 -6
- data/lib/object.rb +1 -1
- data/lib/renderer.rb +70 -41
- data/lib/{scope_file_access.rb → scope_file_access_archive.rb} +23 -20
- data/lib/scope_file_access_flat.rb +36 -0
- data/lib/serve_html_task.rb +33 -0
- data/lib/show_task.rb +19 -5
- data/lib/system_file.rb +25 -0
- data/lib/version.rb +1 -1
- data/man/generated/machinery.1.gz +0 -0
- data/man/generated/machinery.1.html +60 -34
- data/plugins/changed_managed_files/changed_managed_files_renderer.rb +5 -5
- data/plugins/config_files/config_files_renderer.rb +7 -6
- data/plugins/groups/groups_renderer.rb +3 -3
- data/plugins/os/os_renderer.rb +5 -5
- data/plugins/packages/packages_model.rb +25 -0
- data/plugins/packages/packages_renderer.rb +36 -5
- data/plugins/patterns/patterns_renderer.rb +3 -3
- data/plugins/repositories/repositories_model.rb +1 -1
- data/plugins/repositories/repositories_renderer.rb +4 -4
- data/plugins/services/services_model.rb +2 -2
- data/plugins/services/services_renderer.rb +3 -3
- data/plugins/unmanaged_files/unmanaged_files_model.rb +2 -1
- data/plugins/unmanaged_files/unmanaged_files_renderer.rb +5 -5
- data/plugins/users/users_renderer.rb +3 -3
- metadata +36 -4
data/html/index.html.haml
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
%html{"ng-app" => "machinery-show"}
|
3
3
|
%head
|
4
4
|
%title
|
5
|
-
=
|
5
|
+
= description_name + " - Machinery System Description"
|
6
6
|
%meta{:charset => 'utf-8'}/
|
7
7
|
%link{:href => "assets/machinery-base.css", :rel => "stylesheet", :type => "text/css"}/
|
8
8
|
%link{:href => "assets/machinery.css", :rel => "stylesheet", :type => "text/css"}/
|
@@ -12,6 +12,7 @@
|
|
12
12
|
%script{:src => "assets/jquery-2.1.1.min.js"}
|
13
13
|
%script{:src => "assets/transition.js"}
|
14
14
|
%script{:src => "assets/collapse.js"}
|
15
|
+
%script{:src => "assets/modal.js"}
|
15
16
|
|
16
17
|
%script#inspection_details_template{:type => "text/ng-template"}
|
17
18
|
#inspection_details.hidden
|
@@ -248,13 +249,13 @@
|
|
248
249
|
%tbody
|
249
250
|
%tr{"ng-repeat" => "service in description.services.services | filter:query"}
|
250
251
|
%td {{service.name}}
|
251
|
-
%td{:class=>"{{services.init_system}}_{{state}}"}
|
252
|
+
%td{:class=>"{{description.services.init_system}}_{{service.state}}"}
|
252
253
|
{{service.state}}
|
253
254
|
|
254
255
|
%script#scope_config_files.partial{:type => "text/ng-template"}
|
255
256
|
%div{"ng-show" => "description.config_files.files"}
|
256
257
|
%a.scope_anchor{:id => "config_files"}
|
257
|
-
#config_files_container.scope
|
258
|
+
#config_files_container.scope{"data-scope" => "config_files"}
|
258
259
|
%scope-header{ |
|
259
260
|
:summary => "'#{scope_help('config_files')}'", |
|
260
261
|
:logo => "'assets/logo-config-files.png'", |
|
@@ -289,7 +290,11 @@
|
|
289
290
|
%tbody
|
290
291
|
%tr{"ng-repeat" => "file in description.config_files.files | filter:query"}
|
291
292
|
%td
|
292
|
-
{
|
293
|
+
%span{"ng-switch" => true, "on" => "file.downloadable"}
|
294
|
+
%a.file-download{"ng-switch-when" => "true", "href" => "#"}
|
295
|
+
{{file.name}}
|
296
|
+
%span{"ng-switch-default" => true}
|
297
|
+
{{file.name}}
|
293
298
|
%a.diff-toggle{"ng-show" => "file.diff", "data-config-file" => "{{file.name}}", "data-toggle" => "popover"}
|
294
299
|
Show diff
|
295
300
|
%td {{file.package_name}}
|
@@ -304,7 +309,7 @@
|
|
304
309
|
%script#scope_changed_managed_files.partial{:type => "text/ng-template"}
|
305
310
|
%div{"ng-show" => "description.changed_managed_files.files"}
|
306
311
|
%a.scope_anchor{:id => "changed_managed_files"}
|
307
|
-
#changed_managed_files_container.scope
|
312
|
+
#changed_managed_files_container.scope{"data-scope" => "changed_managed_files"}
|
308
313
|
%scope-header{ |
|
309
314
|
:summary => "'#{scope_help('changed_managed_files')}'", |
|
310
315
|
:logo => "'assets/logo-changed-managed-files.png'", |
|
@@ -329,7 +334,11 @@
|
|
329
334
|
%th Group
|
330
335
|
%tbody
|
331
336
|
%tr{"ng-repeat" => "file in description.changed_managed_files.files | filter:query"}
|
332
|
-
%td
|
337
|
+
%td{"ng-switch" => true, "on" => "file.downloadable"}
|
338
|
+
%a.file-download{"ng-switch-when" => "true", "href" => "#"}
|
339
|
+
{{file.name}}
|
340
|
+
%span{"ng-switch-default" => true}
|
341
|
+
{{file.name}}
|
333
342
|
%td {{file.package_name}}
|
334
343
|
%td {{file.package_version}}
|
335
344
|
%td
|
@@ -342,7 +351,7 @@
|
|
342
351
|
%script#scope_unmanaged_files.partial{:type => "text/ng-template"}
|
343
352
|
%div{"ng-show" => "description.unmanaged_files.files"}
|
344
353
|
%a.scope_anchor{:id => "unmanaged_files"}
|
345
|
-
#unmanaged_files_container.scope
|
354
|
+
#unmanaged_files_container.scope{"data-scope" => "unmanaged_files"}
|
346
355
|
%scope-header{ |
|
347
356
|
:summary => "'#{scope_help('unmanaged_files')}'", |
|
348
357
|
:logo => "'assets/logo-unmanaged-files.png'", |
|
@@ -362,22 +371,47 @@
|
|
362
371
|
%th Type
|
363
372
|
%tbody
|
364
373
|
%tr{"ng-repeat" => "file in description.unmanaged_files.files | filter:query"}
|
365
|
-
%td
|
374
|
+
%td{"ng-switch" => true, "on" => "file.downloadable"}
|
375
|
+
%a.file-download{"ng-switch-when" => "true", "href" => "#"}
|
376
|
+
{{file.name}}
|
377
|
+
%span{"ng-switch-default" => true}
|
378
|
+
{{file.name}}
|
366
379
|
%td {{file.type}}
|
367
380
|
|
368
|
-
%script{:src => "assets/description.js"}
|
369
381
|
%script{:src => "assets/show/machinery.js"}
|
370
382
|
%script{:src => "assets/bootstrap-tooltip.js"}
|
371
383
|
%script{:src => "assets/bootstrap-popover.js"}
|
372
384
|
|
373
|
-
%body
|
385
|
+
%body{"data-description" => description_name}
|
386
|
+
#file-modal.modal.fade
|
387
|
+
.modal-dialog.modal-lg
|
388
|
+
.modal-content
|
389
|
+
.modal-header
|
390
|
+
%button.close{"type" => "button", "data-dismiss" => "modal"}
|
391
|
+
×
|
392
|
+
%h4#file-modal-title
|
393
|
+
.modal-body
|
394
|
+
%textarea#file-modal-file-content{"readonly" => "true"}
|
395
|
+
#file-modal-error{"style" => "display: none"}
|
396
|
+
.modal-footer
|
397
|
+
%a#file-modal-download-link.btn.btn-success{"target" => "_blank"}
|
398
|
+
Download
|
399
|
+
%button.btn.btn-primary{"type" => "button", "data-dismiss" => "modal"}
|
400
|
+
Close
|
401
|
+
|
402
|
+
#file_popover{:style => "display: none"}
|
403
|
+
.header
|
404
|
+
%h3.name
|
405
|
+
.body
|
406
|
+
%textarea.content
|
407
|
+
|
374
408
|
.container-fluid
|
375
409
|
#nav-bar
|
376
410
|
.row
|
377
411
|
.col-xs-1
|
378
412
|
.col-xs-10
|
379
413
|
%h1
|
380
|
-
System '#{
|
414
|
+
System '#{description_name}'
|
381
415
|
|
382
416
|
%a.inspection_details{:href => "#", "data-toggle" => "popover"} (inspection details)
|
383
417
|
.row
|
@@ -386,7 +420,7 @@
|
|
386
420
|
Expand all
|
387
421
|
%a#collapse-all{:href => "#"}
|
388
422
|
Collapse all
|
389
|
-
.col-xs-
|
423
|
+
.col-xs-9
|
390
424
|
%small.pull-right.pad-top
|
391
425
|
created by
|
392
426
|
%a{:href => "http://machinery-project.org", :target => "_blank"}
|
data/lib/array.rb
CHANGED
data/lib/cli.rb
CHANGED
@@ -86,6 +86,9 @@ class Cli
|
|
86
86
|
Machinery::Ui.kill_pager
|
87
87
|
|
88
88
|
case e
|
89
|
+
when GLI::MissingRequiredArgumentsException
|
90
|
+
Machinery::Ui.error("Option --" + e.message)
|
91
|
+
exit 1
|
89
92
|
when GLI::UnknownCommandArgument, GLI::UnknownGlobalArgument,
|
90
93
|
GLI::UnknownCommand, GLI::BadCommandLine, OptionParser::MissingArgument
|
91
94
|
Machinery::Ui.error e.to_s + "\n\n"
|
@@ -359,7 +362,6 @@ class Cli
|
|
359
362
|
end
|
360
363
|
|
361
364
|
|
362
|
-
|
363
365
|
desc "Deploy image to OpenStack cloud"
|
364
366
|
long_desc <<-LONGDESC
|
365
367
|
Deploy system description as image to OpenStack cloud.
|
@@ -421,8 +423,6 @@ class Cli
|
|
421
423
|
end
|
422
424
|
end
|
423
425
|
|
424
|
-
|
425
|
-
|
426
426
|
desc "Export system description as AutoYaST profile"
|
427
427
|
long_desc <<-LONGDESC
|
428
428
|
Export system description as AutoYaST profile
|
@@ -450,8 +450,6 @@ class Cli
|
|
450
450
|
end
|
451
451
|
end
|
452
452
|
|
453
|
-
|
454
|
-
|
455
453
|
desc "Inspect running system"
|
456
454
|
long_desc <<-LONGDESC
|
457
455
|
Inspect running system and generate system descripton from inspected data.
|
@@ -566,6 +564,18 @@ class Cli
|
|
566
564
|
end
|
567
565
|
end
|
568
566
|
|
567
|
+
desc "Shows man page"
|
568
|
+
long_desc <<-LONGDESC
|
569
|
+
Shows the man page of the machinery tool.
|
570
|
+
|
571
|
+
LONGDESC
|
572
|
+
command :man do |c|
|
573
|
+
c.action do
|
574
|
+
task = ManTask.new
|
575
|
+
task.man
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
569
579
|
|
570
580
|
|
571
581
|
desc "Remove system descriptions"
|
@@ -605,6 +615,10 @@ class Cli
|
|
605
615
|
desc: "Show specified scopes", arg_name: "SCOPE_LIST"
|
606
616
|
c.flag ["exclude-scope", :e], type: String, required: false,
|
607
617
|
desc: "Exclude specified scopes", arg_name: "SCOPE_LIST"
|
618
|
+
c.flag [:port, :p], type: Integer, required: false, default_value: @config.http_server_port,
|
619
|
+
desc: "Listen on port PORT", arg_name: "PORT"
|
620
|
+
c.flag [:ip, :i], type: String, required: false, default_value: "127.0.0.1",
|
621
|
+
desc: "Listen on ip address IP", arg_name: "IP"
|
608
622
|
c.switch "pager", required: false, default_value: true,
|
609
623
|
desc: "Pipe output into a pager"
|
610
624
|
c.switch "show-diffs", required: false, negatable: false,
|
@@ -642,8 +656,10 @@ class Cli
|
|
642
656
|
|
643
657
|
task = ShowTask.new
|
644
658
|
opts = {
|
645
|
-
|
646
|
-
|
659
|
+
show_diffs: options["show-diffs"],
|
660
|
+
show_html: options["html"],
|
661
|
+
ip: options["ip"],
|
662
|
+
port: options["port"]
|
647
663
|
}
|
648
664
|
task.show(description, scope_list, filter, opts)
|
649
665
|
end
|
@@ -691,21 +707,6 @@ class Cli
|
|
691
707
|
end
|
692
708
|
end
|
693
709
|
|
694
|
-
desc "Generate an HTML view for a system description"
|
695
|
-
long_desc <<-LONGDESC
|
696
|
-
Generates an HTML view for a system description.
|
697
|
-
LONGDESC
|
698
|
-
arg "NAME"
|
699
|
-
command "generate-html" do |c|
|
700
|
-
c.action do |global_options,options,args|
|
701
|
-
name = shift_arg(args, "NAME")
|
702
|
-
|
703
|
-
description = SystemDescription.load(name, system_description_store)
|
704
|
-
task = GenerateHtmlTask.new
|
705
|
-
task.generate(description)
|
706
|
-
end
|
707
|
-
end
|
708
|
-
|
709
710
|
desc "Show or change machinery's configuration"
|
710
711
|
long_desc <<-LONGDESC
|
711
712
|
Show or change machinery's configuration.
|
@@ -731,12 +732,32 @@ class Cli
|
|
731
732
|
task = ConfigTask.new
|
732
733
|
task.config(key, value)
|
733
734
|
|
734
|
-
if key == "hints" &&
|
735
|
+
if key == "hints" && (value == "false" || value == "off")
|
735
736
|
Machinery::Ui.puts "Hints can be switched on again by '#{$0} config hints=on'."
|
736
737
|
end
|
737
738
|
end
|
738
739
|
end
|
739
740
|
|
741
|
+
desc "Start a webserver serving an HTML view of a system description"
|
742
|
+
long_desc <<-LONGDESC
|
743
|
+
Starts a web server which serves an HTML view for the given system description.
|
744
|
+
LONGDESC
|
745
|
+
arg "NAME"
|
746
|
+
command "serve" do |c|
|
747
|
+
c.flag [:port, :p], type: Integer, required: false, default_value: 7585,
|
748
|
+
desc: "Listen on port PORT", arg_name: "PORT"
|
749
|
+
c.flag [:ip, :i], type: String, required: false, default_value: "127.0.0.1",
|
750
|
+
desc: "Listen on ip IP", arg_name: "IP"
|
751
|
+
|
752
|
+
c.action do |_global_options, options, args|
|
753
|
+
name = shift_arg(args, "NAME")
|
754
|
+
|
755
|
+
description = SystemDescription.load(name, system_description_store)
|
756
|
+
task = ServeHtmlTask.new
|
757
|
+
task.serve(description, options[:ip], options[:port])
|
758
|
+
end
|
759
|
+
end
|
760
|
+
|
740
761
|
def self.system_description_store
|
741
762
|
if ENV.has_key?("MACHINERY_DIR")
|
742
763
|
SystemDescriptionStore.new(ENV["MACHINERY_DIR"])
|
data/lib/compare_task.rb
CHANGED
@@ -36,8 +36,8 @@ class CompareTask
|
|
36
36
|
|
37
37
|
scopes.each do |scope|
|
38
38
|
if description1[scope] && description2[scope]
|
39
|
-
comparison =
|
40
|
-
diff[scope] = comparison.
|
39
|
+
comparison = Comparison.compare_scope(description1, description2, scope)
|
40
|
+
diff[scope] = comparison.as_json
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
@@ -54,37 +54,13 @@ class CompareTask
|
|
54
54
|
identical = true
|
55
55
|
identical_scopes = []
|
56
56
|
common_scopes = false
|
57
|
-
store = description1.store
|
58
57
|
scopes.each do |scope|
|
59
58
|
if description1[scope] && description2[scope]
|
60
|
-
comparison =
|
59
|
+
comparison = Comparison.compare_scope(description1, description2, scope)
|
61
60
|
|
62
|
-
|
63
|
-
description1.name,
|
64
|
-
store,
|
65
|
-
scope => comparison[0]
|
66
|
-
)
|
67
|
-
|
68
|
-
partial_description2 = SystemDescription.new(
|
69
|
-
description2.name,
|
70
|
-
store,
|
71
|
-
scope => comparison[1]
|
72
|
-
)
|
73
|
-
|
74
|
-
partial_description_common = SystemDescription.new(
|
75
|
-
"common",
|
76
|
-
store,
|
77
|
-
scope => comparison[2]
|
78
|
-
)
|
79
|
-
|
80
|
-
output += Renderer.for(scope).render_comparison(
|
81
|
-
partial_description1,
|
82
|
-
partial_description2,
|
83
|
-
partial_description_common,
|
84
|
-
options
|
85
|
-
)
|
61
|
+
output += Renderer.for(scope).render_comparison(comparison, options)
|
86
62
|
|
87
|
-
if
|
63
|
+
if comparison.only_in1 || comparison.only_in2 || comparison.changed
|
88
64
|
identical = false
|
89
65
|
else
|
90
66
|
identical_scopes << scope
|
data/lib/comparison.rb
ADDED
@@ -0,0 +1,69 @@
|
|
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 Comparison
|
19
|
+
attr_accessor :name1, :name2, :only_in1, :only_in2, :changed, :common, :store, :scope
|
20
|
+
|
21
|
+
def self.compare_scope(description1, description2, scope)
|
22
|
+
result = new
|
23
|
+
result.store = description1.store
|
24
|
+
result.scope = scope
|
25
|
+
result.name1 = description1.name
|
26
|
+
result.name2 = description2.name
|
27
|
+
|
28
|
+
if !description1[scope]
|
29
|
+
result.only_in2 = description2[scope]
|
30
|
+
elsif !description2[scope]
|
31
|
+
result.only_in1 = description1[scope]
|
32
|
+
else
|
33
|
+
result.only_in1, result.only_in2, result.changed, result.common =
|
34
|
+
description1[scope].compare_with(description2[scope])
|
35
|
+
end
|
36
|
+
|
37
|
+
result
|
38
|
+
end
|
39
|
+
|
40
|
+
def as_description(which)
|
41
|
+
case which
|
42
|
+
when :one
|
43
|
+
name = name1
|
44
|
+
data = only_in1
|
45
|
+
when :two
|
46
|
+
name = name2
|
47
|
+
data = only_in2
|
48
|
+
when :common
|
49
|
+
name = "common"
|
50
|
+
data = common
|
51
|
+
else
|
52
|
+
raise "'which' has to be :one, :two or :common"
|
53
|
+
end
|
54
|
+
|
55
|
+
SystemDescription.new(name, store, scope => data)
|
56
|
+
end
|
57
|
+
|
58
|
+
def as_json
|
59
|
+
json = {}
|
60
|
+
json["only_in1"] = only_in1.as_json if only_in1
|
61
|
+
json["only_in2"] = only_in2.as_json if only_in2
|
62
|
+
if changed
|
63
|
+
json["changed"] = changed.map { |elements| [elements.first.as_json, elements.last.as_json] }
|
64
|
+
end
|
65
|
+
json["common"] = common.as_json if common
|
66
|
+
|
67
|
+
json
|
68
|
+
end
|
69
|
+
end
|
data/lib/config.rb
CHANGED
@@ -42,6 +42,10 @@ module Machinery
|
|
42
42
|
default: true,
|
43
43
|
description: "Check whether the current platform is supported by Machinery"
|
44
44
|
)
|
45
|
+
entry("http_server_port",
|
46
|
+
default: 7585,
|
47
|
+
description: "TCP port used by the HTTP server for the HTML view"
|
48
|
+
)
|
45
49
|
end
|
46
50
|
end
|
47
51
|
end
|
data/lib/export_task.rb
CHANGED
@@ -24,6 +24,13 @@ class ExportTask
|
|
24
24
|
@exporter.system_description.assert_scopes("os")
|
25
25
|
@exporter.system_description.validate_export_compatibility
|
26
26
|
|
27
|
+
["unmanaged_files", "changed_managed_files", "config_files"].each do |scope|
|
28
|
+
if @exporter.system_description[scope] &&
|
29
|
+
!@exporter.system_description.scope_extracted?(scope)
|
30
|
+
raise Machinery::Errors::MissingExtractedFiles.new(@exporter.system_description, [scope])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
27
34
|
output_dir = File.join(output_dir, @exporter.export_name)
|
28
35
|
if File.exists?(output_dir)
|
29
36
|
if options[:force]
|
data/lib/file_scope.rb
CHANGED
@@ -29,7 +29,7 @@ class FileScope < Machinery::Object
|
|
29
29
|
only_self = nil if only_self.empty?
|
30
30
|
only_other = nil if only_other.empty?
|
31
31
|
shared = nil if shared.empty?
|
32
|
-
[only_self, only_other, shared]
|
32
|
+
[only_self, only_other, nil, shared]
|
33
33
|
end
|
34
34
|
|
35
35
|
private
|
@@ -57,7 +57,7 @@ class FileScope < Machinery::Object
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def compare_files(other, only_self, only_other, shared)
|
60
|
-
own_files, other_files, shared_files = files.compare_with(other.files)
|
60
|
+
own_files, other_files, _changed, shared_files = files.compare_with(other.files)
|
61
61
|
|
62
62
|
only_self.files = own_files if own_files
|
63
63
|
only_other.files = other_files if other_files
|
data/lib/html.rb
CHANGED
@@ -16,39 +16,177 @@
|
|
16
16
|
# you may find current contact information at www.suse.com
|
17
17
|
|
18
18
|
class Html
|
19
|
-
|
20
|
-
|
19
|
+
module Helpers
|
20
|
+
def scope_help(scope)
|
21
|
+
text = File.read(File.join(Machinery::ROOT, "plugins", "#{scope}/#{scope}.md"))
|
22
|
+
Kramdown::Document.new(text).to_html
|
23
|
+
end
|
24
|
+
|
25
|
+
def diff_to_object(diff)
|
26
|
+
diff = Machinery.scrub(diff)
|
27
|
+
lines = diff.lines[2..-1]
|
28
|
+
diff_object = {
|
29
|
+
file: diff[/--- a(.*)/, 1],
|
30
|
+
additions: lines.select { |l| l.start_with?("+") }.length,
|
31
|
+
deletions: lines.select { |l| l.start_with?("-") }.length
|
32
|
+
}
|
33
|
+
|
34
|
+
original_line_number = 0
|
35
|
+
new_line_number = 0
|
36
|
+
diff_object[:lines] = lines.map do |line|
|
37
|
+
line = ERB::Util.html_escape(line.chomp).
|
38
|
+
gsub("\\", "\").
|
39
|
+
gsub("\t", " " * 8)
|
40
|
+
case line
|
41
|
+
when /^@.*/
|
42
|
+
entry = {
|
43
|
+
type: "header",
|
44
|
+
content: line
|
45
|
+
}
|
46
|
+
original_line_number = line[/-(\d+)/, 1].to_i
|
47
|
+
new_line_number = line[/\+(\d+)/, 1].to_i
|
48
|
+
when /^ .*/, ""
|
49
|
+
entry = {
|
50
|
+
type: "common",
|
51
|
+
new_line_number: new_line_number,
|
52
|
+
original_line_number: original_line_number,
|
53
|
+
content: line[1..-1]
|
54
|
+
}
|
55
|
+
new_line_number += 1
|
56
|
+
original_line_number += 1
|
57
|
+
when /^\+.*/
|
58
|
+
entry = {
|
59
|
+
type: "addition",
|
60
|
+
new_line_number: new_line_number,
|
61
|
+
content: line[1..-1]
|
62
|
+
}
|
63
|
+
new_line_number += 1
|
64
|
+
when /^\-.*/
|
65
|
+
entry = {
|
66
|
+
type: "deletion",
|
67
|
+
original_line_number: original_line_number,
|
68
|
+
content: line[1..-1]
|
69
|
+
}
|
70
|
+
original_line_number += 1
|
71
|
+
end
|
72
|
+
|
73
|
+
entry
|
74
|
+
end
|
75
|
+
|
76
|
+
diff_object
|
77
|
+
end
|
21
78
|
end
|
22
79
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
80
|
+
# this is required for the #generate_comparison method which renders a HAML template manually with
|
81
|
+
# the local binding, so it expects the helper methods to be available in Html. It can be removed
|
82
|
+
# once the comparison was move to the webserver approach as well
|
83
|
+
extend Helpers
|
84
|
+
|
85
|
+
# Creates a new thread running a sinatra webserver which serves the local system descriptions
|
86
|
+
# The Thread object is returned so that the caller can `.join` it until it's finished.
|
87
|
+
def self.run_server(system_description_store, opts)
|
88
|
+
Thread.new do
|
89
|
+
require "sinatra/base"
|
90
|
+
require "mimemagic"
|
91
|
+
|
92
|
+
server = Sinatra.new do
|
93
|
+
set :port, opts[:port] || 7585
|
94
|
+
set :bind, opts[:ip] || "localhost"
|
95
|
+
set :public_folder, File.join(Machinery::ROOT, "html")
|
96
|
+
|
97
|
+
helpers Helpers
|
98
|
+
|
99
|
+
get "/descriptions/:id.js" do
|
100
|
+
description = SystemDescription.load(params[:id], system_description_store)
|
101
|
+
diffs_dir = description.scope_file_store("analyze/config_file_diffs").path
|
102
|
+
if description.config_files && diffs_dir
|
103
|
+
# Enrich description with the config file diffs
|
104
|
+
description.config_files.files.each do |file|
|
105
|
+
path = File.join(diffs_dir, file.name + ".diff")
|
106
|
+
file.diff = diff_to_object(File.read(path)) if File.exists?(path)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Enrich file information with downloadable flag
|
111
|
+
["config_files", "changed_managed_files", "unmanaged_files"].each do |scope|
|
112
|
+
description[scope].files.each do |file|
|
113
|
+
file.downloadable = file.on_disk?
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
description.to_hash.to_json
|
118
|
+
end
|
119
|
+
|
120
|
+
get "/descriptions/:id/files/:scope/*" do
|
121
|
+
description = SystemDescription.load(params[:id], system_description_store)
|
122
|
+
filename = File.join("/", params["splat"].first)
|
123
|
+
|
124
|
+
file = description[params[:scope]].files.find { |f| f.name == filename }
|
125
|
+
|
126
|
+
if request.accept.first.to_s == "text/plain" && file.binary?
|
127
|
+
status 406
|
128
|
+
return "binary file"
|
129
|
+
end
|
130
|
+
|
131
|
+
content = file.content
|
132
|
+
type = MimeMagic.by_path(filename) || MimeMagic.by_magic(content) || "text/plain"
|
133
|
+
|
134
|
+
content_type type
|
135
|
+
attachment File.basename(filename)
|
136
|
+
|
137
|
+
content
|
138
|
+
end
|
139
|
+
|
140
|
+
get "/:id" do
|
141
|
+
haml File.read(File.join(Machinery::ROOT, "html/index.html.haml")),
|
142
|
+
locals: { description_name: params[:id] }
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
if opts[:ip] != "localhost" && opts[:ip] != "127.0.0.1"
|
147
|
+
Machinery::Ui.puts <<EOF
|
148
|
+
Warning:
|
149
|
+
You specified an IP address other than '127.0.0.1', your server may be reachable from the network.
|
150
|
+
This could lead to confidential data like passwords or private keys being readable by others.
|
151
|
+
EOF
|
152
|
+
end
|
153
|
+
|
154
|
+
begin
|
155
|
+
setup_output_redirection
|
156
|
+
server.run!
|
157
|
+
rescue => e
|
158
|
+
# Re-raise exception in main thread
|
159
|
+
Thread.main.raise e
|
160
|
+
ensure
|
161
|
+
remove_output_redirection
|
35
162
|
end
|
36
163
|
end
|
164
|
+
end
|
37
165
|
|
38
|
-
|
39
|
-
|
166
|
+
def self.when_server_ready(ip, port, &block)
|
167
|
+
20.times do
|
168
|
+
begin
|
169
|
+
TCPSocket.new(ip, port).close
|
170
|
+
block.call
|
171
|
+
return
|
172
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
173
|
+
sleep 0.1
|
174
|
+
end
|
175
|
+
end
|
176
|
+
raise Machinery::Errors::MachineryError, "The web server did not come up in time."
|
177
|
+
end
|
40
178
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
File.
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
179
|
+
def self.setup_output_redirection
|
180
|
+
@orig_stdout = STDOUT.clone
|
181
|
+
@orig_stderr = STDERR.clone
|
182
|
+
server_log = File.join(Machinery::DEFAULT_CONFIG_DIR, "webserver.log")
|
183
|
+
STDOUT.reopen server_log, "w"
|
184
|
+
STDERR.reopen server_log, "w"
|
185
|
+
end
|
186
|
+
|
187
|
+
def self.remove_output_redirection
|
188
|
+
STDOUT.reopen @orig_stdout
|
189
|
+
STDERR.reopen @orig_stderr
|
52
190
|
end
|
53
191
|
|
54
192
|
def self.generate_comparison(diff, target)
|
@@ -68,65 +206,4 @@ class Html
|
|
68
206
|
EOT
|
69
207
|
)
|
70
208
|
end
|
71
|
-
|
72
|
-
# Template helpers
|
73
|
-
|
74
|
-
def self.scope_help(scope)
|
75
|
-
text = File.read(File.join(Machinery::ROOT, "plugins", "#{scope}/#{scope}.md"))
|
76
|
-
Kramdown::Document.new(text).to_html
|
77
|
-
end
|
78
|
-
|
79
|
-
def self.diff_to_object(diff)
|
80
|
-
diff = Machinery.scrub(diff)
|
81
|
-
lines = diff.lines[2..-1]
|
82
|
-
diff_object = {
|
83
|
-
file: diff[/--- a(.*)/, 1],
|
84
|
-
additions: lines.select { |l| l.start_with?("+") }.length,
|
85
|
-
deletions: lines.select { |l| l.start_with?("-") }.length
|
86
|
-
}
|
87
|
-
|
88
|
-
original_line_number = 0
|
89
|
-
new_line_number = 0
|
90
|
-
diff_object[:lines] = lines.map do |line|
|
91
|
-
line = ERB::Util.html_escape(line.chomp).
|
92
|
-
gsub("\\", "\").
|
93
|
-
gsub("\t", " "*8)
|
94
|
-
case line
|
95
|
-
when /^@.*/
|
96
|
-
entry = {
|
97
|
-
type: "header",
|
98
|
-
content: line
|
99
|
-
}
|
100
|
-
original_line_number = line[/-(\d+)/, 1].to_i
|
101
|
-
new_line_number = line[/\+(\d+)/, 1].to_i
|
102
|
-
when /^ .*/, ""
|
103
|
-
entry = {
|
104
|
-
type: "common",
|
105
|
-
new_line_number: new_line_number,
|
106
|
-
original_line_number: original_line_number,
|
107
|
-
content: line[1..-1]
|
108
|
-
}
|
109
|
-
new_line_number += 1
|
110
|
-
original_line_number += 1
|
111
|
-
when /^\+.*/
|
112
|
-
entry = {
|
113
|
-
type: "addition",
|
114
|
-
new_line_number: new_line_number,
|
115
|
-
content: line[1..-1]
|
116
|
-
}
|
117
|
-
new_line_number += 1
|
118
|
-
when /^\-.*/
|
119
|
-
entry = {
|
120
|
-
type: "deletion",
|
121
|
-
original_line_number: original_line_number,
|
122
|
-
content: line[1..-1]
|
123
|
-
}
|
124
|
-
original_line_number += 1
|
125
|
-
end
|
126
|
-
|
127
|
-
entry
|
128
|
-
end
|
129
|
-
|
130
|
-
diff_object
|
131
|
-
end
|
132
209
|
end
|