machinery-tool 1.14.0 → 1.14.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.git_revision +1 -1
  3. data/NEWS +10 -0
  4. data/html/assets/compare/machinery.js +1 -0
  5. data/html/assets/jquery.searcher.min.js +5 -0
  6. data/html/assets/machinery.css +3 -0
  7. data/html/assets/show/machinery.js +10 -4
  8. data/html/comparison.html.haml +18 -652
  9. data/html/index.html.haml +93 -431
  10. data/html/partials/changed_managed_files.html.haml +41 -0
  11. data/html/partials/compare/alert.html.haml +22 -0
  12. data/html/partials/compare/changed_managed_file_list.html.haml +26 -0
  13. data/html/partials/compare/changed_managed_files.html.haml +57 -0
  14. data/html/partials/compare/config_file_list.html.haml +26 -0
  15. data/html/partials/compare/config_files.html.haml +54 -0
  16. data/html/partials/compare/group_list.html.haml +14 -0
  17. data/html/partials/compare/groups.html.haml +52 -0
  18. data/html/partials/compare/os.html.haml +32 -0
  19. data/html/partials/compare/os_table.html.haml +10 -0
  20. data/html/partials/compare/package_list.html.haml +23 -0
  21. data/html/partials/compare/packages.html.haml +66 -0
  22. data/html/partials/compare/pattern_list.html.haml +12 -0
  23. data/html/partials/compare/patterns.html.haml +53 -0
  24. data/html/partials/compare/repositories.html.haml +51 -0
  25. data/html/partials/compare/repository_list.html.haml +28 -0
  26. data/html/partials/compare/service_list.html.haml +11 -0
  27. data/html/partials/compare/services.html.haml +56 -0
  28. data/html/partials/compare/unmanaged_file_list.html.haml +13 -0
  29. data/html/partials/compare/unmanaged_files.html.haml +59 -0
  30. data/html/partials/compare/user_list.html.haml +18 -0
  31. data/html/partials/compare/users.html.haml +52 -0
  32. data/html/partials/config_files.html.haml +57 -0
  33. data/html/partials/groups.html.haml +25 -0
  34. data/html/partials/os.html.haml +26 -0
  35. data/html/partials/packages.html.haml +30 -0
  36. data/html/partials/patterns.html.haml +23 -0
  37. data/html/partials/repositories.html.haml +35 -0
  38. data/html/partials/scope_header.html.haml +17 -0
  39. data/html/partials/services.html.haml +23 -0
  40. data/html/partials/unmanaged_files.html.haml +28 -0
  41. data/html/partials/users.html.haml +29 -0
  42. data/lib/cli.rb +48 -40
  43. data/lib/exceptions.rb +7 -0
  44. data/lib/helper.rb +21 -0
  45. data/lib/hint.rb +1 -1
  46. data/lib/html.rb +9 -1
  47. data/lib/scope_file_access_archive.rb +1 -2
  48. data/lib/scope_file_access_flat.rb +1 -2
  49. data/lib/server.rb +104 -50
  50. data/lib/version.rb +1 -1
  51. data/machinery-helper/version.go +1 -1
  52. data/man/generated/machinery.1.gz +0 -0
  53. data/man/generated/machinery.1.html +106 -16
  54. metadata +47 -18
  55. data/html/assets/angular-sanitize.min.js +0 -16
  56. data/html/assets/angular.min.js +0 -251
  57. data/html/assets/compare/machinery-compare.js +0 -100
  58. data/html/assets/show/machinery-show.js +0 -72
