pennyworth-tool 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.hound.yml +3 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +18 -0
  6. data/CONTRIBUTING.md +67 -0
  7. data/COPYING +674 -0
  8. data/Gemfile +28 -0
  9. data/README.md +339 -0
  10. data/Rakefile +33 -0
  11. data/bin/pennyworth +26 -0
  12. data/config/setup.yml +17 -0
  13. data/examples/README.md +23 -0
  14. data/examples/kiwi/definitions/base_opensuse13.1_kvm/config.sh +87 -0
  15. data/examples/kiwi/definitions/base_opensuse13.1_kvm/config.xml +64 -0
  16. data/examples/kiwi/definitions/base_opensuse13.1_kvm/root/etc/sysconfig/network/ifcfg-eth0 +2 -0
  17. data/examples/kiwi/definitions/base_opensuse13.1_kvm/root/home/vagrant/.ssh/authorized_keys +1 -0
  18. data/examples/vagrant/Vagrantfile +14 -0
  19. data/files/99-libvirt.rules +2 -0
  20. data/files/image_test-template.xml +43 -0
  21. data/files/pool-default.xml +6 -0
  22. data/lib/image_runner.rb +89 -0
  23. data/lib/pennyworth.rb +65 -0
  24. data/lib/pennyworth/cli.rb +339 -0
  25. data/lib/pennyworth/cli_host_controller.rb +107 -0
  26. data/lib/pennyworth/commands/base_command.rb +96 -0
  27. data/lib/pennyworth/commands/boot_command.rb +29 -0
  28. data/lib/pennyworth/commands/build_base_command.rb +103 -0
  29. data/lib/pennyworth/commands/command.rb +43 -0
  30. data/lib/pennyworth/commands/down_command.rb +25 -0
  31. data/lib/pennyworth/commands/import_base_command.rb +112 -0
  32. data/lib/pennyworth/commands/import_ssh_keys_command.rb +27 -0
  33. data/lib/pennyworth/commands/list_command.rb +41 -0
  34. data/lib/pennyworth/commands/setup_command.rb +209 -0
  35. data/lib/pennyworth/commands/shutdown_command.rb +28 -0
  36. data/lib/pennyworth/commands/status_command.rb +26 -0
  37. data/lib/pennyworth/commands/up_command.rb +27 -0
  38. data/lib/pennyworth/exceptions.rb +39 -0
  39. data/lib/pennyworth/helper.rb +39 -0
  40. data/lib/pennyworth/host_config.rb +86 -0
  41. data/lib/pennyworth/host_runner.rb +133 -0
  42. data/lib/pennyworth/image_runner.rb +89 -0
  43. data/lib/pennyworth/libvirt.rb +93 -0
  44. data/lib/pennyworth/local_command_runner.rb +77 -0
  45. data/lib/pennyworth/local_runner.rb +34 -0
  46. data/lib/pennyworth/lock_service.rb +87 -0
  47. data/lib/pennyworth/remote_command_runner.rb +144 -0
  48. data/lib/pennyworth/runner.rb +27 -0
  49. data/lib/pennyworth/settings.rb +42 -0
  50. data/lib/pennyworth/spec.rb +96 -0
  51. data/lib/pennyworth/spec_profiler.rb +85 -0
  52. data/lib/pennyworth/ssh_keys_importer.rb +107 -0
  53. data/lib/pennyworth/urls.rb +28 -0
  54. data/lib/pennyworth/vagrant.rb +81 -0
  55. data/lib/pennyworth/vagrant_command.rb +120 -0
  56. data/lib/pennyworth/vagrant_runner.rb +44 -0
  57. data/lib/pennyworth/version.rb +22 -0
  58. data/lib/pennyworth/vm.rb +62 -0
  59. data/man/.gitignore +2 -0
  60. data/man/pennyworth.1.md +28 -0
  61. data/pennyworth.gemspec +57 -0
  62. data/prophet/Gemfile +3 -0
  63. data/prophet/prophet.rb +82 -0
  64. data/spec/base_command_spec.rb +30 -0
  65. data/spec/build_base_command_spec.rb +147 -0
  66. data/spec/cli_host_controller_spec.rb +113 -0
  67. data/spec/data/hosts.yaml +10 -0
  68. data/spec/data/kiwi/base_opensuse12.3_kvm.box +1 -0
  69. data/spec/data/kiwi/base_opensuse13.1_kvm.box +1 -0
  70. data/spec/data/kiwi/definitions/base_opensuse12.3_kvm/config.sh +1 -0
  71. data/spec/data/kiwi/definitions/base_opensuse12.3_kvm/config.xml +1 -0
  72. data/spec/data/kiwi/definitions/base_opensuse12.3_kvm/root/home/vagrant/.ssh/authorized_keys +1 -0
  73. data/spec/data/kiwi/definitions/base_opensuse13.1_kvm/config.sh +1 -0
  74. data/spec/data/kiwi/definitions/base_opensuse13.1_kvm/config.xml +1 -0
  75. data/spec/data/kiwi/definitions/base_opensuse13.1_kvm/root/home/vagrant/.ssh/authorized_keys +1 -0
  76. data/spec/data/kiwi2/box_state.yaml +14 -0
  77. data/spec/data/kiwi2/definitions/base_opensuse12.3_kvm/config.sh +1 -0
  78. data/spec/data/kiwi2/definitions/base_opensuse12.3_kvm/config.xml +1 -0
  79. data/spec/data/kiwi2/definitions/base_opensuse12.3_kvm/root/home/vagrant/.ssh/authorized_keys +1 -0
  80. data/spec/data/kiwi2/definitions/base_opensuse13.1_kvm/config.sh +1 -0
  81. data/spec/data/kiwi2/definitions/base_opensuse13.1_kvm/config.xml +1 -0
  82. data/spec/data/kiwi2/definitions/base_opensuse13.1_kvm/root/home/vagrant/.ssh/authorized_keys +1 -0
  83. data/spec/data/kiwi3/box_state.yaml +13 -0
  84. data/spec/data/kiwi3/definitions/base_opensuse12.3_kvm/.gitkeep +0 -0
  85. data/spec/data/kiwi3/definitions/base_opensuse13.1_kvm/.gitkeep +0 -0
  86. data/spec/data/kiwi3/import_state.yaml +3 -0
  87. data/spec/data/kiwi4/definitions/base_opensuse12.3_kvm/.gitkeep +0 -0
  88. data/spec/data/kiwi4/definitions/base_opensuse13.1_kvm/.gitkeep +0 -0
  89. data/spec/data/kiwi4/import_state.yaml +3 -0
  90. data/spec/data/kiwi5/import_state.yaml +3 -0
  91. data/spec/data/vagrant/.gitkeep +0 -0
  92. data/spec/host_config_spec.rb +197 -0
  93. data/spec/host_runner_spec.rb +112 -0
  94. data/spec/image_runner_spec.rb +62 -0
  95. data/spec/import_base_command_spec.rb +189 -0
  96. data/spec/local_command_runner_spec.rb +117 -0
  97. data/spec/local_runner_spec.rb +42 -0
  98. data/spec/lock_service_spec.rb +95 -0
  99. data/spec/remote_command_runner_spec.rb +115 -0
  100. data/spec/settings_spec.rb +26 -0
  101. data/spec/setup_command_spec.rb +49 -0
  102. data/spec/spec_helper.rb +50 -0
  103. data/spec/spec_profiler_spec.rb +63 -0
  104. data/spec/spec_spec.rb +99 -0
  105. data/spec/support/command_runner_examples.rb +29 -0
  106. data/spec/support/runner_examples.rb +34 -0
  107. data/spec/urls_spec.rb +46 -0
  108. data/spec/vagrant_command_spec.rb +51 -0
  109. data/spec/vagrant_runner_spec.rb +40 -0
  110. data/spec/vagrant_spec.rb +288 -0
  111. data/spec/vm_spec.rb +56 -0
  112. metadata +257 -0
