testlab 0.3.0 → 0.3.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.
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