@@ -0,0 +1,30 @@
1
+ - if packages
2
+ %div
3
+ %a.scope_anchor{ id: "packages" }
4
+ .scope#packages_container
5
+ = render_partial "scope_header",
6
+ :scope => "packages",
7
+ :title => "Packages",
8
+ :count => "#{packages.length} #{Machinery.pluralize(packages.length, "packages")}"
9
+
10
+ .row.scope_content.collapse.in
11
+ .col-xs-1
12
+ .col-xs-11
13
+ %table.table.table-striped.table-hover.table-condensed.filterable
14
+ %thead
15
+ %tr
16
+ %th Name
17
+ %th Version
18
+ %th Release
19
+ %th Arch
20
+ %th Vendor
21
+ %th Checksum
22
+ %tbody
23
+ - packages.each do |package|
24
+ %tr
25
+ %td= package.name
26
+ %td= package.version
27
+ %td= package.release
28
+ %td= package.arch
29
+ %td= package.vendor
30
+ %td= package.checksum
@@ -0,0 +1,23 @@
1
+ - if patterns
2
+ %div
3
+ %a.scope_anchor{ id: "patterns" }
4
+ .scope#patterns_container
5
+ = render_partial "scope_header",
6
+ :scope => "patterns",
7
+ :title => "Patterns",
8
+ :count => "#{patterns.length} #{Machinery.pluralize(patterns.length, "pattern")}"
9
+ .row.scope_content.collapse.in
10
+ .col-xs-1
11
+ .col-xs-11
12
+ %table.table.table-striped.table-hover.table-condensed.filterable
13
+ %thead
14
+ %tr
15
+ %th Name
16
+ %th Version
17
+ %th Release
18
+ %tbody
19
+ - patterns.each do |pattern|
20
+ %tr
21
+ %td= pattern.name
22
+ %td= pattern.version
23
+ %td= pattern.release
@@ -0,0 +1,35 @@
1
+ - if repositories
2
+ %div
3
+ %a.scope_anchor{ id: "repositories" }
4
+ .scope#repositories_container
5
+ - count = repositories.length
6
+ = render_partial "scope_header",
7
+ :scope => "repositories",
8
+ :title => "Repositories",
9
+ :count => "#{count} #{Machinery.pluralize(count, "repository", "repositories")}"
10
+ .row.scope_content.collapse.in
11
+ .col-xs-1
12
+ .col-xs-11
13
+ %table.table.table-striped.table-hover.table-condensed.filterable
14
+ %thead
15
+ %tr
16
+ %th Name
17
+ %th Alias
18
+ %th Type
19
+ %th URL
20
+ %th Enabled
21
+ %th Autorefresh
22
+ %th GPG Check
23
+ %th Priority
24
+ %tbody
25
+ - repositories.each do |repo|
26
+ %tr
27
+ %td= repo.name
28
+ %td= repo.alias
29
+ %td= repo.type
30
+ %td
31
+ %a{ href: repo.url }= repo.url
32
+ %td= repo.enabled
33
+ %td= repo.autorefresh
34
+ %td= repo.gpgcheck
35
+ %td= repo.priority
@@ -0,0 +1,17 @@
1
+ - meta = @description[scope].meta
2
+ .row
3
+ .col-xs-1
4
+ %img.over.scope_logo_big{ "data-content" => "<p>#{scope_help(scope)}</p>",
5
+ "data-toggle" => "popover",
6
+ "src" => "assets/logo-#{scope.tr("_", "-")}.png",
7
+ "title" => "",
8
+ "data-original-title" => title,
9
+ "style" => "top: 125px;" }
10
+ %span.toggle{ "title" => "Collapse/Expand" }
11
+ .col-xs-11
12
+ %h2
13
+ = title
14
+ &nbsp;
15
+ .scope-summary
16
+ = count
17
+ (inspected host: '#{meta.hostname}', at: #{DateTime.parse(meta.modified).strftime("%F %T")})
@@ -0,0 +1,23 @@
1
+ - if services
2
+ %div
3
+ %a.scope_anchor{ id: "services" }
4
+ .scope#services_container
5
+ - count = services.services.length
6
+ = render_partial "scope_header",
7
+ :scope => "services",
8
+ :title => "Services",
9
+ :count => "#{count} #{Machinery.pluralize(count, "service")}"
10
+ .row.scope_content.collapse.in
11
+ .col-xs-1
12
+ .col-xs-11
13
+ %table.table.table-striped.table-hover.table-condensed.filterable
14
+ %thead
15
+ %tr
16
+ %th Name
17
+ %th State
18
+ %tbody
19
+ - services.services.each do |service|
20
+ %tr
21
+ %td= service.name
22
+ %td{ class: "#{services.init_system}_#{service.state}" }
23
+ = service.state
@@ -0,0 +1,28 @@
1
+ - if unmanaged_files && unmanaged_files.files.length > 0
2
+ %div
3
+ %a.scope_anchor{ id: "unmanaged_files" }
4
+ .scope#unmanaged_files_container{ "data-scope" => "unmanaged_files" }
5
+ - count = unmanaged_files.files.length
6
+ = render_partial "scope_header",
7
+ :scope => "unmanaged_files",
8
+ :title => "Unmanaged Files",
9
+ :count => "#{count} #{Machinery.pluralize(count, "file")}"
10
+ .row.scope_content.collapse.in
11
+ .col-xs-1
12
+ .col-xs-11
13
+ %table.table.table-striped.table-hover.table-condensed.files-table.filterable
14
+ %thead
15
+ %tr
16
+ %th Name
17
+ %th Type
18
+ %tbody
19
+ - unmanaged_files.files.each do |file|
20
+ %tr
21
+ %td
22
+ - if file.on_disk?
23
+ %a.file-download{ href: "#" }
24
+ = file.name
25
+ - else
26
+ %span
27
+ = file.name
28
+ %td= file.type
@@ -0,0 +1,29 @@
1
+ - if users
2
+ %div
3
+ %a.scope_anchor{ id: "users" }
4
+ .scope#users_container
5
+ = render_partial "scope_header",
6
+ :scope => "users",
7
+ :title => "Users",
8
+ :count => "#{users.length} #{Machinery.pluralize(users.length, "user")}"
9
+ .row.scope_content.collapse.in
10
+ .col-xs-1
11
+ .col-xs-11
12
+ %table.table.table-striped.table-hover.table-condensed.filterable
13
+ %thead
14
+ %tr
15
+ %th Name
16
+ %th UID
17
+ %th GID
18
+ %th Comment
19
+ %th Home
20
+ %th Shell
21
+ %tbody
22
+ - users.each do |user|
23
+ %tr
24
+ %td= user.name
25
+ %td= user.uid
26
+ %td= user.gid
27
+ %td= user.comment
28
+ %td= user.home
29
+ %td= user.shell
data/lib/cli.rb CHANGED
@@ -248,12 +248,13 @@ class Cli
248
248
 
249
249
  def self.check_port_validity(port)
250
250
  if port < 2 || port > 65535
251
- raise Machinery::Errors::InvalidCommandLine.new("Please choose a port between 2 and " \
252
- "65535.")
251
+ raise Machinery::Errors::ServerPortError.new("The specified port '#{port}' is not " \
252
+ "valid. A valid port can be in a range between 2 and 65535.")
253
253
  else
254
254
  if port >= 2 && port <= 1023 && !CurrentUser.new.is_root?
255
- raise Machinery::Errors::InvalidCommandLine.new("You need root rights when you want " \
256
- "to use a port between 2 and 65535.")
255
+ raise Machinery::Errors::ServerPortError.new("The specified port '#{port}' needs " \
256
+ "root privileges. Otherwise, the server cannot be started. All ports in a range " \
257
+ "of 2-1023 need root privileges.")
257
258
  end
258
259
  end
259
260
  end
@@ -347,14 +348,6 @@ class Cli
347
348
  c.switch "show-all", required: false, negatable: false,
348
349
  desc: "Show also common properties"
349
350
  if @config.experimental_features
350
- c.flag [:port, :p], type: Integer, required: false, default_value: @config.http_server_port,
351
- desc: "Listen on port PORT. Ports can be selected in a range between 2-65535. Ports between
352
- 2 and 1023 can only be chosen when `machinery` will be executed as `root` user.",
353
- arg_name: "PORT"
354
- c.flag [:ip, :i], type: String, required: false, default_value: "127.0.0.1",
355
- desc: "Listen on ip address IP. It's only possible to use an IP address (or hostnames
356
- resolving to an IP address) which is assigned to a network interface on the local
357
- machine.", arg_name: "IP"
358
351
  c.switch "html", required: false, negatable: false,