@@ -0,0 +1,34 @@
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 Pennyworth
19
+ class LocalRunner < Runner
20
+ def initialize(opts = {})
21
+ @command_runner = LocalCommandRunner.new(opts)
22
+ end
23
+
24
+ def start
25
+ # Nothing to do here
26
+
27
+ "127.0.0.1"
28
+ end
29
+
30
+ def stop
31
+ # Nothing to do here either
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,87 @@
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
+ module Pennyworth
19
+ class LockService
20
+ attr_reader :lock_server_host
21
+ attr_reader :lock_server_port
22
+
23
+ def initialize(lock_server_address)
24
+ fields = lock_server_address.split(":")
25
+ @lock_server_host = fields[0]
26
+ @lock_server_port = fields[1]
27
+ @sockets = {}
28
+ end
29
+
30
+ def socket(lock_name)
31
+ if !@sockets.has_key?(lock_name)
32
+ @sockets[lock_name] = TCPSocket.new(@lock_server_host, @lock_server_port)
33
+ end
34
+ @sockets[lock_name]
35
+ end
36
+
37
+ def request_lock(lock_name)
38
+ socket(lock_name).puts("g #{lock_name}")
39
+ response = socket(lock_name).gets
40
+ if response =~ /^1/
41
+ return true
42
+ elsif response =~ /^0/
43
+ return false
44
+ else
45
+ raise LockError.new("Error, received: #{response}")
46
+ end
47
+ end
48
+
49
+ def keep_lock
50
+ # Sleep forever to keep process running to keep TCP connection to lock
51
+ # server open. When the process is ended the connection is closed and the
52
+ # lock is released. Users can end the process and release the lock with
53
+ # Ctrl-C.
54
+ sleep
55
+ end
56
+
57
+ def release_lock(lock_name)
58
+ if !@sockets[lock_name]
59
+ raise LockError.new("Lock '#{lock_name}' doesn't exist")
60
+ end
61
+ @sockets[lock_name].close
62
+ @sockets.delete(lock_name)
63
+ end
64
+
65
+ def locked?(lock_name)
66
+ socket(lock_name).puts("i #{lock_name}")
67
+ response = socket(lock_name).gets
68
+ if response =~ /^1/
69
+ return true
70
+ else
71
+ return false
72
+ end
73
+ end
74
+
75
+ def info(lock_name)
76
+ if locked?(lock_name)
77
+ socket(lock_name).puts("d #{lock_name}")
78
+ response = socket(lock_name).gets
79
+ response =~ /^#{lock_name}: (.*):/
80
+ client = $1
81
+ return "'#{lock_name}' is locked by #{client}"
82
+ else
83
+ return "'#{lock_name}' is not locked"
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,144 @@
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
+ # The purpose of this class is to execute commands on a remote machine via SSH.
19
+ module Pennyworth
20
+ class RemoteCommandRunner
21
+ def initialize(ip, username)
22
+ @ip = ip
23
+ @username = username
24
+ end
25
+
26
+ def run(*args)
27
+ # When ssh executes commands, it passes them through shell expansion.
28
+ # For example, compare
29
+ #
30
+ # $ echo '$HOME'
31
+ # $HOME
32
+ #
33
+ # with
34
+ #
35
+ # $ ssh localhost echo '$HOME'
36
+ # /home/dmajda
37
+ #
38
+ # To mitigate that and maintain usual Cheetah semantics, we need to
39
+ # protect the command and its arguments using another layer of escaping.
40
+ options = args.last.is_a?(Hash) ? args.pop : {}
41
+ args.map! { |a| Shellwords.escape(a) } if !options[:skip_escape]
42
+
43
+ if user = options.delete(:as)
44
+ args = ["su", "-l", user, "-c"] + args
45
+ end
46
+
47
+ Cheetah.run(
48
+ "ssh",
49
+ "-o",
50
+ "UserKnownHostsFile=/dev/null",
51
+ "-o",
52
+ "StrictHostKeyChecking=no",
53
+ "#{@username}@#{@ip}",
54
+ "LC_ALL=C",
55
+ *args,
56
+ options
57
+ )
58
+ rescue Cheetah::ExecutionFailed => e
59
+ raise ExecutionFailed.new(e)
60
+ end
61
+
62
+ # Copy a local file to the remote system.
63
+ #
64
+ # +source+:: Path to the local file
65
+ # +destination+:: Path to the remote file or directory. If +destination+ is a
66
+ # path, the same filename as +source+ will be used.
67
+ # +opts+:: Options to modify the attributes of the remote file.
68
+ #
69
+ # Available options:
70
+ # [owner]:: Owner of the file, e.g. "tux"
71
+ # [group]:: Group of the file, e.g. "users"
72
+ # [mode]:: Mode of the file, e.g. "600"
73
+ def inject_file(source, destination, opts = {})
74
+ # Append filename (taken from +source+) to destination if it is a path, so
75
+ # that +destination+ is always the full target path including the filename.
76
+ destination += File.basename(source) if destination.end_with?("/")
77
+
78
+ Cheetah.run(
79
+ "scp",
80
+ "-o",
81
+ "UserKnownHostsFile=/dev/null",
82
+ "-o",
83
+ "StrictHostKeyChecking=no",
84
+ source,
85
+ "#{@username}@#{@ip}:#{destination}"
86
+ )
87
+
88
+ if opts[:owner] || opts[:group]
89
+ owner_group = opts[:owner] || ""
90
+ owner_group += ":#{opts[:group]}" if opts[:group]
91
+ run "chown", "-R", owner_group, destination
92
+ end
93
+
94
+ if opts[:mode]
95
+ run "chmod", opts[:mode], destination
96
+ end
97
+ rescue Cheetah::ExecutionFailed => e
98
+ raise ExecutionFailed.new(e)
99
+ end
100
+
101
+ def extract_file(source, destination)
102
+ Cheetah.run(
103
+ "scp",
104
+ "-o",
105
+ "UserKnownHostsFile=/dev/null",
106
+ "-o",
107
+ "StrictHostKeyChecking=no",
108
+ "#{@username}@#{@ip}:#{source}",
109
+ destination
110
+ )
111
+ rescue Cheetah::ExecutionFailed => e
112
+ raise ExecutionFailed.new(e)
113
+ end
114
+
115
+ def inject_directory(source, destination, opts = {})
116
+ if opts[:owner] || opts[:group]
117
+ owner_group = opts[:owner] || ""
118
+ owner_group += ":#{opts[:group]}" if opts[:group]
119
+ end
120
+
121
+ chown_cmd = " && chown #{owner_group} '#{destination}'" if owner_group
122
+ mkdir_cmd = "test -d '#{destination}' || (mkdir -p '#{destination}' #{chown_cmd} )"
123
+
124
+ run mkdir_cmd, skip_escape: true
125
+
126
+ Cheetah.run(
127
+ "scp",
128
+ "-r",
129
+ "-o",
130
+ "UserKnownHostsFile=/dev/null",
131
+ "-o",
132
+ "StrictHostKeyChecking=no",
133
+ source,
134
+ "#{@username}@#{@ip}:#{destination}"
135
+ )
136
+
137
+ if owner_group
138
+ run "chown", "-R", owner_group, File.join(destination, File.basename(source))
139
+ end
140
+ rescue Cheetah::ExecutionFailed => e
141
+ raise ExecutionFailed.new(e)
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,27 @@
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 Pennyworth
19
+ # Base class for the runner classes
20
+ class Runner
21
+ attr_reader :command_runner
22
+
23
+ def cleanup_directory(dir)
24
+ command_runner.run("test -d #{dir} && rm -r #{dir}")
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,42 @@
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 Pennyworth
19
+ class Settings
20
+
21
+ attr_accessor :verbose, :silent, :definitions_dir
22
+
23
+ def initialize
24
+ @verbose = false
25
+ @silent = false
26
+ @definitions_dir = File.expand_path("~/.pennyworth")
27
+ end
28
+
29
+ def kiwi_dir
30
+ File.join(@definitions_dir, "/kiwi")
31
+ end
32
+
33
+ def vagrant_dir
34
+ File.join(@definitions_dir, "/vagrant")
35
+ end
36
+
37
+ def version
38
+ Pennyworth::VERSION
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,96 @@
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
+ require "cheetah"
19
+ require "libvirt"
20
+ require "socket"
21
+ require "open-uri"
22
+ require "yaml"
23
+
24
+ require_relative "exceptions"
25
+ require_relative "helper"
26
+ require_relative "libvirt"
27
+ require_relative "runner"
28
+ require_relative "vagrant"
29
+ require_relative "vagrant_runner"
30
+ require_relative "image_runner"
31
+ require_relative "host_config"
32
+ require_relative "host_runner"
33
+ require_relative "lock_service"
34
+ require_relative "ssh_keys_importer"
35
+ require_relative "vm"
36
+ require_relative "spec_profiler"
37
+ require_relative "remote_command_runner"
38
+ require_relative "settings"
39
+ require_relative "local_runner"
40
+ require_relative "local_command_runner"
41
+
42
+ module Pennyworth
43
+ module SpecHelper
44
+ def start_system(opts)
45
+ opts = {
46
+ skip_ssh_setup: false
47
+ }.merge(opts)
48
+ username = opts[:username] || "root"
49
+ if opts[:box]
50
+ runner = VagrantRunner.new(opts[:box], RSpec.configuration.vagrant_dir, username)
51
+ password = opts[:password] || "vagrant"
52
+ elsif opts[:image]
53
+ runner = ImageRunner.new(opts[:image], username)
54
+ password = opts[:password] || "linux"
55
+ elsif opts[:host]
56
+ config = HostConfig.new(RSpec.configuration.hosts_file)
57
+ config.read
58
+ runner = HostRunner.new(opts[:host], config, username)
59
+ elsif opts[:local]
60
+ runner = LocalRunner.new(env: opts[:env])
61
+ end
62
+
63
+ raise "No image specified." unless runner
64
+
65
+ system = VM.new(runner)
66
+
67
+ # Make sure to stop the machine again when the example group is done
68
+ self.class.after(:all) do
69
+ system.stop
70
+ end
71
+
72
+ measure("Boot machine '#{opts[:box] || opts[:image] || opts[:host]}'") do
73
+ system.start
74
+ end
75
+ if !opts[:skip_ssh_setup] && !opts[:host] && !opts[:local]
76
+ SshKeysImporter.import(system.ip, username, password)
77
+ end
78
+
79
+ system
80
+ end
81
+ end
82
+ end
83
+
84
+ RSpec.configure do |config|
85
+ defaults = Pennyworth::Settings.new
86
+ config.include(Pennyworth::SpecHelper)
87
+ config.add_setting :pennyworth_mode, default: false
88
+ config.add_setting :vagrant_dir, default: defaults.vagrant_dir
89
+ config.add_setting :hosts_file, default: File.join(defaults.definitions_dir, "/hosts.yaml")
90
+
91
+ config.before(:all) do
92
+ unless RSpec.configuration.pennyworth_mode
93
+ Pennyworth::Libvirt.ensure_libvirt_env_started
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,85 @@
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
+ require "benchmark"
19
+
20
+ def measure(label, &block)
21
+ own_parent = @parent_measurement
22
+ measurement = {
23
+ label: label,
24
+ child_measurements: []
25
+ }
26
+
27
+ @parent_measurement = measurement
28
+ time = Benchmark.measure do
29
+ block.call
30
+ end
31
+ @parent_measurement = own_parent
32
+ measurement.merge!(time: time.real)
33
+
34
+ # Calculate time that was not spent in the measured child blocks but somewhere
35
+ # else
36
+ if !measurement[:child_measurements].empty?
37
+ other_time = measurement[:time]
38
+ measurement[:child_measurements].each do |child|
39
+ other_time -= child[:time]
40
+ end
41
+ measurement[:child_measurements] << { label: "Other", time: other_time }
42
+ end
43
+
44
+ if own_parent
45
+ own_parent[:child_measurements] << measurement
46
+ else
47
+ @measurements << measurement
48
+ end
49
+ end
50
+
51
+ def print_measurement(measurement, indent = 0)
52
+ name = measurement[:label][0..65-indent].ljust(70-indent)
53
+ STDERR.puts (" " * indent) + "#{name}: #{measurement[:time]}"
54
+
55
+ if measurement[:child_measurements]
56
+ measurement[:child_measurements].each do |child|
57
+ print_measurement(child, indent + 2)
58
+ end
59
+ end
60
+ end
61
+
62
+ RSpec.configure do |config|
63
+ config.before(:all) do
64
+ @measurements = []
65
+ end
66
+
67
+ config.around(:each) do |example|
68
+ if RSpec.configuration.pennyworth_mode
69
+ example.run
70
+ else
71
+ measure(example.metadata[:full_description]) do
72
+ example.run
73
+ end
74
+ end
75
+ end
76
+
77
+ config.after(:all) do
78
+ if !RSpec.configuration.pennyworth_mode
79
+ @measurements.each do |measurement|
80
+ print_measurement(measurement)
81
+ end
82
+ end
83
+ end
84
+ end
85
+