pennyworth-tool 0.0.1

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 (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,107 @@
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 "tempfile"
19
+
20
+ module Pennyworth
21
+ class SshKeysImporter
22
+ def self.import(ip, username, password, identity_file = nil)
23
+ tmp_file = Tempfile.new("pennyworth-ssh-key-importer")
24
+
25
+ cmd = "ssh-copy-id"
26
+ if identity_file
27
+ cmd << " -i \"#{identity_file}\""
28
+ cmd << " -o \"IdentityFile=#{identity_file}\""
29
+ end
30
+ cmd << " -o \"UserKnownHostsFile=/dev/null\""
31
+ cmd << " -o \"StrictHostKeyChecking=no\""
32
+ cmd << " #{username}@#{ip}"
33
+
34
+ # This temporary bash script copies the SSH keys to the host using
35
+ # ssh-copy-id.
36
+ #
37
+ # In order to drive this interactive command it is executed by expect.
38
+ tmp_file.write(<<-EOF
39
+ #!/usr/bin/expect -f
40
+
41
+ spawn #{cmd}
42
+ expect {
43
+ -re ".*assword.*" {
44
+ send "#{password}\\r"
45
+ expect {
46
+ -re ".*assword.*" {
47
+ exit 1
48
+ }
49
+ -re ".*Number of key.*" {
50
+ exit 0
51
+ }
52
+ }
53
+ }
54
+ -re ".*Number of key.*" {
55
+ exit 0
56
+ }
57
+ "skipped" {
58
+ exit 2
59
+ }
60
+ "authenticity" {
61
+ send "yes\\r"
62
+ exp_continue
63
+ }
64
+ "Connection refused" {
65
+ exit 3
66
+ }
67
+ }
68
+ exit 4
69
+ EOF
70
+ )
71
+ tmp_file.close
72
+
73
+ attempt = 1
74
+ begin
75
+ Cheetah.run("expect", tmp_file.path)
76
+ rescue Cheetah::ExecutionFailed => e
77
+ case e.status.exitstatus
78
+ when 1
79
+ raise WrongPasswordException, "Error: Could not upload SSH keys because" \
80
+ " the password is wrong."
81
+ when 2
82
+ raise SshKeysAlreadyExistsException, "SSH keys were not uploaded because they" \
83
+ " were already on the host."
84
+ when 3
85
+ # We give sshd 30 seconds to start up
86
+ if attempt > 30
87
+ raise SshConnectionFailed, "SSH connection failed."
88
+ else
89
+ attempt += 1
90
+ sleep 1
91
+ retry
92
+ end
93
+ when 4
94
+ raise RuntimeError, "An unknown error occured while trying to upload " \
95
+ "the SSH keys."
96
+ when 127
97
+ raise CommandNotFoundError, "Error: Please install expect before running" \
98
+ " pennyworth copy-ssh-keys."
99
+ else
100
+ raise e
101
+ end
102
+ end
103
+ ensure
104
+ tmp_file.unlink
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,28 @@
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
+ # URL manipulation utilities.
20
+ module URLs
21
+ def self.join(*parts)
22
+ parts = parts.reject(&:empty?)
23
+ parts = [parts.first] + parts[1..-1].map { |p| p.sub(/^\//, "") }
24
+ parts = parts[0..-2].map { |p| p.sub(/\/$/, "") } + [parts.last]
25
+ parts.join("/")
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,81 @@
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 Vagrant
20
+ def initialize(vagrant_dir)
21
+ @vagrant_dir = vagrant_dir
22
+ end
23
+
24
+ def run(*args)
25
+ # Many vagrant commands don't have a --provider option, but they fail
26
+ # because they can't find VirtualBox. The following code is a crude
27
+ # workaround.
28
+ Dir.chdir(@vagrant_dir) do
29
+ with_env "VAGRANT_DEFAULT_PROVIDER" => "libvirt" do
30
+ Cheetah.run "vagrant", *args
31
+ end
32
+ end
33
+ end
34
+
35
+ def ssh_config(vm_name)
36
+ output = Dir.chdir(@vagrant_dir) do
37
+ if vm_name
38
+ run "ssh-config", vm_name, :stdout => :capture
39
+ else
40
+ config = ""
41
+ status = run "status", :stdout => :capture
42
+ status.scan(/\n(.*?)\s*running/).each do |vm_name|
43
+ config += run "ssh-config", vm_name.first, :stdout => :capture
44
+ end
45
+ config
46
+ end
47
+ end
48
+
49
+ parse_ssh_config_output(output)
50
+ end
51
+
52
+ private
53
+
54
+ def parse_ssh_config_output(output)
55
+ # See http://linux.die.net/man/5/ssh_config for description of the format.
56
+
57
+ config = {}
58
+ host = nil
59
+
60
+ output.each_line do |line|
61
+ line.chomp!("\n")
62
+
63
+ next if line.empty? || line.start_with?("#")
64
+
65
+ m = /^\s*(?<key>\w+)(\s*=\s*|\s+)(?<value>.*)$/.match(line)
66
+ raise "Invalid line in SSH config: #{line.inspect}." unless m
67
+
68
+ if m[:key] == "Host"
69
+ host = m[:value]
70
+ config[host] = {}
71
+ else
72
+ raise "Missing Host keyword before #{m[:key]}." unless host
73
+
74
+ config[host][m[:key]] = m[:value]
75
+ end
76
+ end
77
+
78
+ config
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,120 @@
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 VagrantCommand < Command
20
+ attr_reader :vagrant
21
+
22
+ def initialize
23
+ @vagrant = Pennyworth::Vagrant.new(Pennyworth::Cli.settings.vagrant_dir)
24
+ end
25
+
26
+ def self.parse_status output
27
+ result = []
28
+ parsing_vms = false
29
+ output.each_line do |line|
30
+ line.strip!
31
+ if line.empty?
32
+ break if parsing_vms
33
+ parsing_vms = true
34
+ elsif parsing_vms
35
+ line =~ /^(.*?)\s+(.*)$/
36
+ vm_name = $1
37
+ vm_state = $2
38
+ if vm_state =~ /running/
39
+ vm_name += " (running)"
40
+ end
41
+ result.push vm_name
42
+ end
43
+ end
44
+ result
45
+ end
46
+
47
+ def self.setup_environment(dir)
48
+ if !File.exists?(File.join(dir, "Vagrantfile"))
49
+ vagrant = Pennyworth::Vagrant.new(dir)
50
+ FileUtils.mkdir_p(dir)
51
+ vagrant.run("init")
52
+ end
53
+ end
54
+
55
+ def up(vm_name, destroy)
56
+ Pennyworth::Libvirt.ensure_libvirt_env_started
57
+
58
+ if vm_name
59
+ log "Starting VM #{vm_name}..."
60
+ @vagrant.run "destroy", vm_name if destroy
61
+ @vagrant.run "up", vm_name
62
+ else
63
+ log "Starting test environment..."
64
+ @vagrant.run "destroy" if destroy
65
+ @vagrant.run "up"
66
+ end
67
+ end
68
+
69
+ def down(vm_name)
70
+ if vm_name
71
+ log "Stopping VM #{vm_name}..."
72
+ @vagrant.run "halt", vm_name
73
+ else
74
+ log "Stopping test environment..."
75
+ @vagrant.run "halt"
76
+ end
77
+ end
78
+
79
+ def env_clean?
80
+ status = @vagrant.run "status", :stdout => :capture
81
+
82
+ return status.index("running").nil?
83
+ end
84
+
85
+ def reset(vm_name)
86
+ if vm_name
87
+ log "Resetting VM #{vm_name}..."
88
+ @vagrant.run "destroy", vm_name
89
+ else
90
+ log "Resetting test environment..."
91
+ @vagrant.run "destroy"
92
+ end
93
+ end
94
+
95
+ def ssh(vm_name, cmd, stdout = nil)
96
+ if vm_name
97
+ @vagrant.run "ssh", vm_name, "-c", cmd, :stdout => stdout
98
+ else
99
+ @vagrant.run "ssh", "-c", cmd, :stdout => stdout
100
+ end
101
+ end
102
+
103
+ def list
104
+ boxes = @vagrant.run "box", "list", :stdout => :capture
105
+ boxes.split("\n")
106
+ end
107
+
108
+ def status
109
+ Pennyworth::VagrantCommand.parse_status( @vagrant.run "status", :stdout => :capture )
110
+ end
111
+
112
+ def destroy
113
+ @vagrant.run "destroy"
114
+ end
115
+
116
+ def add_box box, box_path
117
+ @vagrant.run "box", "add", box, box_path, "--force"
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,44 @@
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 VagrantRunner < Runner
20
+ def initialize(box, vagrant_dir, username)
21
+ @box = box
22
+ @username = username
23
+ @vagrant = Vagrant.new(vagrant_dir)
24
+ end
25
+
26
+ def start
27
+ @vagrant.run "destroy", @box
28
+ @vagrant.run "up", @box
29
+
30
+ ip = @vagrant.ssh_config(@box)[@box]["HostName"]
31
+ @command_runner = RemoteCommandRunner.new(ip, @username)
32
+
33
+ ip
34
+ end
35
+
36
+ def stop
37
+ @vagrant.run "halt", @box
38
+ end
39
+
40
+ def cleanup_directory(_dir)
41
+ # The machine will be reset anyway after the tests, so this is is a NOP
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,22 @@
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
+
20
+ VERSION = "0.0.1"
21
+
22
+ end
@@ -0,0 +1,62 @@
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
+ # Represents a virtual machine that can be started, stopped, and interacted
20
+ # with.
21
+ class VM
22
+ attr_accessor :ip, :runner
23
+
24
+ def initialize(runner)
25
+ @runner = runner
26
+ end
27
+
28
+ def start
29
+ @ip = @runner.start
30
+ end
31
+
32
+ def stop
33
+ @runner.stop
34
+ end
35
+
36
+ def run_command(*args)
37
+ command_runner.run(*args)
38
+ end
39
+
40
+ def inject_file(source, destination, opts = {})
41
+ command_runner.inject_file(source, destination, opts)
42
+ end
43
+
44
+ def extract_file(source, destination)
45
+ command_runner.extract_file(source, destination)
46
+ end
47
+
48
+ def inject_directory(source, destination, opts = {})
49
+ command_runner.inject_directory(source, destination, opts)
50
+ end
51
+
52
+ def cleanup_directory(dir)
53
+ runner.cleanup_directory(dir)
54
+ end
55
+
56
+ private
57
+
58
+ def command_runner
59
+ @runner.command_runner
60
+ end
61
+ end
62
+ end