359
352
  desc: "Open comparison in HTML format in your web browser."
360
353
  end
@@ -367,7 +360,14 @@ class Cli
367
360
  name1 = shift_arg(args, "NAME1")
368
361
  name2 = shift_arg(args, "NAME2")
369
362
 
370
- check_port_validity(options[:port]) if options[:html]
363
+ if options[:html]
364
+ begin
365
+ check_port_validity(@config.http_server_port)
366
+ rescue Machinery::Errors::ServerPortError => e
367
+ raise Machinery::Errors::InvalidCommandLine.new(e.message + " The port can be " \
368
+ "specified in the 'http_server_port' section of the configuration file.")
369
+ end
370
+ end
371
371
 
372
372
  store = system_description_store
373
373
  description1 = SystemDescription.load(name1, store)
@@ -378,8 +378,8 @@ class Cli
378
378
  opts = {
379
379
  show_html: options["html"],
380
380
  show_all: options["show-all"],
381
- ip: options["ip"],
382
- port: options["port"]
381
+ ip: "127.0.0.1",
382
+ port: @config.http_server_port
383
383
  }
384
384
  task.compare(description1, description2, scope_list, opts)
385
385
  end
@@ -520,9 +520,6 @@ class Cli
520
520
  c.flag "skip-files", required: false, negatable: false,
521
521
  desc: "Do not consider given files or directories during inspection. " \
522
522
  "Either provide one file or directory name or a list of names separated by commas."
