testlab 0.0.1 → 0.0.2
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.
- data/.coveralls.yml +1 -0
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +9 -0
- data/Gemfile +2 -1
- data/LICENSE +202 -0
- data/README.md +30 -16
- data/Rakefile +75 -1
- data/bin/testlab-console +12 -0
- data/lib/testlab/container.rb +218 -0
- data/lib/testlab/labfile.rb +15 -0
- data/lib/testlab/network.rb +127 -0
- data/lib/testlab/node.rb +148 -0
- data/lib/testlab/provider.rb +27 -0
- data/lib/testlab/providers/aws.rb +20 -0
- data/lib/testlab/providers/local.rb +20 -0
- data/lib/testlab/providers/templates/vagrant/Vagrantfile.erb +25 -0
- data/lib/testlab/providers/vagrant.rb +204 -0
- data/lib/testlab/provisioner.rb +25 -0
- data/lib/testlab/provisioners/chef.rb +21 -0
- data/lib/testlab/provisioners/shell.rb +21 -0
- data/lib/testlab/provisioners/templates/chef/bootstrap.erb +0 -0
- data/lib/testlab/router.rb +71 -0
- data/lib/testlab/version.rb +5 -2
- data/lib/testlab.rb +121 -3
- data/spec/provider_spec.rb +15 -0
- data/spec/provisioner_spec.rb +15 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/Labfile +36 -0
- data/spec/testlab_spec.rb +16 -0
- data/testlab.gemspec +41 -13
- metadata +162 -11
- data/LICENSE.txt +0 -22
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
class TestLab
|
|
2
|
+
|
|
3
|
+
# Network Error Class
|
|
4
|
+
class NetworkError < TestLabError; end
|
|
5
|
+
|
|
6
|
+
# Network Class
|
|
7
|
+
#
|
|
8
|
+
# @author Zachary Patten <zachary@jovelabs.net>
|
|
9
|
+
class Network < ZTK::DSL::Base
|
|
10
|
+
STATUS_KEYS = %w(node_id id state interface).map(&:to_sym)
|
|
11
|
+
|
|
12
|
+
belongs_to :node, :class_name => 'TestLab::Node'
|
|
13
|
+
|
|
14
|
+
attribute :bridge
|
|
15
|
+
|
|
16
|
+
attribute :cidr
|
|
17
|
+
attribute :config
|
|
18
|
+
|
|
19
|
+
def initialize(*args)
|
|
20
|
+
super(*args)
|
|
21
|
+
|
|
22
|
+
@ui = TestLab.ui
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Network status
|
|
26
|
+
def status
|
|
27
|
+
interface = "#{bridge}:#{cidr}"
|
|
28
|
+
{
|
|
29
|
+
:id => self.id,
|
|
30
|
+
:node_id => self.node.id,
|
|
31
|
+
:state => self.state,
|
|
32
|
+
:interface => interface
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
################################################################################
|
|
37
|
+
|
|
38
|
+
# Create the network
|
|
39
|
+
def create
|
|
40
|
+
@ui.logger.debug { "Network Create: #{self.id} " }
|
|
41
|
+
|
|
42
|
+
self.node.ssh.exec(%(sudo brctl addbr #{self.bridge}), :silence => true, :ignore_exit_status => true)
|
|
43
|
+
self.node.ssh.exec(%(sudo ifconfig #{self.bridge} #{self.cidr} down), :silence => true, :ignore_exit_status => true)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Destroy the network
|
|
47
|
+
def destroy
|
|
48
|
+
@ui.logger.debug { "Network Destroy: #{self.id} " }
|
|
49
|
+
|
|
50
|
+
self.node.ssh.exec(%(sudo brctl delbr #{self.bridge}), :silence => true, :ignore_exit_status => true)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Start the network
|
|
54
|
+
def up
|
|
55
|
+
@ui.logger.debug { "Network Up: #{self.id} " }
|
|
56
|
+
|
|
57
|
+
self.node.ssh.exec(%(sudo ifconfig #{self.bridge} up), :silence => true, :ignore_exit_status => true)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Stop the network
|
|
61
|
+
def down
|
|
62
|
+
@ui.logger.debug { "Network Down: #{self.id} " }
|
|
63
|
+
|
|
64
|
+
self.node.ssh.exec(%(sudo ifconfig #{self.bridge} down), :silence => true, :ignore_exit_status => true)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
################################################################################
|
|
68
|
+
|
|
69
|
+
# Reload the network
|
|
70
|
+
def reload
|
|
71
|
+
@ui.logger.debug { "Network Reload: #{self.id} " }
|
|
72
|
+
|
|
73
|
+
self.down
|
|
74
|
+
self.up
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
################################################################################
|
|
78
|
+
|
|
79
|
+
# State of the network
|
|
80
|
+
def state
|
|
81
|
+
output = self.node.ssh.exec(%(sudo ifconfig #{self.bridge} | grep 'MTU'), :silence => true, :ignore_exit_status => true).output.strip
|
|
82
|
+
if ((output =~ /UP/) && (output =~ /RUNNING/))
|
|
83
|
+
:running
|
|
84
|
+
else
|
|
85
|
+
:stopped
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
################################################################################
|
|
90
|
+
|
|
91
|
+
# Network Callback: after_create
|
|
92
|
+
def after_create
|
|
93
|
+
@ui.logger.debug { "Network Callback: After Create: #{self.id} " }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Network Callback: after_up
|
|
97
|
+
def after_up
|
|
98
|
+
@ui.logger.debug { "Network Callback: After Up: #{self.id} " }
|
|
99
|
+
|
|
100
|
+
self.create
|
|
101
|
+
self.up
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Network Callback: before_down
|
|
105
|
+
def before_down
|
|
106
|
+
@ui.logger.debug { "Network Callback: Before Down: #{self.id} " }
|
|
107
|
+
|
|
108
|
+
self.down
|
|
109
|
+
self.destroy
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Network Callback: before_destroy
|
|
113
|
+
def before_destroy
|
|
114
|
+
@ui.logger.debug { "Network Callback: Before Destroy: #{self.id} " }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
################################################################################
|
|
118
|
+
|
|
119
|
+
# Method missing handler
|
|
120
|
+
def method_missing(method_name, *method_args)
|
|
121
|
+
@ui.logger.debug { "NETWORK METHOD MISSING: #{method_name.inspect}(#{method_args.inspect})" }
|
|
122
|
+
super(method_name, *method_args)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
end
|
data/lib/testlab/node.rb
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
require 'lxc'
|
|
2
|
+
|
|
3
|
+
class TestLab
|
|
4
|
+
|
|
5
|
+
# Node Error Class
|
|
6
|
+
class NodeError < TestLabError; end
|
|
7
|
+
|
|
8
|
+
# Node Class
|
|
9
|
+
#
|
|
10
|
+
# @author Zachary Patten <zachary@jovelabs.net>
|
|
11
|
+
class Node < ZTK::DSL::Base
|
|
12
|
+
STATUS_KEYS = %w(id instance_id state user ip port provider con net rtr).map(&:to_sym)
|
|
13
|
+
|
|
14
|
+
belongs_to :labfile, :class_name => 'TestLab::Lab'
|
|
15
|
+
|
|
16
|
+
has_many :routers, :class_name => 'TestLab::Router'
|
|
17
|
+
has_many :containers, :class_name => 'TestLab::Container'
|
|
18
|
+
has_many :networks, :class_name => 'TestLab::Network'
|
|
19
|
+
|
|
20
|
+
attribute :provider
|
|
21
|
+
attribute :config
|
|
22
|
+
|
|
23
|
+
def initialize(*args)
|
|
24
|
+
super(*args)
|
|
25
|
+
|
|
26
|
+
@ui = TestLab.ui
|
|
27
|
+
@provider = self.provider.new(self.config)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def status
|
|
31
|
+
{
|
|
32
|
+
:instance_id => @provider.instance_id,
|
|
33
|
+
:state => @provider.state,
|
|
34
|
+
:user => @provider.user,
|
|
35
|
+
:ip => @provider.ip,
|
|
36
|
+
:port => @provider.port,
|
|
37
|
+
:provider => @provider.class,
|
|
38
|
+
:con => self.containers.count,
|
|
39
|
+
:net => self.networks.count,
|
|
40
|
+
:rtr => self.routers.count
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# SSH to the Node
|
|
45
|
+
def ssh(options={})
|
|
46
|
+
if (!defined?(@ssh) || @ssh.nil?)
|
|
47
|
+
@ssh ||= ZTK::SSH.new({:ui => @ui, :timeout => 1200, :silence => true}.merge(options))
|
|
48
|
+
@ssh.config do |c|
|
|
49
|
+
c.host_name = @provider.ip
|
|
50
|
+
c.user = @provider.user
|
|
51
|
+
c.keys = @provider.identity
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
@ssh
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# SSH to a container running on the Node
|
|
58
|
+
def ssh_container(id, options={})
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Returns the LXC object for this Node
|
|
62
|
+
#
|
|
63
|
+
# This object is used to control containers on the node via it's provider
|
|
64
|
+
def lxc(options={})
|
|
65
|
+
if (!defined?(@lxc) || @lxc.nil?)
|
|
66
|
+
@lxc ||= LXC.new
|
|
67
|
+
@lxc.use_sudo = true
|
|
68
|
+
@lxc.use_ssh = self.ssh
|
|
69
|
+
end
|
|
70
|
+
@lxc
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def arch
|
|
74
|
+
@arch ||= self.ssh.exec(%(uname -m)).output.strip
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
################################################################################
|
|
78
|
+
|
|
79
|
+
# Callback: After Create
|
|
80
|
+
# Ensure our packages are installed.
|
|
81
|
+
def after_create
|
|
82
|
+
self.ssh.exec(%(sudo apt-get -qq -y --force-yes update))
|
|
83
|
+
self.ssh.exec(%(sudo apt-get -qq -y --force-yes install lxc bridge-utils debootstrap yum isc-dhcp-server bind9 ntpdate ntp))
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Callback: After Up
|
|
87
|
+
def after_up
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
################################################################################
|
|
91
|
+
|
|
92
|
+
# Provides a generic interface for triggering our callback framework
|
|
93
|
+
def proxy_callbacks(action, objects, method_name, *method_args)
|
|
94
|
+
callback_method = "#{action}_#{method_name}".to_sym
|
|
95
|
+
|
|
96
|
+
objects.each do |object|
|
|
97
|
+
if object.respond_to?(callback_method)
|
|
98
|
+
object.send(callback_method, *method_args)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Provides a generic interface for triggering our callback framework
|
|
104
|
+
def self_callbacks(action, method_name, *method_args)
|
|
105
|
+
callback_method = "#{action}_#{method_name}".to_sym
|
|
106
|
+
|
|
107
|
+
if self.respond_to?(callback_method)
|
|
108
|
+
self.send(callback_method, *method_args)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Method missing handler
|
|
113
|
+
def method_missing(method_name, *method_args)
|
|
114
|
+
@ui.logger.debug { "NODE METHOD MISSING: #{method_name.inspect}(#{method_args.inspect})" }
|
|
115
|
+
|
|
116
|
+
if TestLab::Provider::PROXY_METHODS.include?(method_name)
|
|
117
|
+
result = nil
|
|
118
|
+
object_collections = [self.containers, self.routers, self.networks]
|
|
119
|
+
|
|
120
|
+
self_callbacks(:before, method_name, *method_args)
|
|
121
|
+
|
|
122
|
+
object_collections.each do |object_collection|
|
|
123
|
+
proxy_callbacks(:before, object_collection, method_name, *method_args)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
@ui.logger.debug { "method_name == #{method_name.inspect}" }
|
|
127
|
+
if @provider.respond_to?(method_name)
|
|
128
|
+
@ui.logger.debug { "@provider.send(#{method_name.inspect}, #{method_args.inspect})" }
|
|
129
|
+
result = @provider.send(method_name, *method_args)
|
|
130
|
+
else
|
|
131
|
+
raise TestLab::ProviderError, "Your provider does not respond to the method '#{method_name}'!"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
self_callbacks(:after, method_name, *method_args)
|
|
135
|
+
|
|
136
|
+
object_collections.reverse.each do |object_collection|
|
|
137
|
+
proxy_callbacks(:after, object_collection, method_name, *method_args)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
result
|
|
141
|
+
else
|
|
142
|
+
super(method_name, *method_args)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
class TestLab
|
|
2
|
+
|
|
3
|
+
# Provider Error Class
|
|
4
|
+
class ProviderError < TestLabError; end
|
|
5
|
+
|
|
6
|
+
# Provider Class
|
|
7
|
+
#
|
|
8
|
+
# @author Zachary Patten <zachary@jovelabs.net>
|
|
9
|
+
class Provider
|
|
10
|
+
PROXY_METHODS = %w(instance_id state user ip port create destroy up down reload status alive? dead? exists?).map(&:to_sym)
|
|
11
|
+
|
|
12
|
+
autoload :AWS, 'testlab/providers/aws'
|
|
13
|
+
autoload :Local, 'testlab/providers/local'
|
|
14
|
+
autoload :Vagrant, 'testlab/providers/vagrant'
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
|
|
18
|
+
# Returns the path to the gems provider templates
|
|
19
|
+
def template_dir
|
|
20
|
+
File.join(TestLab.gem_dir, "lib", "testlab", "providers", "templates")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class TestLab
|
|
2
|
+
|
|
3
|
+
class Provider
|
|
4
|
+
|
|
5
|
+
# AWS Provider Error Class
|
|
6
|
+
class AWSError < ProviderError; end
|
|
7
|
+
|
|
8
|
+
# AWS Provider Class
|
|
9
|
+
#
|
|
10
|
+
# @author Zachary Patten <zachary@jovelabs.net>
|
|
11
|
+
class AWS
|
|
12
|
+
|
|
13
|
+
def initialize(ui=ZTK::UI.new)
|
|
14
|
+
@ui = ui
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class TestLab
|
|
2
|
+
|
|
3
|
+
class Provider
|
|
4
|
+
|
|
5
|
+
# Local Provider Error Class
|
|
6
|
+
class LocalError < ProviderError; end
|
|
7
|
+
|
|
8
|
+
# Local Provider Class
|
|
9
|
+
#
|
|
10
|
+
# @author Zachary Patten <zachary@jovelabs.net>
|
|
11
|
+
class Local
|
|
12
|
+
|
|
13
|
+
def initialize(ui=ZTK::UI.new)
|
|
14
|
+
@ui = ui
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# Auto-generated by TestLab v<%= TestLab::VERSION %> on <%= Time.now.utc %> -- DO NOT EDIT!
|
|
4
|
+
#
|
|
5
|
+
# Vagrant v2 Configuration
|
|
6
|
+
###########################
|
|
7
|
+
|
|
8
|
+
Vagrant.configure("2") do |config|
|
|
9
|
+
config.vm.define <%= @id.inspect %> do |testlab|
|
|
10
|
+
testlab.vm.hostname = <%= @hostname.inspect %>
|
|
11
|
+
testlab.vm.box = <%= @box.inspect %>
|
|
12
|
+
testlab.vm.box_url = <%= @box_url.inspect %>
|
|
13
|
+
testlab.vm.network(:private_network, :ip => <%= @ip.inspect %>)
|
|
14
|
+
|
|
15
|
+
testlab.vm.provider :virtualbox do |vb|
|
|
16
|
+
vb.name = <%= @hostname.inspect %>
|
|
17
|
+
vb.customize(["modifyvm", :id, "--cpus", <%= @cpus.inspect %>])
|
|
18
|
+
vb.customize(["modifyvm", :id, "--memory", <%= @memory.inspect %>])
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
testlab.ssh.username = <%= @user.inspect %>
|
|
22
|
+
testlab.ssh.port = <%= @port.inspect %>
|
|
23
|
+
testlab.ssh.forward_agent = true
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
class TestLab
|
|
2
|
+
|
|
3
|
+
class Provider
|
|
4
|
+
|
|
5
|
+
# Vagrant Provider Error Class
|
|
6
|
+
class VagrantError < ProviderError; end
|
|
7
|
+
|
|
8
|
+
# Vagrant Provider Class
|
|
9
|
+
#
|
|
10
|
+
# @author Zachary Patten <zachary@jovelabs.net>
|
|
11
|
+
class Vagrant
|
|
12
|
+
|
|
13
|
+
# States which indicate the VM is running
|
|
14
|
+
RUNNING_STATES = %w(running).map(&:to_sym)
|
|
15
|
+
|
|
16
|
+
# States which indicate the VM is shut down
|
|
17
|
+
SHUTDOWN_STATES = %w(aborted paused saved poweroff).map(&:to_sym)
|
|
18
|
+
|
|
19
|
+
# The state we report if we can not determine the VM state
|
|
20
|
+
UNKNOWN_STATE = :unknown
|
|
21
|
+
|
|
22
|
+
# A collection of all valid states the VM can be in
|
|
23
|
+
VALID_STATES = (RUNNING_STATES + SHUTDOWN_STATES).flatten
|
|
24
|
+
|
|
25
|
+
# A collection of all invalid states the VM can be in
|
|
26
|
+
INVALID_STATES = (%w(not_created).map(&:to_sym) + [UNKNOWN_STATE]).flatten
|
|
27
|
+
|
|
28
|
+
# A collection of all states the VM can be in
|
|
29
|
+
ALL_STATES = (VALID_STATES + INVALID_STATES).flatten
|
|
30
|
+
|
|
31
|
+
MSG_NO_LAB = %(We could not find a test lab!)
|
|
32
|
+
|
|
33
|
+
################################################################################
|
|
34
|
+
|
|
35
|
+
def initialize(config={}, ui=nil)
|
|
36
|
+
@config = config
|
|
37
|
+
@ui = (ui || TestLab.ui)
|
|
38
|
+
|
|
39
|
+
# ensure our vagrant key is there
|
|
40
|
+
@config[:vagrant] ||= Hash.new
|
|
41
|
+
|
|
42
|
+
render_vagrantfile
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
################################################################################
|
|
46
|
+
|
|
47
|
+
# Create the Vagrant instance
|
|
48
|
+
def create
|
|
49
|
+
self.up
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Destroy Vagrant-controlled VM
|
|
53
|
+
def destroy
|
|
54
|
+
!self.exists? and raise VagrantError, MSG_NO_LAB
|
|
55
|
+
|
|
56
|
+
self.down
|
|
57
|
+
self.vagrant_cli("destroy", "--force", self.instance_id)
|
|
58
|
+
|
|
59
|
+
self.state
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
################################################################################
|
|
63
|
+
|
|
64
|
+
# Online Vagrant-controlled VM
|
|
65
|
+
def up
|
|
66
|
+
self.vagrant_cli("up", self.instance_id)
|
|
67
|
+
ZTK::TCPSocketCheck.new(:host => self.ip, :port => self.port, :wait => 120, :ui => @ui).wait
|
|
68
|
+
|
|
69
|
+
self.state
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Halt Vagrant-controlled VM
|
|
73
|
+
def down
|
|
74
|
+
!self.exists? and raise VagrantError, MSG_NO_LAB
|
|
75
|
+
|
|
76
|
+
self.vagrant_cli("halt", self.instance_id)
|
|
77
|
+
|
|
78
|
+
self.state
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
################################################################################
|
|
82
|
+
|
|
83
|
+
# Reload Vagrant-controlled VM
|
|
84
|
+
def reload
|
|
85
|
+
self.down
|
|
86
|
+
self.up
|
|
87
|
+
|
|
88
|
+
self.state
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
################################################################################
|
|
92
|
+
|
|
93
|
+
# Inquire the state of the Vagrant-controlled VM
|
|
94
|
+
def state
|
|
95
|
+
output = self.vagrant_cli("status | grep '#{self.instance_id}'").output
|
|
96
|
+
result = UNKNOWN_STATE
|
|
97
|
+
ALL_STATES.map{ |s| s.to_s.gsub('_', ' ') }.each do |state|
|
|
98
|
+
if output =~ /#{state}/
|
|
99
|
+
result = state.to_s.gsub(' ', '_')
|
|
100
|
+
break
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
result.to_sym
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
################################################################################
|
|
107
|
+
|
|
108
|
+
# Does the Vagrant-controlled VM exist?
|
|
109
|
+
def exists?
|
|
110
|
+
(self.state != :not_created)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Is the Vagrant-controlled VM alive?
|
|
114
|
+
def alive?
|
|
115
|
+
(self.exists? && (RUNNING_STATES.include?(self.state) rescue false))
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Is the Vagrant-controlled VM dead?
|
|
119
|
+
def dead?
|
|
120
|
+
!self.alive?
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# START CORE CONFIG
|
|
124
|
+
####################
|
|
125
|
+
|
|
126
|
+
def instance_id
|
|
127
|
+
(@config[:vagrant][:id] || "testlab-#{ENV['USER']}".downcase)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def user
|
|
131
|
+
(@config[:vagrant][:user] || "vagrant")
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def identity
|
|
135
|
+
(@config[:vagrant][:identity] || File.join(ENV['HOME'], ".vagrant.d", "insecure_private_key"))
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def ip
|
|
139
|
+
(@config[:vagrant][:ip] || "192.168.33.10")
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def port
|
|
143
|
+
(@config[:vagrant][:port] || 22)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
##################
|
|
147
|
+
# END CORE CONFIG
|
|
148
|
+
|
|
149
|
+
def hostname
|
|
150
|
+
(@config[:vagrant][:hostname] || self.instance_id)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def box
|
|
154
|
+
(@config[:vagrant][:box] || "precise64")
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def box_url
|
|
158
|
+
(@config[:vagrant][:box_url] || "http://files.vagrantup.com/precise64.box")
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def cpus
|
|
162
|
+
(@config[:vagrant][:cpus] || 2)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def memory
|
|
166
|
+
(@config[:vagrant][:memory] || 2048)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
################################################################################
|
|
170
|
+
|
|
171
|
+
def vagrant_cli(*args)
|
|
172
|
+
@ui.logger.debug { "args == #{args.inspect}" }
|
|
173
|
+
|
|
174
|
+
command = TestLab.build_command("vagrant", *args)
|
|
175
|
+
@ui.logger.debug { "command == #{command.inspect}" }
|
|
176
|
+
|
|
177
|
+
ZTK::Command.new(:ui => @ui).exec(command, :silence => true)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def render_vagrantfile
|
|
181
|
+
context = {
|
|
182
|
+
:id => self.instance_id,
|
|
183
|
+
:ip => self.ip,
|
|
184
|
+
:hostname => self.hostname,
|
|
185
|
+
:user => self.user,
|
|
186
|
+
:port => self.port,
|
|
187
|
+
:cpus => self.cpus,
|
|
188
|
+
:memory => self.memory,
|
|
189
|
+
:box => self.box,
|
|
190
|
+
:box_url => self.box_url
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
vagrantfile_template = File.join(TestLab::Provider.template_dir, "vagrant", "Vagrantfile.erb")
|
|
194
|
+
vagrantfile = File.join(@config[:repo], "Vagrantfile")
|
|
195
|
+
IO.write(vagrantfile, ZTK::Template.render(vagrantfile_template, context))
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
################################################################################
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
end
|
|
204
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
class TestLab
|
|
2
|
+
|
|
3
|
+
# Provisioner Error Class
|
|
4
|
+
class ProvisionerError < TestLabError; end
|
|
5
|
+
|
|
6
|
+
# Provisioner Class
|
|
7
|
+
#
|
|
8
|
+
# @author Zachary Patten <zachary@jovelabs.net>
|
|
9
|
+
class Provisioner
|
|
10
|
+
|
|
11
|
+
autoload :Shell, 'testlab/provisioners/shell'
|
|
12
|
+
autoload :Chef, 'testlab/provisioners/chef'
|
|
13
|
+
|
|
14
|
+
class << self
|
|
15
|
+
|
|
16
|
+
# Returns the path to the gems provisioner templates
|
|
17
|
+
def template_dir
|
|
18
|
+
File.join(TestLab.gem_dir, "lib", "testlab", "provisioners", "templates")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
class TestLab
|
|
2
|
+
|
|
3
|
+
class Provisioner
|
|
4
|
+
|
|
5
|
+
# Chef Provisioner Error Class
|
|
6
|
+
class ChefError < ProvisionerError; end
|
|
7
|
+
|
|
8
|
+
# Chef Provisioner Class
|
|
9
|
+
#
|
|
10
|
+
# @author Zachary Patten <zachary@jovelabs.net>
|
|
11
|
+
class Chef
|
|
12
|
+
|
|
13
|
+
def initialize(config={}, ui=nil)
|
|
14
|
+
@config = config
|
|
15
|
+
@ui = (ui || TestLab.ui)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
class TestLab
|
|
2
|
+
|
|
3
|
+
class Provisioner
|
|
4
|
+
|
|
5
|
+
# Shell Provisioner Error Class
|
|
6
|
+
class ShellError < ProvisionerError; end
|
|
7
|
+
|
|
8
|
+
# Shell Provisioner Class
|
|
9
|
+
#
|
|
10
|
+
# @author Zachary Patten <zachary@jovelabs.net>
|
|
11
|
+
class Shell
|
|
12
|
+
|
|
13
|
+
def initialize(config={}, ui=nil)
|
|
14
|
+
@config = config
|
|
15
|
+
@ui = (ui || TestLab.ui)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
end
|
|
File without changes
|