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,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