523
- c.flag ["remote-user", :r], type: String, required: false, default_value: @config.remote_user,
524
- desc: "Defines the user which is used to access the inspected system via SSH."\
525
- "This user needs sudo access on the remote machine or be root.", arg_name: "USER"
526
523
  c.switch ["extract-files", :x], required: false, negatable: false,
527
524
  desc: "Extract changed configuration files and unmanaged files from inspected system"
528
525
  c.switch "extract-changed-config-files", required: false, negatable: false,
@@ -589,6 +586,9 @@ class Cli
589
586
  command "inspect" do |c|
590
587
  supports_filtering(c)
591
588
  define_inspect_command_options(c)
589
+ c.flag ["remote-user", :r], type: String, required: false, default_value: @config.remote_user,
590
+ desc: "Defines the user which is used to access the inspected system via SSH."\
591
+ "This user needs sudo access on the remote machine or be root.", arg_name: "USER"
592
592
 
593
593
  c.action do |_global_options, options, args|
594
594
  host = shift_arg(args, "HOSTNAME")
@@ -618,21 +618,20 @@ class Cli
618
618
  desc "Inspect container image"
619
619
  long_desc <<-LONGDESC
620
620
  Inspect container image and generate system descripton from inspected data.
621
+ Right now we only support docker images.
621
622
 
622
623
  Multiple scopes can be passed as comma-separated list. If no specific scopes
623
624
  are given, all scopes are inspected.
624
625
 
625
626
  Available scopes: #{AVAILABLE_SCOPE_LIST}
626
627
  LONGDESC
627
- arg "IMAGENAME"
628
+ arg "(IMAGENAME|IMAGEID)"
628
629
  command "inspect-container" do |c|
629
630
  supports_filtering(c)
630
631
  define_inspect_command_options(c)
631
- c.switch ["docker", :d], required: true, negatable: false,
632
- desc: "Inspect a docker container"
633
632
 
634
633
  c.action do |_global_options, options, args|
635
- image = shift_arg(args, "IMAGENAME")
634
+ image = shift_arg(args, "(IMAGENAME|IMAGEID)")
636
635
  system = DockerSystem.new(image)
637
636
  inspector_task = InspectTask.new
638
637
 
@@ -741,14 +740,6 @@ class Cli
741
740
  desc: "Show specified scopes", arg_name: "SCOPE_LIST"
742
741
  c.flag ["exclude-scope", :e], type: String, required: false,
743
742
  desc: "Exclude specified scopes", arg_name: "SCOPE_LIST"
744
- c.flag [:port, :p], type: Integer, required: false, default_value: @config.http_server_port,
745
- desc: "Listen on port PORT. Ports can be selected in a range between 2-65535. Ports between
746
- 2 and 1023 can only be chosen when `machinery` will be executed as `root` user.",
747
- arg_name: "PORT"
748
- c.flag [:ip, :i], type: String, required: false, default_value: "127.0.0.1",
749
- desc: "Listen on ip address IP. It's only possible to use an IP address (or hostnames
750
- resolving to an IP address) which is assigned to a network interface on the local
751
- machine.", arg_name: "IP"
752
743
  c.switch "pager", required: false, default_value: true,
753
744
  desc: "Pipe output into a pager"
754
745
  c.switch "show-diffs", required: false, negatable: false,
@@ -761,13 +752,20 @@ class Cli
761
752
  c.action do |global_options,options,args|
762
753
  Machinery::Ui.use_pager = options["pager"]
763
754
 
755
+ if options[:html]
756
+ begin
757
+ check_port_validity(@config.http_server_port)
758
+ rescue Machinery::Errors::ServerPortError => e
759
+ raise Machinery::Errors::InvalidCommandLine.new(e.message + " The port can be " \
760
+ "specified in the 'http_server_port' section of the configuration file.")
761
+ end
762
+ end
763
+
764
764
  name = shift_arg(args, "NAME")
765
765
  if name == "localhost" && !CurrentUser.new.is_root?
766
766
  Machinery::Ui.puts "You need root rights to access the system description of your locally inspected system."
767
767
  end
768
768
 
769
- check_port_validity(options[:port]) if options[:html]
770
-
771
769
  description = SystemDescription.load(name, system_description_store)
772
770
  scope_list = process_scope_option(options[:scope], options["exclude-scope"])
773
771
 
@@ -791,8 +789,8 @@ class Cli
791
789
  opts = {
792
790
  show_diffs: options["show-diffs"],
793
791
  show_html: options["html"],
794
- ip: options["ip"],
795
- port: options["port"]
792
+ ip: "127.0.0.1",
793
+ port: @config.http_server_port
796
794
  }
