testlab 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml CHANGED
@@ -1,9 +1,14 @@
1
1
  language: ruby
2
2
 
3
3
  rvm:
4
+ - ree
4
5
  - 1.9.2
5
6
  - 1.9.3
6
7
  - 2.0.0
7
8
 
8
9
  notifications:
9
10
  irc: "irc.freenode.net#jovelabs"
11
+
12
+ matrix:
13
+ allow_failures:
14
+ - rvm: ree
data/README.md CHANGED
@@ -40,7 +40,7 @@ Issues:
40
40
 
41
41
  TestLab - A framework for building lightweight virtual laboratories using LXC
42
42
 
43
- * Author: Zachary Patten <zachary@jovelabs.com> [![endorse](http://api.coderwall.com/zpatten/endorsecount.png)](http://coderwall.com/zpatten)
43
+ * Author: Zachary Patten <zachary AT jovelabs DOT com> [![endorse](http://api.coderwall.com/zpatten/endorsecount.png)](http://coderwall.com/zpatten)
44
44
  * Copyright: Copyright (c) Zachary Patten
45
45
  * License: Apache License, Version 2.0
46
46
 
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
1
  ################################################################################
2
2
  #
3
- # Author: Zachary Patten <zachary@jovelabs.net>
3
+ # Author: Zachary Patten <zachary AT jovelabs DOT com>
4
4
  # Copyright: Copyright (c) Zachary Patten
5
5
  # License: Apache License, Version 2.0
6
6
  #
data/bin/tl CHANGED
@@ -1,4 +1,23 @@
1
1
  #!/usr/bin/env ruby
2
+ ################################################################################
3
+ #
4
+ # Author: Zachary Patten <zachary AT jovelabs DOT com>
5
+ # Copyright: Copyright (c) Zachary Patten
6
+ # License: Apache License, Version 2.0
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+ ################################################################################
2
21
  require 'gli'
3
22
  require 'testlab'
4
23
 
@@ -58,7 +77,14 @@ end
58
77
  desc 'Display information on the status of the test lab'
59
78
  command :status do |status|
60
79
  status.action do |global_options,options,args|
61
- @testlab.status
80
+ @testlab.ui.stdout.puts("\nNODES:".green.bold)
81
+ commands[:node].commands[:status].execute({}, {}, [])
82
+
83
+ @testlab.ui.stdout.puts("\nNETWORKS:".green.bold)
84
+ commands[:network].commands[:status].execute({}, {}, [])
85
+
86
+ @testlab.ui.stdout.puts("\nCONTAINERS:".green.bold)
87
+ commands[:container].commands[:status].execute({}, {}, [])
62
88
  end
63
89
  end
64
90
 
@@ -76,18 +102,60 @@ command :node do |c|
76
102
  help_now!('id is required') if options[:id].nil?
77
103
 
78
104
  node = @testlab.nodes.select{ |n| n.id.to_sym == options[:id].to_sym }.first
105
+ node.nil? and raise TestLab::TestLabError, "We could not find the node you supplied!"
106
+
79
107
  node.ssh.console
80
108
  end
81
109
  end
82
110
 
111
+ c.desc 'Display the status of node(s)'
112
+ c.long_desc 'Displays the status of all nodes or a single node if supplied via the id parameter.'
113
+ c.command :status do |status|
114
+ status.action do |global_options, options, args|
115
+ if options[:id].nil?
116
+ ZTK::Report.new(:ui => @testlab.ui).spreadsheet(@testlab.nodes, TestLab::Node::STATUS_KEYS) do |node|
117
+ OpenStruct.new(node.status)
118
+ end
119
+ else
120
+ node = @testlab.nodes.select{ |c| c.id.to_sym == options[:id].to_sym }.first
121
+ node.nil? and raise TestLab::TestLabError, "We could not find the node you supplied!"
122
+
123
+ ZTK::Report.new(:ui => @testlab.ui).list(node, TestLab::Node::STATUS_KEYS) do |node|
124
+ OpenStruct.new(node.status)
125
+ end
126
+ end
127
+ end
128
+ end
129
+
83
130
  end
84
131
 
85
132
  desc 'Manage networks'
86
133
  arg_name 'Describe arguments to network here'
87
134
  command :network do |c|
88
- c.action do |global_options,options,args|
89
- puts "network command ran"
135
+
136
+ c.desc 'Network ID or Name'
137
+ c.arg_name 'network'
138
+ c.flag [:i, :id]
139
+
140
+ c.desc 'Display the status of network(s)'
141
+ c.long_desc 'Displays the status of all networks or a single network if supplied via the id parameter.'
142
+ c.command :status do |status|
143
+ status.action do |global_options, options, args|
144
+ if options[:id].nil?
145
+ ZTK::Report.new(:ui => @testlab.ui).spreadsheet(@testlab.networks, TestLab::Network::STATUS_KEYS) do |network|
146
+ OpenStruct.new(network.status)
147
+ end
148
+ else
149
+ network = @testlab.networks.select{ |c| c.id.to_sym == options[:id].to_sym }.first
150
+ network.nil? and raise TestLab::TestLabError, "We could not find the network you supplied!"
151
+
152
+ ZTK::Report.new(:ui => @testlab.ui).list(network, TestLab::Network::STATUS_KEYS) do |network|
153
+ OpenStruct.new(network.status)
154
+ end
155
+ end
156
+ end
90
157
  end
158
+
91
159
  end
92
160
 
93
161
  desc 'Manage containers'
@@ -100,14 +168,35 @@ command :container do |c|
100
168
 
101
169
  c.desc 'Open an SSH console to a container'
102
170
  c.command :ssh do |ssh|
103
- ssh.action do |global_options,options,args|
171
+ ssh.action do |global_options, options, args|
104
172
  help_now!('id is required') if options[:id].nil?
105
173
 
106
174
  container = @testlab.containers.select{ |n| n.id.to_sym == options[:id].to_sym }.first
175
+ container.nil? and raise TestLab::TestLabError, "We could not find the container you supplied!"
176
+
107
177
  container.ssh.console
108
178
  end
109
179
  end
110
180
 
181
+ c.desc 'Display the status of container(s)'
182
+ c.long_desc 'Displays the status of all containers or a single container if supplied via the id parameter.'
183
+ c.command :status do |status|
184
+ status.action do |global_options, options, args|
185
+ if options[:id].nil?
186
+ ZTK::Report.new(:ui => @testlab.ui).spreadsheet(@testlab.containers, TestLab::Container::STATUS_KEYS) do |container|
187
+ OpenStruct.new(container.status)
188
+ end
189
+ else
190
+ container = @testlab.containers.select{ |c| c.id.to_sym == options[:id].to_sym }.first
191
+ container.nil? and raise TestLab::TestLabError, "We could not find the container you supplied!"
192
+
193
+ ZTK::Report.new(:ui => @testlab.ui).list(container, TestLab::Container::STATUS_KEYS) do |container|
194
+ OpenStruct.new(container.status)
195
+ end
196
+ end
197
+ end
198
+ end
199
+
111
200
  end
112
201
 
113
202
  desc 'Manage routes'
@@ -169,6 +258,7 @@ on_error do |exception|
169
258
  command_regex = /Command '([\w]+)' /
170
259
  command = exception.message.scan(command_regex).flatten.first
171
260
 
261
+ @ui.stderr.puts
172
262
  commands[:help] and commands[:help].execute({}, {}, (command.nil? ? [] : [command.to_s]))
173
263
 
174
264
  false
@@ -52,7 +52,7 @@ class TestLab
52
52
  #
53
53
  # @see TestLab::Interface
54
54
  #
55
- # @author Zachary Patten <zachary@jovelabs.net>
55
+ # @author Zachary Patten <zachary AT jovelabs DOT com>
56
56
  class Container < ZTK::DSL::Base
57
57
 
58
58
  # An array of symbols of the various keys in our status hash.
@@ -5,7 +5,7 @@ class TestLab
5
5
 
6
6
  # Interface Class
7
7
  #
8
- # @author Zachary Patten <zachary@jovelabs.net>
8
+ # @author Zachary Patten <zachary AT jovelabs DOT com>
9
9
  class Interface < ZTK::DSL::Base
10
10
 
11
11
  # Associations and Attributes
@@ -5,7 +5,7 @@ class TestLab
5
5
 
6
6
  # Labfile Class
7
7
  #
8
- # @author Zachary Patten <zachary@jovelabs.net>
8
+ # @author Zachary Patten <zachary AT jovelabs DOT com>
9
9
  class Labfile < ZTK::DSL::Base
10
10
  has_many :nodes, :class_name => 'TestLab::Node'
11
11
 
@@ -5,7 +5,7 @@ class TestLab
5
5
 
6
6
  # Network Class
7
7
  #
8
- # @author Zachary Patten <zachary@jovelabs.net>
8
+ # @author Zachary Patten <zachary AT jovelabs DOT com>
9
9
  class Network < ZTK::DSL::Base
10
10
  STATUS_KEYS = %w(node_id id state interface network netmask broadcast).map(&:to_sym)
11
11
 
data/lib/testlab/node.rb CHANGED
@@ -5,7 +5,7 @@ class TestLab
5
5
 
6
6
  # Node Class
7
7
  #
8
- # @author Zachary Patten <zachary@jovelabs.net>
8
+ # @author Zachary Patten <zachary AT jovelabs DOT com>
9
9
  class Node < ZTK::DSL::Base
10
10
  STATUS_KEYS = %w(id instance_id state user ip port provider con net).map(&:to_sym)
11
11
 
@@ -5,7 +5,7 @@ class TestLab
5
5
 
6
6
  # Provider Class
7
7
  #
8
- # @author Zachary Patten <zachary@jovelabs.net>
8
+ # @author Zachary Patten <zachary AT jovelabs DOT com>
9
9
  class Provider
10
10
  PROXY_METHODS = %w(instance_id state user ip port create destroy up down reload status alive? dead? exists?).map(&:to_sym)
11
11
 
@@ -7,7 +7,7 @@ class TestLab
7
7
 
8
8
  # AWS Provider Class
9
9
  #
10
- # @author Zachary Patten <zachary@jovelabs.net>
10
+ # @author Zachary Patten <zachary AT jovelabs DOT com>
11
11
  class AWS
12
12
 
13
13
  def initialize(ui=ZTK::UI.new)
@@ -7,7 +7,7 @@ class TestLab
7
7
 
8
8
  # Local Provider Class
9
9
  #
10
- # @author Zachary Patten <zachary@jovelabs.net>
10
+ # @author Zachary Patten <zachary AT jovelabs DOT com>
11
11
  class Local
12
12
 
13
13
  def initialize(ui=ZTK::UI.new)
@@ -7,7 +7,7 @@ class TestLab
7
7
 
8
8
  # Vagrant Provider Class
9
9
  #
10
- # @author Zachary Patten <zachary@jovelabs.net>
10
+ # @author Zachary Patten <zachary AT jovelabs DOT com>
11
11
  class Vagrant
12
12
 
13
13
  # States which indicate the VM is running
@@ -5,7 +5,7 @@ class TestLab
5
5
 
6
6
  # Provisioner Class
7
7
  #
8
- # @author Zachary Patten <zachary@jovelabs.net>
8
+ # @author Zachary Patten <zachary AT jovelabs DOT com>
9
9
  class Provisioner
10
10
  autoload :Shell, 'testlab/provisioners/shell'
11
11
  autoload :Chef, 'testlab/provisioners/chef'
@@ -7,7 +7,7 @@ class TestLab
7
7
 
8
8
  # Chef Provisioner Class
9
9
  #
10
- # @author Zachary Patten <zachary@jovelabs.net>
10
+ # @author Zachary Patten <zachary AT jovelabs DOT com>
11
11
  class Chef
12
12
 
13
13
  def initialize(config={}, ui=nil)
@@ -7,7 +7,7 @@ class TestLab
7
7
 
8
8
  # Shell Provisioner Class
9
9
  #
10
- # @author Zachary Patten <zachary@jovelabs.net>
10
+ # @author Zachary Patten <zachary AT jovelabs DOT com>
11
11
  class Shell
12
12
  require 'tempfile'
13
13
 
@@ -6,7 +6,7 @@ class TestLab
6
6
 
7
7
  # CIDR Module
8
8
  #
9
- # @author Zachary Patten <zachary@jovelabs.net>
9
+ # @author Zachary Patten <zachary AT jovelabs DOT com>
10
10
  module CIDR
11
11
 
12
12
  CIDR_MATRIX = {
@@ -6,7 +6,7 @@ class TestLab
6
6
 
7
7
  # Misc Module
8
8
  #
9
- # @author Zachary Patten <zachary@jovelabs.net>
9
+ # @author Zachary Patten <zachary AT jovelabs DOT com>
10
10
  module Misc
11
11
 
12
12
  def format_object(object, color)
@@ -36,7 +36,8 @@ class TestLab
36
36
 
37
37
  message = format_message(message)
38
38
  length = message.uncolor.length
39
- mark = ((' ' * (60 - length)) + mark)
39
+ max = (length >= 60 ? (length+1) : (60 - length))
40
+ mark = ((' ' * max) + mark)
40
41
 
41
42
  ZTK::Benchmark.bench(:ui => ui, :message => message, :mark => mark) do
42
43
  yield
@@ -5,7 +5,10 @@ class TestLab
5
5
 
6
6
  # Utility Module
7
7
  #
8
- # @author Zachary Patten <zachary@jovelabs.net>
8
+ # This provides an interface to our various child utility modules. We also at
9
+ # times mix those child modules in instead of calling them here.
10
+ #
11
+ # @author Zachary Patten <zachary AT jovelabs DOT com>
9
12
  module Utility
10
13
  autoload :CIDR, 'testlab/utility/cidr'
11
14
  autoload :Misc, 'testlab/utility/misc'
@@ -1,6 +1,6 @@
1
1
  class TestLab
2
2
  unless const_defined?(:VERSION)
3
3
  # TestLab Gem Version
4
- VERSION = "0.3.0"
4
+ VERSION = "0.3.1"
5
5
  end
6
6
  end
data/lib/testlab.rb CHANGED
@@ -1,14 +1,90 @@
1
1
  require 'ztk'
2
+ require 'active_support/inflector'
2
3
 
3
4
  require 'testlab/version'
4
5
  require 'testlab/monkeys'
5
6
 
6
- # Top-Level TestLab Class
7
+ # TestLab - A framework for building lightweight virtual infrastructure using LXC
7
8
  #
8
- # @author Zachary Patten <zachary@jovelabs.net>
9
+ # The core concept with the TestLab is the *Labfile*. This file dictates the
10
+ # topology of your virtual infrastructure. With simple commands you can setup
11
+ # and teardown this infrastructure on the fly for all sorts of purposes from
12
+ # automating infrastructure testing to testing new software to experimenting
13
+ # in general where you want to spin up alot of servers but do not want the
14
+ # overhead of virtualization. At it's core TestLab uses Linux Containers (LXC)
15
+ # to accomplish this.
16
+ #
17
+ # @example Sample Labfile:
18
+ # shell_provision_script = <<-EOF
19
+ # set -x
20
+ # apt-get -y update
21
+ # apt-get -y install dnsutils
22
+ # EOF
23
+ #
24
+ # config Hash[
25
+ # :domain => "default.zone"
26
+ # ]
27
+ #
28
+ # node :localhost do
29
+ # components %w(resolv bind)
30
+ #
31
+ # provider TestLab::Provider::Vagrant
32
+ # config Hash[
33
+ # :vagrant => {
34
+ # :id => "mytestlab-#{ENV['USER']}".downcase,
35
+ # :ip => "192.168.13.37",
36
+ # :user => "vagrant",
37
+ # :port => 22,
38
+ # :cpus => 8,
39
+ # :memory => 16384,
40
+ # :box => 'raring64'
41
+ # },
42
+ # :repo => File.join(ENV['HOME'], "code", "personal", "testlab-repo")
43
+ # ]
44
+ #
45
+ # network :east do
46
+ # address '10.10.0.1/16'
47
+ # bridge :br0
48
+ # end
49
+ #
50
+ # container "server-east-1" do
51
+ # domain "east.zone"
52
+ #
53
+ # distro "ubuntu"
54
+ # release "precise"
55
+ #
56
+ # provisioner TestLab::Provisioner::Shell
57
+ # config Hash[
58
+ # :shell => "/bin/bash",
59
+ # :setup => shell_provision_script
60
+ # ]
61
+ #
62
+ # interface do
63
+ # name :eth0
64
+ # network_id :east
65
+ # address '10.10.0.254/16'
66
+ # mac '00:00:5e:b7:e5:15'
67
+ # end
68
+ # end
69
+ #
70
+ # end
71
+ #
72
+ # @example TestLab can be instantiated easily:
73
+ # log_file = File.join(Dir.pwd, "testlab.log")
74
+ # logger = ZTK::Logger.new(log_file)
75
+ # ui = ZTK::UI.new(:logger => logger)
76
+ # testlab = TestLab.new(:ui => ui)
77
+ #
78
+ # @example We can control things via code easily as well:
79
+ # testlab.create # creates the lab
80
+ # testlab.up # ensures the lab is up and running
81
+ # testlab.setup # setup the lab, creating all networks and containers
82
+ # testlab.teardown # teardown the lab, destroy all networks and containers
83
+ #
84
+ # @author Zachary Patten <zachary AT jovelabs DOT com>
9
85
  class TestLab
10
86
 
11
- # Top-Level Error Class
87
+ # TestLab Error Class
12
88
  class TestLabError < StandardError; end
13
89
 
14
90
  # Main Classes
@@ -24,11 +100,10 @@ class TestLab
24
100
  include TestLab::Utility::Misc
25
101
 
26
102
  def initialize(options={})
27
- labfile = (options[:labfile] || 'Labfile')
28
- labfile_path = ZTK::Locator.find(labfile)
29
-
30
103
  self.ui = (options[:ui] || ZTK::UI.new)
31
104
 
105
+ labfile = (options[:labfile] || 'Labfile')
106
+ labfile_path = ZTK::Locator.find(labfile)
32
107
  @labfile = TestLab::Labfile.load(labfile_path)
33
108
  end
34
109
 
@@ -71,53 +146,22 @@ class TestLab
71
146
 
72
147
  # Test Lab Alive?
73
148
  #
74
- # Are all of our nodes alive; that is up and running?
149
+ # Are all of our nodes up and running?
75
150
  #
76
- # @return [Boolean] True is all nodes are running; false otherwise.
151
+ # @return [Boolean] True if all nodes are running; false otherwise.
77
152
  def alive?
78
153
  nodes.map(&:state).all?{ |state| state == :running }
79
154
  end
80
155
 
81
156
  # Test Lab Dead?
82
157
  #
83
- # Are any of our nodes dead; that is not up and running?
158
+ # Are any of our nodes not up and running?
84
159
  #
85
160
  # @return [Boolean] False is all nodes are running; true otherwise.
86
161
  def dead?
87
162
  !alive?
88
163
  end
89
164
 
90
- # Test Lab Status
91
- #
92
- # Iterates our various DSL objects and calls their status methods pushing
93
- # the results through ZTK::Report to generate nice tabled output for us
94
- # indicating the state of the lab.
95
- #
96
- # This can only be run if the lab is alive.
97
- #
98
- # @return [Boolean] True if successful; false otherwise.
99
- def status
100
- if alive?
101
- %w(nodes networks containers).map(&:to_sym).each do |object_symbol|
102
- self.ui.stdout.puts
103
- self.ui.stdout.puts("#{object_symbol}:".upcase.green.bold)
104
-
105
- klass = object_symbol.to_s.singularize.capitalize
106
- status_keys = "TestLab::#{klass}::STATUS_KEYS".constantize
107
-
108
- ZTK::Report.new(:ui => self.ui).spreadsheet(self.send(object_symbol), status_keys) do |object|
109
- OpenStruct.new(object.status)
110
- end
111
- end
112
-
113
- true
114
- else
115
- self.ui.stdout.puts("Looks like your test lab is dead; fix this and try again.")
116
-
117
- false
118
- end
119
- end
120
-
121
165
  # Test Lab Setup
122
166
  #
123
167
  # Attempts to setup our lab topology. This calls the setup method on all of
@@ -0,0 +1,193 @@
1
+ ################################################################################
2
+ #
3
+ # Author: Zachary Patten <zachary AT jovelabs DOT com>
4
+ # Copyright: Copyright (c) Zachary Patten
5
+ # License: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+ ################################################################################
20
+ require "spec_helper"
21
+
22
+ describe TestLab::Container do
23
+
24
+ subject { @testlab = TestLab.new(:labfile => LABFILE); @testlab.containers.first }
25
+
26
+ describe "class" do
27
+
28
+ it "should be an instance of TestLab::Container" do
29
+ subject.should be_an_instance_of TestLab::Container
30
+ end
31
+
32
+ end
33
+
34
+ describe "methods" do
35
+
36
+ describe "domains" do
37
+ it "should return the domains for all defined containers" do
38
+ subject.class.domains.should be_kind_of(Array)
39
+ subject.class.domains.should_not be_empty
40
+ subject.class.domains.should == ["default.zone"]
41
+ end
42
+ end
43
+
44
+ describe "#status" do
45
+ it "should return a hash of status information about the container" do
46
+ subject.lxc.stub(:state) { :not_created }
47
+ subject.status.should be_kind_of(Hash)
48
+ subject.status.should_not be_empty
49
+ end
50
+ end
51
+
52
+ describe "#state" do
53
+ it "should return the state of the container" do
54
+ subject.lxc.stub(:state) { :not_created }
55
+ subject.state.should == :not_created
56
+ end
57
+ end
58
+
59
+ describe "#fqdn" do
60
+ it "should return the FQDN for the container" do
61
+ subject.fqdn.should == "server-1.default.zone"
62
+ end
63
+ end
64
+
65
+ describe "#ip" do
66
+ it "should return the IP address of the containers primary interface" do
67
+ subject.ip.should == "192.168.0.254"
68
+ end
69
+ end
70
+
71
+ describe "#cidr" do
72
+ it "should return the CIDR of the containers primary interface" do
73
+ subject.cidr.should == 16
74
+ end
75
+ end
76
+
77
+ describe "#ptr" do
78
+ it "should return a BIND PTR record for the containers primary interface" do
79
+ subject.ptr.should be_kind_of(String)
80
+ subject.ptr.should_not be_empty
81
+ subject.ptr.should == "254.0"
82
+ end
83
+ end
84
+
85
+ describe "#lxc" do
86
+ it "should return an instance of LXC::Container configured for this container" do
87
+ subject.lxc.should_not be_nil
88
+ subject.lxc.should be_kind_of(LXC::Container)
89
+ end
90
+ end
91
+
92
+ describe "#ssh" do
93
+ it "should return an instance of ZTK::SSH configured for this container" do
94
+ subject.ssh.should_not be_nil
95
+ subject.ssh.should be_kind_of(ZTK::SSH)
96
+ end
97
+ end
98
+
99
+ describe "#exists?" do
100
+ it "should return false for a non-existant container" do
101
+ subject.lxc.stub(:exists?) { false }
102
+ subject.exists?.should == false
103
+ end
104
+ end
105
+
106
+ describe "#detect_arch" do
107
+ it "should return the appropriate disto dependent machine architecture for our lxc-template" do
108
+ subject.node.stub(:arch) { "x86_64" }
109
+ subject.detect_arch.should == "amd64"
110
+ end
111
+ end
112
+
113
+ describe "#primary_interface" do
114
+ it "should return the primary interface for a container" do
115
+ subject.primary_interface.should_not be_nil
116
+ subject.primary_interface.should be_kind_of(TestLab::Interface)
117
+
118
+ @testlab.containers.last.primary_interface.should_not be_nil
119
+ @testlab.containers.last.primary_interface.should be_kind_of(TestLab::Interface)
120
+ end
121
+ end
122
+
123
+ describe "#generate_ip" do
124
+ it "should generate a random RFC compliant private IP address" do
125
+ subject.generate_ip.should_not be_nil
126
+ subject.generate_ip.should be_kind_of(String)
127
+ end
128
+ end
129
+
130
+ describe "#generate_mac" do
131
+ it "should generate a random RFC compliant private MAC address" do
132
+ subject.generate_mac.should_not be_nil
133
+ subject.generate_mac.should be_kind_of(String)
134
+ end
135
+ end
136
+
137
+ describe "#create" do
138
+ it "should create the container" do
139
+ subject.lxc.config.stub(:save) { true }
140
+ subject.stub(:detect_arch) { "amd64" }
141
+ subject.lxc.stub(:create) { true }
142
+ subject.create
143
+ end
144
+ end
145
+
146
+ describe "#destroy" do
147
+ it "should destroy the container" do
148
+ subject.lxc.stub(:destroy) { true }
149
+ subject.destroy
150
+ end
151
+ end
152
+
153
+ describe "#up" do
154
+ it "should up the container" do
155
+ subject.lxc.stub(:start) { true }
156
+ subject.lxc.stub(:wait) { true }
157
+ subject.lxc.stub(:state) { :running }
158
+ subject.up
159
+ end
160
+ end
161
+
162
+ describe "#down" do
163
+ it "should down the container" do
164
+ subject.lxc.stub(:stop) { true }
165
+ subject.lxc.stub(:wait) { true }
166
+ subject.lxc.stub(:state) { :stopped }
167
+ subject.down
168
+ end
169
+ end
170
+
171
+ describe "#setup" do
172
+ it "should create and online the container" do
173
+ subject.stub(:create) { true }
174
+ subject.stub(:up) { true }
175
+ subject.instance_variable_get(:@provisioner) and subject.instance_variable_get(:@provisioner).stub(:setup) { true }
176
+
177
+ subject.setup
178
+ end
179
+ end
180
+
181
+ describe "#teardown" do
182
+ it "should create and online the container" do
183
+ subject.stub(:down) { true }
184
+ subject.stub(:destroy) { true }
185
+ subject.instance_variable_get(:@provisioner) and subject.instance_variable_get(:@provisioner).stub(:teardown) { true }
186
+
187
+ subject.teardown
188
+ end
189
+ end
190
+
191
+ end
192
+
193
+ end
@@ -0,0 +1,153 @@
1
+ ################################################################################
2
+ #
3
+ # Author: Zachary Patten <zachary AT jovelabs DOT com>
4
+ # Copyright: Copyright (c) Zachary Patten
5
+ # License: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+ ################################################################################
20
+ require "spec_helper"
21
+
22
+ describe TestLab::Network do
23
+
24
+ subject { @testlab = TestLab.new(:labfile => LABFILE); @testlab.networks.first }
25
+
26
+ describe "class" do
27
+
28
+ it "should be an instance of TestLab::Network" do
29
+ subject.should be_an_instance_of TestLab::Network
30
+ end
31
+
32
+ end
33
+
34
+ describe "methods" do
35
+
36
+ describe "ips" do
37
+ it "should return the ips for all defined containers" do
38
+ subject.class.ips.should be_kind_of(Array)
39
+ subject.class.ips.should_not be_empty
40
+ subject.class.ips.should == ["192.168.255.254"]
41
+ end
42
+ end
43
+
44
+ describe "#ptr" do
45
+ it "should return a BIND PTR record for the networks bridge interface" do
46
+ subject.ptr.should be_kind_of(String)
47
+ subject.ptr.should_not be_empty
48
+ subject.ptr.should == "254.255"
49
+ end
50
+ end
51
+
52
+ describe "#arpa" do
53
+ it "should return the ARPA network calculated from the cidr address" do
54
+ subject.arpa.should be_kind_of(String)
55
+ subject.arpa.should_not be_empty
56
+ subject.arpa.should == "168.192.in-addr.arpa"
57
+ end
58
+ end
59
+
60
+ describe "#ip" do
61
+ it "should return the IP address of the networks bridge interface" do
62
+ subject.ip.should == "192.168.255.254"
63
+ end
64
+ end
65
+
66
+ describe "#cidr" do
67
+ it "should return the CIDR of the networks bridge interface" do
68
+ subject.cidr.should == 16
69
+ end
70
+ end
71
+
72
+ describe "#netmask" do
73
+ it "should return the netmask of the networks bridge interface" do
74
+ subject.netmask.should == "255.255.0.0"
75
+ end
76
+ end
77
+
78
+ describe "#network" do
79
+ it "should return the network address of the networks bridge interface" do
80
+ subject.network.should == "192.168.0.0"
81
+ end
82
+ end
83
+
84
+ describe "#broadcast" do
85
+ it "should return the broadcast address of the networks bridge interface" do
86
+ subject.broadcast.should == "192.168.255.255"
87
+ end
88
+ end
89
+
90
+ describe "#status" do
91
+ it "should return a hash of status information about the container" do
92
+ subject.stub(:state) { :stopped }
93
+ subject.status.should be_kind_of(Hash)
94
+ subject.status.should_not be_empty
95
+ end
96
+ end
97
+
98
+ describe "#state" do
99
+ it "should return the state of the bridge" do
100
+ subject.node.ssh.stub(:exec) { OpenStruct.new(:output => " UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1") }
101
+ subject.state.should == :running
102
+ end
103
+ end
104
+
105
+ describe "#create" do
106
+ it "should create the network bridge" do
107
+ subject.node.ssh.stub(:exec) { true }
108
+ subject.create
109
+ end
110
+ end
111
+
112
+ describe "#destroy" do
113
+ it "should destroy the network bridge" do
114
+ subject.node.ssh.stub(:exec) { true }
115
+ subject.destroy
116
+ end
117
+ end
118
+
119
+ describe "#up" do
120
+ it "should online the network bridge" do
121
+ subject.node.ssh.stub(:exec) { true }
122
+ subject.up
123
+ end
124
+ end
125
+
126
+ describe "#down" do
127
+ it "should offline the network bridge" do
128
+ subject.node.ssh.stub(:exec) { true }
129
+ subject.down
130
+ end
131
+ end
132
+
133
+ describe "#setup" do
134
+ it "should create and online the network" do
135
+ subject.stub(:create) { true }
136
+ subject.stub(:up) { true }
137
+
138
+ subject.setup
139
+ end
140
+ end
141
+
142
+ describe "#teardown" do
143
+ it "should create and online the network" do
144
+ subject.stub(:down) { true }
145
+ subject.stub(:destroy) { true }
146
+
147
+ subject.teardown
148
+ end
149
+ end
150
+
151
+ end
152
+
153
+ end
data/spec/node_spec.rb ADDED
@@ -0,0 +1,95 @@
1
+ ################################################################################
2
+ #
3
+ # Author: Zachary Patten <zachary AT jovelabs DOT com>
4
+ # Copyright: Copyright (c) Zachary Patten
5
+ # License: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+ ################################################################################
20
+ require "spec_helper"
21
+
22
+ describe TestLab::Node do
23
+
24
+ subject { @testlab = TestLab.new(:labfile => LABFILE); @testlab.nodes.first }
25
+
26
+ describe "class" do
27
+
28
+ it "should be an instance of TestLab::Node" do
29
+ subject.should be_an_instance_of TestLab::Node
30
+ end
31
+
32
+ end
33
+
34
+ describe "methods" do
35
+
36
+ describe "template_dir" do
37
+ it "should return the path to the node template directory" do
38
+ subject.class.template_dir.should == "#{TestLab.gem_dir}/lib/testlab/node/templates"
39
+ end
40
+ end
41
+
42
+ describe "#status" do
43
+ it "should return a hash of status information about the node" do
44
+ subject.instance_variable_get(:@provider).stub(:state) { :not_created }
45
+ subject.status.should be_kind_of(Hash)
46
+ subject.status.should_not be_empty
47
+ end
48
+ end
49
+
50
+ describe "#lxc" do
51
+ it "should return an instance of LXC configured for this node" do
52
+ subject.lxc.should be_kind_of(LXC)
53
+ subject.lxc.should_not be_nil
54
+ end
55
+ end
56
+
57
+ describe "#arch" do
58
+ it "should return the machine architecture of the node" do
59
+ subject.ssh.stub(:exec) { OpenStruct.new(:output => "x86_64") }
60
+ subject.arch.should be_kind_of(String)
61
+ subject.arch.should_not be_empty
62
+ end
63
+ end
64
+
65
+ describe "#create" do
66
+ it "should create the node" do
67
+ subject.instance_variable_get(:@provider).stub(:create) { true }
68
+ subject.create
69
+ end
70
+ end
71
+
72
+ describe "#destroy" do
73
+ it "should destroy the node" do
74
+ subject.instance_variable_get(:@provider).stub(:destroy) { true }
75
+ subject.destroy
76
+ end
77
+ end
78
+
79
+ describe "#up" do
80
+ it "should online the node" do
81
+ subject.instance_variable_get(:@provider).stub(:up) { true }
82
+ subject.up
83
+ end
84
+ end
85
+
86
+ describe "#down" do
87
+ it "should offline the node" do
88
+ subject.instance_variable_get(:@provider).stub(:down) { true }
89
+ subject.down
90
+ end
91
+ end
92
+
93
+ end
94
+
95
+ end
@@ -1,3 +1,22 @@
1
+ ################################################################################
2
+ #
3
+ # Author: Zachary Patten <zachary AT jovelabs DOT com>
4
+ # Copyright: Copyright (c) Zachary Patten
5
+ # License: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+ ################################################################################
1
20
  require "spec_helper"
2
21
 
3
22
  describe TestLab::Provider do
@@ -1,3 +1,22 @@
1
+ ################################################################################
2
+ #
3
+ # Author: Zachary Patten <zachary AT jovelabs DOT com>
4
+ # Copyright: Copyright (c) Zachary Patten
5
+ # License: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+ ################################################################################
1
20
  require "spec_helper"
2
21
 
3
22
  describe TestLab::Provisioner do
@@ -12,4 +31,14 @@ describe TestLab::Provisioner do
12
31
 
13
32
  end
14
33
 
34
+ describe "methods" do
35
+
36
+ describe "template_dir" do
37
+ it "should return the path to the provisioner template directory" do
38
+ subject.class.template_dir.should == "#{TestLab.gem_dir}/lib/testlab/provisioners/templates"
39
+ end
40
+ end
41
+
42
+ end
43
+
15
44
  end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  ################################################################################
2
2
  #
3
- # Author: Zachary Patten <zachary@jovelabs.net>
3
+ # Author: Zachary Patten <zachary AT jovelabs DOT com>
4
4
  # Copyright: Copyright (c) Zachary Patten
5
5
  # License: Apache License, Version 2.0
6
6
  #
data/spec/support/Labfile CHANGED
@@ -1,10 +1,13 @@
1
1
  #!/usr/bin/env ruby
2
2
  #^syntax detection
3
3
 
4
+ config Hash[
5
+ :domain => "default.zone"
6
+ ]
7
+
4
8
  node :localhost do
5
9
 
6
10
  provider TestLab::Provider::Vagrant
7
-
8
11
  config Hash[
9
12
  :vagrant => {
10
13
  :id => "mytestlab-#{ENV['USER']}".downcase,
@@ -17,6 +20,8 @@ node :localhost do
17
20
  :repo => File.dirname(__FILE__)
18
21
  ]
19
22
 
23
+ route false
24
+
20
25
  network :testnet do
21
26
  address '192.168.255.254/16'
22
27
  bridge :br0
@@ -24,12 +29,30 @@ node :localhost do
24
29
 
25
30
  container "server-1" do
26
31
  distro "ubuntu"
27
- release "lucid"
32
+ release "precise"
28
33
 
29
34
  interface do
30
35
  name :eth0
31
36
  network_id :testnet
32
37
  address '192.168.0.254/16'
38
+ primary true
39
+ end
40
+
41
+ interface do
42
+ name :eth1
43
+ network_id :testnet
44
+ address '192.168.0.253/16'
45
+ end
46
+ end
47
+
48
+ container "server-2" do
49
+ distro "ubuntu"
50
+ release "precise"
51
+
52
+ interface do
53
+ name :eth0
54
+ network_id :testnet
55
+ address '192.168.0.200/16'
33
56
  end
34
57
  end
35
58
 
data/spec/testlab_spec.rb CHANGED
@@ -1,3 +1,22 @@
1
+ ################################################################################
2
+ #
3
+ # Author: Zachary Patten <zachary AT jovelabs DOT com>
4
+ # Copyright: Copyright (c) Zachary Patten
5
+ # License: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+ ################################################################################
1
20
  require "spec_helper"
2
21
 
3
22
  describe TestLab do
@@ -12,5 +31,73 @@ describe TestLab do
12
31
 
13
32
  end
14
33
 
34
+ describe "methods" do
35
+
36
+ describe "setup" do
37
+ it "should setup the test lab" do
38
+ subject.stub(:dead?) { false }
39
+ subject.nodes.each do |node|
40
+ node.stub(:setup) { true }
41
+ end
42
+ subject.containers.each do |container|
43
+ container.stub(:setup) { true }
44
+ end
45
+ subject.networks.each do |network|
46
+ network.stub(:setup) { true }
47
+ end
48
+ subject.setup
49
+ end
50
+ end
51
+
52
+ describe "teardown" do
53
+ it "should teardown the test lab" do
54
+ subject.stub(:dead?) { false }
55
+ subject.nodes.each do |node|
56
+ node.stub(:teardown) { true }
57
+ end
58
+ subject.containers.each do |container|
59
+ container.stub(:teardown) { true }
60
+ end
61
+ subject.networks.each do |network|
62
+ network.stub(:teardown) { true }
63
+ end
64
+ subject.teardown
65
+ end
66
+ end
67
+
68
+ describe "up" do
69
+ it "should online the test lab" do
70
+ subject.stub(:dead?) { false }
71
+ subject.nodes.each do |node|
72
+ node.stub(:up) { true }
73
+ end
74
+ subject.containers.each do |container|
75
+ container.stub(:up) { true }
76
+ end
77
+ subject.networks.each do |network|
78
+ network.stub(:up) { true }
79
+ end
80
+ subject.up
81
+ end
82
+ end
83
+
84
+ describe "down" do
85
+ it "should offline the test lab" do
86
+ subject.stub(:dead?) { false }
87
+ subject.nodes.each do |node|
88
+ node.stub(:down) { true }
89
+ end
90
+ subject.containers.each do |container|
91
+ container.stub(:down) { true }
92
+ end
93
+ subject.networks.each do |network|
94
+ network.stub(:down) { true }
95
+ end
96
+ subject.down
97
+ end
98
+ end
99
+
100
+ end
101
+
15
102
 
16
103
  end
data/testlab.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  ################################################################################
2
2
  #
3
- # Author: Zachary Patten <zachary@jovelabs.net>
3
+ # Author: Zachary Patten <zachary AT jovelabs DOT com>
4
4
  # Copyright: Copyright (c) Zachary Patten
5
5
  # License: Apache License, Version 2.0
6
6
  #
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
25
25
  spec.name = "testlab"
26
26
  spec.version = TestLab::VERSION
27
27
  spec.authors = ["Zachary Patten"]
28
- spec.email = ["zachary@jovelabs.com"]
28
+ spec.email = ["zachary AT jovelabs DOT com"]
29
29
  spec.description = %q{A framework for building lightweight virtual infrastructure using LXC}
30
30
  spec.summary = %q{A framework for building lightweight virtual infrastructure using LXC}
31
31
  spec.homepage = "https://github.com/zpatten/testlab"
@@ -39,6 +39,7 @@ Gem::Specification.new do |spec|
39
39
  spec.add_dependency("gli")
40
40
  spec.add_dependency("lxc")
41
41
  spec.add_dependency("ztk")
42
+ spec.add_dependency("activesupport")
42
43
 
43
44
  spec.add_development_dependency("bundler")
44
45
  spec.add_development_dependency("pry")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: testlab
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-04 00:00:00.000000000 Z
12
+ date: 2013-05-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: gli
@@ -59,6 +59,22 @@ dependencies:
59
59
  - - ! '>='
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: activesupport
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
62
78
  - !ruby/object:Gem::Dependency
63
79
  name: bundler
64
80
  requirement: !ruby/object:Gem::Requirement
@@ -173,7 +189,7 @@ dependencies:
173
189
  version: '0'
174
190
  description: A framework for building lightweight virtual infrastructure using LXC
175
191
  email:
176
- - zachary@jovelabs.com
192
+ - zachary AT jovelabs DOT com
177
193
  executables:
178
194
  - tl
179
195
  extensions: []
@@ -237,6 +253,9 @@ files:
237
253
  - lib/testlab/utility/cidr.rb
238
254
  - lib/testlab/utility/misc.rb
239
255
  - lib/testlab/version.rb
256
+ - spec/container_spec.rb
257
+ - spec/network_spec.rb
258
+ - spec/node_spec.rb
240
259
  - spec/provider_spec.rb
241
260
  - spec/provisioner_spec.rb
242
261
  - spec/spec_helper.rb
@@ -258,7 +277,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
258
277
  version: '0'
259
278
  segments:
260
279
  - 0
261
- hash: 1288569389927238950
280
+ hash: -1882861325237330048
262
281
  required_rubygems_version: !ruby/object:Gem::Requirement
263
282
  none: false
264
283
  requirements:
@@ -267,7 +286,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
267
286
  version: '0'
268
287
  segments:
269
288
  - 0
270
- hash: 1288569389927238950
289
+ hash: -1882861325237330048
271
290
  requirements: []
272
291
  rubyforge_project:
273
292
  rubygems_version: 1.8.25
@@ -275,6 +294,9 @@ signing_key:
275
294
  specification_version: 3
276
295
  summary: A framework for building lightweight virtual infrastructure using LXC
277
296
  test_files:
297
+ - spec/container_spec.rb
298
+ - spec/network_spec.rb
299
+ - spec/node_spec.rb
278
300
  - spec/provider_spec.rb
279
301
  - spec/provisioner_spec.rb
280
302
  - spec/spec_helper.rb