797
795
  task.show(description, scope_list, filter, opts)
798
796
  end
@@ -887,19 +885,29 @@ class Cli
887
885
  desc: "Listen on port PORT. Ports can be selected in a range between 2-65535. Ports between
888
886
  2 and 1023 can only be chosen when `machinery` will be executed as `root` user.",
889
887
  arg_name: "PORT"
890
- c.flag [:ip, :i], type: String, required: false, default_value: "127.0.0.1",
891
- desc: "Listen on ip address IP. It's only possible to use an IP address (or hostnames
892
- resolving to an IP address) which is assigned to a network interface on the local
893
- machine.", arg_name: "IP"
888
+ c.switch "public", required: false, negatable: false,
889
+ desc: "Makes the server reachable from all IP addresses."
894
890
 
895
891
  c.action do |_global_options, options, args|
896
892
  name = shift_arg(args, "NAME")
897
893
 
898
- check_port_validity(options[:port])
894
+ begin
895
+ check_port_validity(options[:port])
896
+ rescue Machinery::Errors::ServerPortError => e
897
+ raise Machinery::Errors::InvalidCommandLine.new(e.message + " The port can be " \
898
+ "either specified in the 'http_server_port' section of the configuration file " \
899
+ "or via the --port option.")
900
+ end
901
+
902
+ if options[:public]
903
+ ip = "0.0.0.0"
904
+ else
905
+ ip = "127.0.0.1"
906
+ end
899
907
 
900
908
  description = SystemDescription.load(name, system_description_store)
901
909
  task = ServeHtmlTask.new
902
- task.serve(description, options[:ip], options[:port])
910
+ task.serve(description, ip, options[:port])
903
911
  end
904
912
  end
905
913
 
@@ -140,5 +140,12 @@ module Machinery
140
140
  class UnexpectedInputData < MachineryError; end
141
141
  class ComposeServiceLink < MachineryError; end
142
142
  class UnsupportedHelperVersion < MachineryError; end
143
+ class ServerPortError < MachineryError
144
+ attr_reader :message
145
+
146
+ def initialize(message)
147
+ @message = message
148
+ end
149
+ end
143
150
  end
144
151
  end
@@ -20,6 +20,27 @@ module Machinery
20
20
  (string =~ /^\d+$/) != nil
21
21
  end
22
22
 
23
+ def self.content_is_binary?(content)
24
+ # Code by http://www.thecodingforums.com/threads/test-if-file-is-binary.843447/#post-4572282
25
+ # Modified by SUSE Linux GmbH
26
+ ascii = 0
27
+ control = 0
28
+ binary = 0
29
+
30
+ content.each_byte do |byte|
31
+ case byte
32
+ when 0...32
33
+ control += 1
34
+ when 32...128
35
+ ascii += 1
36
+ else
37
+ binary += 1
38
+ end
39
+ end
40
+
41
+ control.to_f / ascii > 0.1 || binary.to_f / ascii > 0.05
42
+ end
43
+
23
44
  # Implementation of String#scrub for Ruby < 2.1. Assumes the string is in
24
45
  # UTF-8.
25
46
  def self.scrub(s)
@@ -67,7 +67,7 @@ class Hint
67
67
  "#{program_name} inspect #{options[:host]} --name #{options[:name]} --extract-files"
68
68
  elsif options[:docker_container]
69
69
  "To do a full inspection containing all scopes and to extract files run:\n" \
70
- "#{program_name} inspect-container --docker #{options[:docker_container]} " \
70
+ "#{program_name} inspect-container #{options[:docker_container]}" \
71
71
  "--name #{options[:name]} --extract-files"
72
72
  end
73
73
  end
@@ -26,11 +26,19 @@ class Html
26
26
  Server.set :public_folder, File.join(Machinery::ROOT, "html")
27
27
 
28
28
  if opts[:ip] != "localhost" && opts[:ip] != "127.0.0.1"
29
- Machinery::Ui.puts <<EOF
29
+ if opts[:ip] == "0.0.0.0"
30
+ Machinery::Ui.puts <<EOF
31
+ Warning:
32
+ The server is listening on all configured IP addresses.
33
+ This could lead to confidential data like passwords or private keys being readable by others.
34
+ EOF
35
+ else
36
+ Machinery::Ui.puts <<EOF
30
37
  Warning:
31
38
  You specified an IP address other than '127.0.0.1', your server may be reachable from the network.
32
39
  This could lead to confidential data like passwords or private keys being readable by others.
33
40
  EOF
41
+ end
34
42
  end
35
43
 
36
44
  begin