testlab 0.7.4 → 0.7.5

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -17,3 +17,4 @@ test/version_tmp
17
17
  tmp
18
18
  *log
19
19
  Vagrantfile
20
+ .vagrant
data/Rakefile CHANGED
@@ -18,6 +18,7 @@
18
18
  #
19
19
  ################################################################################
20
20
 
21
+ require 'rake/clean'
21
22
  require 'bundler/gem_tasks'
22
23
 
23
24
  ################################################################################
@@ -29,6 +30,17 @@ task :test => [:spec]
29
30
 
30
31
  ################################################################################
31
32
 
33
+ require 'cucumber/rake/task'
34
+ desc 'Run features'
35
+ Cucumber::Rake::Task.new(:features) do |t|
36
+ opts = "features --format pretty -x"
37
+ opts += " --tags #{ENV['TAGS']}" if ENV['TAGS']
38
+ t.cucumber_opts = opts
39
+ t.fork = false
40
+ end
41
+
42
+ ################################################################################
43
+
32
44
  require 'coveralls/rake/task'
33
45
  Coveralls::RakeTask.new
34
46
  task :coveralls => [:spec, 'coveralls:push']
data/bin/tl CHANGED
@@ -18,8 +18,6 @@
18
18
  # limitations under the License.
19
19
  #
20
20
  ################################################################################
21
- require 'socket'
22
-
23
21
  require 'gli'
24
22
  require 'testlab'
25
23
 
@@ -34,6 +32,8 @@ program_desc %(TestLab - A toolkit for building virtual computer labs)
34
32
  sort_help :manually
35
33
  default_command :help
36
34
 
35
+ preserve_argv true
36
+
37
37
  commands_from 'commands'
38
38
 
39
39
  desc 'Show verbose output'
@@ -44,9 +44,9 @@ desc 'Quiet mode'
44
44
  default_value false
45
45
  switch [:q, :quiet]
46
46
 
47
- desc 'Path to Labfile: ${PWD}/Labfile'
47
+ desc 'Path to Labfile: ${REPO}/Labfile'
48
48
  arg_name 'path/to/file'
49
- default_value File.join(Dir.pwd, 'Labfile')
49
+ # default_value File.join(Dir.pwd, 'Labfile')
50
50
  flag [:l, :labfile]
51
51
 
52
52
  desc 'Path to Repository directory: ${PWD}'
@@ -54,15 +54,15 @@ arg_name 'path/to/directory'
54
54
  default_value Dir.pwd
55
55
  flag [:r, :repo]
56
56
 
57
- desc 'Path to Configuration directory: ${PWD}/.testlab-$(hostname -s)'
57
+ desc 'Path to Configuration directory: ${REPO}/.testlab-$(hostname -s)'
58
58
  arg_name 'path/to/directory'
59
- default_value File.join(Dir.pwd, ".testlab-#{TestLab.hostname}")
59
+ # default_value File.join(Dir.pwd, ".testlab-#{TestLab.hostname}")
60
60
  flag [:c, :config]
61
61
 
62
62
  pre do |global,command,options,args|
63
63
  (global[:verbose] == true) and (ENV['LOG_LEVEL'] = 'DEBUG')
64
64
 
65
- log_file = File.join(Dir.pwd, "testlab-#{TestLab.hostname}.log")
65
+ log_file = File.join(global[:repo], "testlab-#{TestLab.hostname}.log")
66
66
  @logger = ZTK::Logger.new(log_file)
67
67
 
68
68
  @ui = ZTK::UI.new(
@@ -73,9 +73,9 @@ pre do |global,command,options,args|
73
73
 
74
74
  @testlab = TestLab.new(
75
75
  :ui => @ui,
76
- :labfile => global[:labfile],
77
- :config_dir => global[:config],
78
- :repo_dir => global[:repo]
76
+ :labfile_path => global[:labfile],
77
+ :config_dir => global[:config],
78
+ :repo_dir => global[:repo]
79
79
  )
80
80
 
81
81
  @ui.logger.debug { "global(#{global.inspect})" }
@@ -0,0 +1,35 @@
1
+ When /^I get the containers status with "([^"]*)"$/ do |app_name|
2
+ container_cmd(app_name, %W(status -n test-server))
3
+ end
4
+
5
+ When /^I get the containers ssh-config with "([^"]*)"$/ do |app_name|
6
+ container_cmd(app_name, %W(ssh-config -n test-server))
7
+ end
8
+
9
+ When /^I up the containers with "([^"]*)"$/ do |app_name|
10
+ container_cmd(app_name, %W(up -n test-server))
11
+ end
12
+
13
+ When /^I down the containers with "([^"]*)"$/ do |app_name|
14
+ container_cmd(app_name, %W(down -n test-server))
15
+ end
16
+
17
+ When /^I clone the containers with "([^"]*)"$/ do |app_name|
18
+ container_cmd(app_name, %W(clone -n test-server))
19
+ end
20
+
21
+ When /^I build the containers with "([^"]*)"$/ do |app_name|
22
+ container_cmd(app_name, %W(build -n test-server))
23
+ end
24
+
25
+ When /^I export the containers with "([^"]*)"$/ do |app_name|
26
+ container_cmd(app_name, %W(export -n test-server --output=/tmp/test-server.sc))
27
+ end
28
+
29
+ When /^I import the containers with "([^"]*)"$/ do |app_name|
30
+ container_cmd(app_name, %W(import -n test-server --input=/tmp/test-server.sc))
31
+ end
32
+
33
+ def container_cmd(app_name, *args)
34
+ testlab_cmd(app_name, [%(container), args].flatten)
35
+ end
@@ -0,0 +1,7 @@
1
+ When /^I get the networks status with "([^"]*)"$/ do |app_name|
2
+ network_cmd(app_name, %W(status -n labnet))
3
+ end
4
+
5
+ def network_cmd(app_name, *args)
6
+ testlab_cmd(app_name, [%(network), args].flatten)
7
+ end
@@ -0,0 +1,19 @@
1
+ When /^I get the nodes status with "([^"]*)"$/ do |app_name|
2
+ node_cmd(app_name, %W(status -n vagrant))
3
+ end
4
+
5
+ When /^I up the nodes with "([^"]*)"$/ do |app_name|
6
+ node_cmd(app_name, %W(up -n vagrant))
7
+ end
8
+
9
+ When /^I down the nodes with "([^"]*)"$/ do |app_name|
10
+ node_cmd(app_name, %W(down -n vagrant))
11
+ end
12
+
13
+ When /^I build the nodes with "([^"]*)"$/ do |app_name|
14
+ node_cmd(app_name, %W(build -n vagrant))
15
+ end
16
+
17
+ def node_cmd(app_name, *args)
18
+ testlab_cmd(app_name, [%(node), args].flatten)
19
+ end
@@ -0,0 +1,28 @@
1
+ When /^I get help for "([^"]*)"$/ do |app_name|
2
+ testlab_cmd(app_name, %W(help))
3
+ end
4
+
5
+ When /^I get the status with "([^"]*)"$/ do |app_name|
6
+ testlab_cmd(app_name, %W(status))
7
+ end
8
+
9
+ When /^I build the lab with "([^"]*)"$/ do |app_name|
10
+ testlab_cmd(app_name, %W(build))
11
+ end
12
+
13
+ When /^I up the lab with "([^"]*)"$/ do |app_name|
14
+ testlab_cmd(app_name, %W(up))
15
+ end
16
+
17
+ When /^I down the lab with "([^"]*)"$/ do |app_name|
18
+ testlab_cmd(app_name, %W(down))
19
+ end
20
+
21
+ When /^I destroy the lab with "([^"]*)"$/ do |app_name|
22
+ testlab_cmd(app_name, %W(destroy))
23
+ end
24
+
25
+ def testlab_cmd(app_name, *args)
26
+ args = args.join(' ')
27
+ step %(I run `#{app_name} --repo=#{TEST_REPO} #{args}`)
28
+ end
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env ruby
2
+ #^syntax detection
3
+
4
+ # Labfile Simple Example
5
+ #########################
6
+ # This is a simple example that builds a lab with a chef-server and chef-client.
7
+
8
+ # Here we define our node; in this case we are using the localhost. We've
9
+ # selected the Vagrant provider so the Vagrant VM will be our target LXC node.
10
+ node :vagrant do
11
+
12
+ # This sets the provider for our node. This could be many things, from
13
+ # Vagrant, to AWS, to Local, to custom internal providers. Any class can be
14
+ # supplied assuming it's interface honors our contract.
15
+ provider TestLab::Provider::Vagrant
16
+
17
+ # These are provisioners that only need to run against the node (i.e. they
18
+ # do not have any container components). Provisioner classes applied to
19
+ # containers only are automatically interogated and executed during runtime
20
+ # if they have a node component.
21
+ provisioners [
22
+ TestLab::Provisioner::Raring,
23
+ TestLab::Provisioner::Bind
24
+ ]
25
+
26
+ # this is a generic configuration object (hash) which is supplied to
27
+ # the provider
28
+ config ({
29
+ :vagrant => {
30
+ :id => "test-cucumber-#{TestLab.hostname}".downcase,
31
+ :cpus => ZTK::Parallel::MAX_FORKS.div(2), # use half of the available processors
32
+ :memory => ZTK::Parallel::MAX_MEMORY.div(3).div(1024 * 1024), # use a third of available RAM
33
+ :box => 'raring64',
34
+ :box_url => 'https://dl.dropboxusercontent.com/u/22904185/boxes/raring64.box',
35
+ :file => File.dirname(__FILE__)
36
+ },
37
+ :bind => {
38
+ :domain => "default.zone"
39
+ }
40
+ })
41
+
42
+ # Here we define our network segments; these will manifest into network
43
+ # bridges on the target LXC node.
44
+ network 'labnet' do
45
+ # provisioners [TestLab::Provisioner::Route]
46
+ address '10.128.0.1/16'
47
+ bridge :br0
48
+ end
49
+
50
+ # test-server.default.zone
51
+ ###########################
52
+ container "test-server" do
53
+ distro "ubuntu"
54
+ release "precise"
55
+
56
+ provisioners [
57
+ TestLab::Provisioner::Resolv,
58
+ TestLab::Provisioner::AptCacherNG,
59
+ TestLab::Provisioner::Apt
60
+ ]
61
+
62
+ # Here we define a default user to seed onto the box. This user will be
63
+ # given passwordless sudo access as well.
64
+ user 'deployer' do
65
+ password 'deployer'
66
+ identity File.join(ENV['HOME'], '.ssh', 'id_rsa')
67
+ public_identity File.join(ENV['HOME'], '.ssh', 'id_rsa.pub')
68
+ uid 2600
69
+ gid 2600
70
+ end
71
+
72
+ # Interfaces define what networks this container is linked to and what our
73
+ # configuration on that network should be. In the event we have more than
74
+ # one interface we should define one as the "primary". When multiple
75
+ # interfaces are at play, the default route for the container will be off
76
+ # the primary interface.
77
+ interface do
78
+ network_id 'labnet'
79
+ name :eth0
80
+ address '10.128.0.254/16'
81
+ mac '00:00:5e:63:b5:9f'
82
+ end
83
+ end
84
+
85
+ end
@@ -0,0 +1,18 @@
1
+ require 'aruba/cucumber'
2
+
3
+ ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
4
+ LIB_DIR = File.join(File.expand_path(File.dirname(__FILE__)),'..','..','lib')
5
+ TEST_REPO = File.dirname(__FILE__)
6
+
7
+ Before do
8
+ # Using "announce" causes massive warnings on 1.9.2
9
+ @aruba_timeout_seconds = 3600
10
+ @puts = true
11
+ @original_rubylib = ENV['RUBYLIB']
12
+ ENV['RUBYLIB'] = LIB_DIR + File::PATH_SEPARATOR + ENV['RUBYLIB'].to_s
13
+ # ENV['VAGRANT_HOME'] = File.join("", "tmp", ".vagrant.d")
14
+ end
15
+
16
+ After do
17
+ ENV['RUBYLIB'] = @original_rubylib
18
+ end
@@ -0,0 +1,64 @@
1
+ Feature: TestLab command-line
2
+
3
+ Background: TestLab status
4
+ When I get the status with "tl"
5
+ Then the exit status should be 0
6
+ When I get the nodes status with "tl"
7
+ Then the exit status should be 0
8
+ When I get the networks status with "tl"
9
+ Then the exit status should be 0
10
+ When I get the containers status with "tl"
11
+ Then the exit status should be 0
12
+ When I get the containers ssh-config with "tl"
13
+ Then the exit status should be 0
14
+
15
+
16
+ Scenario: TestLab help
17
+ When I get help for "tl"
18
+ Then the exit status should be 0
19
+
20
+
21
+ Scenario: TestLab export
22
+ When I build the lab with "tl"
23
+ Then the exit status should be 0
24
+ When I export the containers with "tl"
25
+ Then the exit status should be 0
26
+
27
+
28
+ Scenario: TestLab import
29
+ When I down the lab with "tl"
30
+ Then the exit status should be 0
31
+ When I build the nodes with "tl"
32
+ Then the exit status should be 0
33
+ When I import the containers with "tl"
34
+ Then the exit status should be 0
35
+ When I build the lab with "tl"
36
+ Then the exit status should be 0
37
+
38
+
39
+ Scenario: TestLab clone
40
+ When I down the lab with "tl"
41
+ Then the exit status should be 0
42
+ When I build the lab with "tl"
43
+ Then the exit status should be 0
44
+ When I clone the containers with "tl"
45
+ Then the exit status should be 0
46
+ When I build the containers with "tl"
47
+ Then the exit status should be 0
48
+ When I clone the containers with "tl"
49
+ Then the exit status should be 0
50
+ When I build the containers with "tl"
51
+ Then the exit status should be 0
52
+ When I down the containers with "tl"
53
+ Then the exit status should be 0
54
+ When I up the containers with "tl"
55
+ Then the exit status should be 0
56
+ When I build the containers with "tl"
57
+ Then the exit status should be 0
58
+
59
+
60
+ Scenario: TestLab destroy
61
+ When I down the lab with "tl"
62
+ Then the exit status should be 0
63
+ When I destroy the lab with "tl"
64
+ Then the exit status should be 0
@@ -24,25 +24,28 @@ desc 'Manage containers'
24
24
  arg_name 'Describe arguments to container here'
25
25
  command :container do |c|
26
26
 
27
- c.desc 'Container ID or Name'
28
- c.arg_name 'container'
27
+ c.desc 'Single or comma separated list of container IDs'
28
+ c.arg_name 'container[,container,...]'
29
29
  c.flag [:n, :name]
30
30
 
31
31
  # CONTAINER CREATE
32
32
  ###################
33
33
  c.desc 'Create a container'
34
34
  c.long_desc <<-EOF
35
- Create a container. The container is created.
35
+ Creates a container on the node the container belongs to.
36
36
  EOF
37
37
  c.command :create do |create|
38
38
  create.action do |global_options, options, args|
39
39
  if options[:name].nil?
40
40
  help_now!('a name is required') if options[:name].nil?
41
41
  else
42
- container = @testlab.containers.select{ |c| c.id.to_sym == options[:name].to_sym }.first
43
- container.nil? and raise TestLab::TestLabError, "We could not find the container you screateplied!"
42
+ names = options[:name].split(',')
43
+ containers = TestLab::Container.find(names)
44
+ (containers.nil? || (containers.count == 0)) and raise TestLab::TestLabError, "We could not find any of the containers you supplied!"
44
45
 
45
- container.create
46
+ containers.each do |container|
47
+ container.create
48
+ end
46
49
  end
47
50
  end
48
51
  end
@@ -51,17 +54,20 @@ EOF
51
54
  ####################
52
55
  c.desc 'Destroy a container'
53
56
  c.long_desc <<-EOF
54
- Destroy a container. The container is stopped and destroyed.
57
+ Destroys the container, force stopping it if necessary. The containers file system is purged from disk. This is a destructive operation, there is no way to recover from it.
55
58
  EOF
56
59
  c.command :destroy do |destroy|
57
60
  destroy.action do |global_options, options, args|
58
61
  if options[:name].nil?
59
62
  help_now!('a name is required') if options[:name].nil?
60
63
  else
61
- container = @testlab.containers.select{ |c| c.id.to_sym == options[:name].to_sym }.first
62
- container.nil? and raise TestLab::TestLabError, "We could not find the container you supplied!"
64
+ names = options[:name].split(',')
65
+ containers = TestLab::Container.find(names)
66
+ (containers.nil? || (containers.count == 0)) and raise TestLab::TestLabError, "We could not find any of the containers you supplied!"
63
67
 
64
- container.destroy
68
+ containers.each do |container|
69
+ container.destroy
70
+ end
65
71
  end
66
72
  end
67
73
  end
@@ -70,17 +76,20 @@ EOF
70
76
  ###############
71
77
  c.desc 'Up a container'
72
78
  c.long_desc <<-EOF
73
- Up a container. The container is started and brought online.
79
+ The container is started and brought online.
74
80
  EOF
75
81
  c.command :up do |up|
76
82
  up.action do |global_options, options, args|
77
83
  if options[:name].nil?
78
84
  help_now!('a name is required') if options[:name].nil?
79
85
  else
80
- container = @testlab.containers.select{ |c| c.id.to_sym == options[:name].to_sym }.first
81
- container.nil? and raise TestLab::TestLabError, "We could not find the container you supplied!"
86
+ names = options[:name].split(',')
87
+ containers = TestLab::Container.find(names)
88
+ (containers.nil? || (containers.count == 0)) and raise TestLab::TestLabError, "We could not find any of the containers you supplied!"
82
89
 
83
- container.up
90
+ containers.each do |container|
91
+ container.up
92
+ end
84
93
  end
85
94
  end
86
95
  end
@@ -89,17 +98,20 @@ EOF
89
98
  #################
90
99
  c.desc 'Down a container'
91
100
  c.long_desc <<-EOF
92
- Down a container. The container is stopped taking it offline.
101
+ The container is stopped taking it offline.
93
102
  EOF
94
103
  c.command :down do |down|
95
104
  down.action do |global_options, options, args|
96
105
  if options[:name].nil?
97
106
  help_now!('a name is required') if options[:name].nil?
98
107
  else
99
- container = @testlab.containers.select{ |c| c.id.to_sym == options[:name].to_sym }.first
100
- container.nil? and raise TestLab::TestLabError, "We could not find the container you supplied!"
108
+ names = options[:name].split(',')
109
+ containers = TestLab::Container.find(names)
110
+ (containers.nil? || (containers.count == 0)) and raise TestLab::TestLabError, "We could not find any of the containers you supplied!"
101
111
 
102
- container.down
112
+ containers.each do |container|
113
+ container.down
114
+ end
103
115
  end
104
116
  end
105
117
  end
@@ -108,17 +120,20 @@ EOF
108
120
  ####################
109
121
  c.desc 'Setup a container'
110
122
  c.long_desc <<-EOF
111
- Setup a container. The container is created, started and provisioned.
123
+ The container is provisioned.
112
124
  EOF
113
125
  c.command :setup do |setup|
114
126
  setup.action do |global_options, options, args|
115
127
  if options[:name].nil?
116
128
  help_now!('a name is required') if options[:name].nil?
117
129
  else
118
- container = @testlab.containers.select{ |c| c.id.to_sym == options[:name].to_sym }.first
119
- container.nil? and raise TestLab::TestLabError, "We could not find the container you supplied!"
130
+ names = options[:name].split(',')
131
+ containers = TestLab::Container.find(names)
132
+ (containers.nil? || (containers.count == 0)) and raise TestLab::TestLabError, "We could not find any of the containers you supplied!"
120
133
 
121
- container.setup
134
+ containers.each do |container|
135
+ container.setup
136
+ end
122
137
  end
123
138
  end
124
139
  end
@@ -127,17 +142,20 @@ EOF
127
142
  ####################
128
143
  c.desc 'Teardown a container'
129
144
  c.long_desc <<-EOF
130
- Teardown a container. The container is offlined and destroyed.
145
+ The container is deprovisioned.
131
146
  EOF
132
147
  c.command :teardown do |teardown|
133
148
  teardown.action do |global_options, options, args|
134
149
  if options[:name].nil?
135
150
  help_now!('a name is required') if options[:name].nil?
136
151
  else
137
- container = @testlab.containers.select{ |c| c.id.to_sym == options[:name].to_sym }.first
138
- container.nil? and raise TestLab::TestLabError, "We could not find the container you supplied!"
152
+ names = options[:name].split(',')
153
+ containers = TestLab::Container.find(names)
154
+ (containers.nil? || (containers.count == 0)) and raise TestLab::TestLabError, "We could not find any of the containers you supplied!"
139
155
 
140
- container.teardown
156
+ containers.each do |container|
157
+ container.teardown
158
+ end
141
159
  end
142
160
  end
143
161
  end
@@ -157,41 +175,47 @@ EOF
157
175
  if options[:name].nil?
158
176
  help_now!('a name is required') if options[:name].nil?
159
177
  else
160
- container = @testlab.containers.select{ |c| c.id.to_sym == options[:name].to_sym }.first
161
- container.nil? and raise TestLab::TestLabError, "We could not find the container you supplied!"
178
+ names = options[:name].split(',')
179
+ containers = TestLab::Container.find(names)
180
+ (containers.nil? || (containers.count == 0)) and raise TestLab::TestLabError, "We could not find any of the containers you supplied!"
162
181
 
163
- container.build
182
+ containers.each do |container|
183
+ container.build
184
+ end
164
185
  end
165
186
  end
166
187
  end
167
188
 
168
189
  # CONTAINER STATUS
169
190
  ###################
170
- c.desc 'Display the status of container(s)'
191
+ c.desc 'Display the status of containers'
171
192
  c.long_desc <<-EOF
172
- Displays the status of all containers or a single container if supplied via the ID parameter.
193
+ Displays the status of all containers or single/multiple containers if supplied via the ID parameter.
173
194
  EOF
174
195
  c.command :status do |status|
175
196
  status.action do |global_options, options, args|
197
+ containers = Array.new
198
+
176
199
  if options[:name].nil?
177
200
  # No ID supplied; show everything
178
- containers = @testlab.containers.delete_if{ |c| c.node.dead? }
179
- if containers.count == 0
180
- @testlab.ui.stderr.puts("You either have no containers defined or dead nodes!".yellow)
181
- else
182
- ZTK::Report.new(:ui => @testlab.ui).list(containers, TestLab::Container::STATUS_KEYS) do |container|
183
- OpenStruct.new(container.status)
184
- end
185
- end
201
+ containers = TestLab::Container.all
186
202
  else
187
- # ID supplied; show just that item
188
- container = @testlab.containers.select{ |c| c.id.to_sym == options[:name].to_sym }.first
189
- container.nil? and raise TestLab::TestLabError, "We could not find the container you supplied!"
203
+ # ID supplied; show just those items
204
+ names = options[:name].split(',')
205
+ containers = TestLab::Container.find(names)
206
+ (containers.nil? || (containers.count == 0)) and raise TestLab::TestLabError, "We could not find any of the containers you supplied!"
207
+ end
208
+
209
+ containers = containers.delete_if{ |container| container.node.dead? }
190
210
 
191
- ZTK::Report.new(:ui => @testlab.ui).list(container, TestLab::Container::STATUS_KEYS) do |container|
211
+ if (containers.count == 0)
212
+ @testlab.ui.stderr.puts("You either have no containers defined or dead nodes!".yellow)
213
+ else
214
+ ZTK::Report.new(:ui => @testlab.ui).list(containers, TestLab::Container::STATUS_KEYS) do |container|
192
215
  OpenStruct.new(container.status)
193
216
  end
194
217
  end
218
+
195
219
  end
196
220
  end
197
221
 
@@ -231,12 +255,17 @@ EOF
231
255
  c.command :'ssh-config' do |ssh_config|
232
256
 
233
257
  ssh_config.action do |global_options, options, args|
234
- help_now!('a name is required') if options[:name].nil?
235
-
236
- container = @testlab.containers.select{ |n| n.id.to_sym == options[:name].to_sym }.first
237
- container.nil? and raise TestLab::TestLabError, "We could not find the container you supplied!"
258
+ if options[:name].nil?
259
+ help_now!('a name is required') if options[:name].nil?
260
+ else
261
+ names = options[:name].split(',')
262
+ containers = TestLab::Container.find(names)
263
+ (containers.nil? || (containers.count == 0)) and raise TestLab::TestLabError, "We could not find any of the containers you supplied!"
238
264
 
239
- puts(container.ssh_config)
265
+ containers.each do |container|
266
+ puts(container.ssh_config)
267
+ end
268
+ end
240
269
  end
241
270
  end
242
271
 
@@ -255,16 +284,19 @@ EOF
255
284
  if options[:name].nil?
256
285
  help_now!('a name is required') if options[:name].nil?
257
286
  else
258
- container = @testlab.containers.select{ |c| c.id.to_sym == options[:name].to_sym }.first
259
- container.nil? and raise TestLab::TestLabError, "We could not find the container you supplied!"
260
-
261
- container.teardown
262
- container.down
263
- container.destroy
264
-
265
- container.create
266
- container.up
267
- container.setup
287
+ names = options[:name].split(',')
288
+ containers = TestLab::Container.find(names)
289
+ (containers.nil? || (containers.count == 0)) and raise TestLab::TestLabError, "We could not find any of the containers you supplied!"
290
+
291
+ containers.each do |container|
292
+ container.teardown
293
+ container.down
294
+ container.destroy
295
+
296
+ container.create
297
+ container.up
298
+ container.setup
299
+ end
268
300
  end
269
301
  end
270
302
  end
@@ -273,17 +305,20 @@ EOF
273
305
  ##################
274
306
  c.desc 'Clone a container'
275
307
  c.long_desc <<-EOF
276
- Clone a container. The container is offlined and an ephemeral copy of it is started.
308
+ An ephemeral copy of the container is started. There is a small delay incured during the first clone operation.
277
309
  EOF
278
310
  c.command :clone do |clone|
279
311
  clone.action do |global_options, options, args|
280
312
  if options[:name].nil?
281
313
  help_now!('a name is required') if options[:name].nil?
282
314
  else
283
- container = @testlab.containers.select{ |c| c.id.to_sym == options[:name].to_sym }.first
284
- container.nil? and raise TestLab::TestLabError, "We could not find the container you supplied!"
315
+ names = options[:name].split(',')
316
+ containers = TestLab::Container.find(names)
317
+ (containers.nil? || (containers.count == 0)) and raise TestLab::TestLabError, "We could not find any of the containers you supplied!"
285
318
 
286
- container.clone
319
+ containers.each do |container|
320
+ container.clone
321
+ end
287
322
  end
288
323
  end
289
324
  end
@@ -304,12 +339,17 @@ EOF
304
339
  export.flag [:output]
305
340
 
306
341
  export.action do |global_options, options, args|
307
- help_now!('a name is required') if options[:name].nil?
308
-
309
- container = @testlab.containers.select{ |n| n.id.to_sym == options[:name].to_sym }.first
310
- container.nil? and raise TestLab::TestLabError, "We could not find the container you supplied!"
342
+ if options[:name].nil?
343
+ help_now!('a name is required') if options[:name].nil?
344
+ else
345
+ names = options[:name].split(',')
346
+ containers = TestLab::Container.find(names)
347
+ (containers.nil? || (containers.count == 0)) and raise TestLab::TestLabError, "We could not find any of the containers you supplied!"
311
348
 
312
- container.export(options[:compression], options[:output])
349
+ containers.each do |container|
350
+ container.export(options[:compression], options[:output])
351
+ end
352
+ end
313
353
  end
314
354
  end
315
355
 
@@ -323,13 +363,18 @@ EOF
323
363
  import.flag [:input]
324
364
 
325
365
  import.action do |global_options, options, args|
326
- help_now!('a name is required') if options[:name].nil?
327
- help_now!('a filename is required') if options[:input].nil?
328
-
329
- container = @testlab.containers.select{ |n| n.id.to_sym == options[:name].to_sym }.first
330
- container.nil? and raise TestLab::TestLabError, "We could not find the container you supplied!"
366
+ if (options[:name].nil? || options[:input].nil?)
367
+ help_now!('a name is required') if options[:name].nil?
368
+ help_now!('a filename is required') if options[:input].nil?
369
+ else
370
+ names = options[:name].split(',')
371
+ containers = TestLab::Container.find(names)
372
+ (containers.nil? || (containers.count == 0)) and raise TestLab::TestLabError, "We could not find any of the containers you supplied!"
331
373
 
332
- container.import(options[:input])
374
+ containers.each do |container|
375
+ container.import(options[:input])
376
+ end
377
+ end
333
378
  end
334
379
  end
335
380
 
@@ -167,30 +167,34 @@ EOF
167
167
 
168
168
  # NETWORK STATUS
169
169
  #################
170
- c.desc 'Display the status of network(s)'
170
+ c.desc 'Display the status of networks'
171
171
  c.long_desc <<-EOF
172
- Displays the status of all networks or a single network if supplied via the ID parameter.
172
+ Displays the status of all networks or single/multiple networks if supplied via the ID parameter.
173
173
  EOF
174
174
  c.command :status do |status|
175
175
  status.action do |global_options, options, args|
176
176
  if options[:name].nil?
177
- # No ID supplied; show everything
178
- networks = @testlab.networks.delete_if{|n| n.node.dead? }
179
- if networks.count == 0
177
+ networks = Array.new
178
+
179
+ if options[:name].nil?
180
+ # No ID supplied; show everything
181
+ networks = TestLab::Network.all
182
+ else
183
+ # ID supplied; show just those items
184
+ names = options[:name].split(',')
185
+ networks = TestLab::Network.find(names)
186
+ (networks.nil? || (networks.count == 0)) and raise TestLab::TestLabError, "We could not find any of the networks you supplied!"
187
+ end
188
+
189
+ networks = networks.delete_if{ |network| network.node.dead? }
190
+
191
+ if (networks.count == 0)
180
192
  @testlab.ui.stderr.puts("You either have no networks defined or dead nodes!".yellow)
181
193
  else
182
194
  ZTK::Report.new(:ui => @testlab.ui).list(networks, TestLab::Network::STATUS_KEYS) do |network|
183
195
  OpenStruct.new(network.status)
184
196
  end
185
197
  end
186
- else
187
- # ID supplied; show just that item
188
- network = @testlab.networks.select{ |c| c.id.to_sym == options[:name].to_sym }.first
189
- network.nil? and raise TestLab::TestLabError, "We could not find the network you supplied!"
190
-
191
- ZTK::Report.new(:ui => @testlab.ui).list(network, TestLab::Network::STATUS_KEYS) do |network|
192
- OpenStruct.new(network.status)
193
- end
194
198
  end
195
199
  end
196
200
  end
@@ -12,6 +12,7 @@ class TestLab
12
12
  def create
13
13
  @ui.logger.debug { "Container Create: #{self.id} " }
14
14
 
15
+ (self.node.state == :not_created) and return false
15
16
  (self.lxc.state != :not_created) and return false
16
17
 
17
18
  please_wait(:ui => @ui, :message => format_object_action(self, 'Create', :green)) do
@@ -31,6 +32,7 @@ class TestLab
31
32
  def destroy
32
33
  @ui.logger.debug { "Container Destroy: #{self.id} " }
33
34
 
35
+ (self.node.state == :not_created) and return false
34
36
  (self.lxc.state == :not_created) and return false
35
37
 
36
38
  please_wait(:ui => @ui, :message => format_object_action(self, 'Destroy', :red)) do
@@ -49,6 +51,7 @@ class TestLab
49
51
  def up
50
52
  @ui.logger.debug { "Container Up: #{self.id} " }
51
53
 
54
+ (self.node.state != :running) and return false
52
55
  (self.lxc.state == :running) and return false
53
56
 
54
57
  please_wait(:ui => @ui, :message => format_object_action(self, 'Up', :green)) do
@@ -79,6 +82,7 @@ class TestLab
79
82
  def down
80
83
  @ui.logger.debug { "Container Down: #{self.id} " }
81
84
 
85
+ (self.node.state != :running) and return false
82
86
  (self.lxc.state != :running) and return false
83
87
 
84
88
  please_wait(:ui => @ui, :message => format_object_action(self, 'Down', :red)) do
@@ -18,7 +18,11 @@ class TestLab
18
18
  # execute. This is generally a bash script of some sort for example.
19
19
  # @return [String] The output of *lxc-attach*.
20
20
  def bootstrap(content)
21
- self.lxc.bootstrap(content)
21
+ if self.lxc_clone.exists?
22
+ self.ssh.bootstrap(content)
23
+ else
24
+ self.lxc.bootstrap(content)
25
+ end
22
26
  end
23
27
 
24
28
  # LXC::Container object
@@ -7,6 +7,7 @@ class TestLab
7
7
  def create
8
8
  @ui.logger.debug { "Network Create: #{self.id} " }
9
9
 
10
+ (self.node.state == :not_created) and return false
10
11
  (self.state != :not_created) and return false
11
12
 
12
13
  please_wait(:ui => @ui, :message => format_object_action(self, 'Create', :green)) do
@@ -20,6 +21,7 @@ class TestLab
20
21
  def destroy
21
22
  @ui.logger.debug { "Network Destroy: #{self.id} " }
22
23
 
24
+ (self.node.state == :not_created) and return false
23
25
  (self.state == :not_created) and return false
24
26
 
25
27
  please_wait(:ui => @ui, :message => format_object_action(self, 'Destroy', :red)) do
@@ -33,6 +35,7 @@ class TestLab
33
35
  def up
34
36
  @ui.logger.debug { "Network Up: #{self.id} " }
35
37
 
38
+ (self.node.state != :running) and return false
36
39
  (self.state == :running) and return false
37
40
 
38
41
  please_wait(:ui => @ui, :message => format_object_action(self, 'Up', :green)) do
@@ -46,6 +49,7 @@ class TestLab
46
49
  def down
47
50
  @ui.logger.debug { "Network Down: #{self.id} " }
48
51
 
52
+ (self.node.state != :running) and return false
49
53
  (self.state != :running) and return false
50
54
 
51
55
  please_wait(:ui => @ui, :message => format_object_action(self, 'Down', :red)) do
@@ -27,7 +27,8 @@ class TestLab
27
27
  "repo_dir" => testlab.repo_dir.inspect,
28
28
  "labfile_path" => testlab.labfile_path.inspect,
29
29
  "logdev" => testlab.ui.logger.logdev.inspect,
30
- "version" => TestLab::VERSION
30
+ "version" => TestLab::VERSION.inspect,
31
+ "argv" => ARGV.inspect
31
32
  }
32
33
  end
33
34
 
@@ -1,6 +1,6 @@
1
1
  class TestLab
2
2
  unless const_defined?(:VERSION)
3
3
  # TestLab Gem Version
4
- VERSION = "0.7.4"
4
+ VERSION = "0.7.5"
5
5
  end
6
6
  end
data/lib/testlab.rb CHANGED
@@ -108,13 +108,18 @@ class TestLab
108
108
  self.ui = (options[:ui] || ZTK::UI.new)
109
109
  self.class.ui = self.ui
110
110
 
111
- @config_dir = (options[:config_dir] || File.join(Dir.pwd, ".testlab-#{TestLab.hostname}"))
112
- @repo_dir = (options[:repo_dir] || Dir.pwd)
111
+ @repo_dir = File.expand_path(options[:repo_dir] || Dir.pwd)
112
+
113
+ @config_dir = File.expand_path(options[:config_dir] || File.join(@repo_dir, ".testlab-#{TestLab.hostname}"))
114
+ File.exists?(@config_dir) or FileUtils.mkdir_p(@config_dir)
115
+
116
+ labfile_path = (options[:labfile_path] || File.join(@repo_dir, 'Labfile'))
117
+ @labfile_path = File.expand_path(ZTK::Locator.find(labfile_path))
113
118
 
114
- labfile = (options[:labfile] || File.join(Dir.pwd, 'Labfile'))
115
- @labfile_path = ZTK::Locator.find(labfile)
116
119
  @labfile = TestLab::Labfile.load(labfile_path)
117
120
  @labfile.testlab = self
121
+
122
+ Dir.chdir(@repo_dir)
118
123
  end
119
124
 
120
125
  # Test Lab Nodes
@@ -23,7 +23,7 @@ describe TestLab::Container do
23
23
 
24
24
  subject {
25
25
  @ui = ZTK::UI.new(:stdout => StringIO.new, :stderr => StringIO.new)
26
- @testlab = TestLab.new(:labfile => LABFILE, :ui => @ui)
26
+ @testlab = TestLab.new(:labfile_path => LABFILE, :ui => @ui)
27
27
  @testlab.containers.first
28
28
  }
29
29
 
@@ -145,6 +145,7 @@ describe TestLab::Container do
145
145
 
146
146
  describe "#create" do
147
147
  it "should create the container" do
148
+ subject.node.stub(:state) { :running }
148
149
  subject.lxc.config.stub(:save) { true }
149
150
  subject.stub(:detect_arch) { "amd64" }
150
151
  subject.lxc.stub(:create) { true }
@@ -156,6 +157,7 @@ describe TestLab::Container do
156
157
 
157
158
  describe "#destroy" do
158
159
  it "should destroy the container" do
160
+ subject.node.stub(:state) { :running }
159
161
  subject.lxc.stub(:exists?) { true }
160
162
  subject.lxc.stub(:state) { :stopped }
161
163
  subject.lxc.stub(:destroy) { true }
@@ -166,6 +168,7 @@ describe TestLab::Container do
166
168
 
167
169
  describe "#up" do
168
170
  it "should up the container" do
171
+ subject.node.stub(:state) { :running }
169
172
  subject.lxc.stub(:exists?) { true }
170
173
  subject.lxc.stub(:start) { true }
171
174
  subject.lxc.stub(:wait) { true }
@@ -180,6 +183,7 @@ describe TestLab::Container do
180
183
 
181
184
  describe "#down" do
182
185
  it "should down the container" do
186
+ subject.node.stub(:state) { :running }
183
187
  subject.lxc.stub(:exists?) { true }
184
188
  subject.lxc.stub(:stop) { true }
185
189
  subject.lxc.stub(:wait) { true }
data/spec/network_spec.rb CHANGED
@@ -23,7 +23,7 @@ describe TestLab::Network do
23
23
 
24
24
  subject {
25
25
  @ui = ZTK::UI.new(:stdout => StringIO.new, :stderr => StringIO.new)
26
- @testlab = TestLab.new(:labfile => LABFILE, :ui => @ui)
26
+ @testlab = TestLab.new(:labfile_path => LABFILE, :ui => @ui)
27
27
  @testlab.networks.first
28
28
  }
29
29
 
@@ -108,6 +108,7 @@ describe TestLab::Network do
108
108
 
109
109
  describe "#create" do
110
110
  it "should create the network bridge" do
111
+ subject.node.stub(:state) { :running }
111
112
  subject.stub(:state) { :not_created }
112
113
  subject.node.ssh.stub(:exec) { true }
113
114
  subject.create
@@ -116,6 +117,7 @@ describe TestLab::Network do
116
117
 
117
118
  describe "#destroy" do
118
119
  it "should destroy the network bridge" do
120
+ subject.node.stub(:state) { :running }
119
121
  subject.stub(:state) { :stopped }
120
122
  subject.node.ssh.stub(:exec) { true }
121
123
  subject.destroy
@@ -124,6 +126,7 @@ describe TestLab::Network do
124
126
 
125
127
  describe "#up" do
126
128
  it "should online the network bridge" do
129
+ subject.node.stub(:state) { :running }
127
130
  subject.stub(:state) { :stopped }
128
131
  subject.node.ssh.stub(:exec) { true }
129
132
  subject.up
@@ -132,6 +135,7 @@ describe TestLab::Network do
132
135
 
133
136
  describe "#down" do
134
137
  it "should offline the network bridge" do
138
+ subject.node.stub(:state) { :running }
135
139
  subject.stub(:state) { :running }
136
140
  subject.node.ssh.stub(:exec) { true }
137
141
  subject.down
data/spec/node_spec.rb CHANGED
@@ -23,7 +23,7 @@ describe TestLab::Node do
23
23
 
24
24
  subject {
25
25
  @ui = ZTK::UI.new(:stdout => StringIO.new, :stderr => StringIO.new)
26
- @testlab = TestLab.new(:labfile => LABFILE, :ui => @ui)
26
+ @testlab = TestLab.new(:labfile_path => LABFILE, :ui => @ui)
27
27
  @testlab.nodes.first
28
28
  }
29
29
 
@@ -23,7 +23,7 @@ describe TestLab::Provisioner::Shell do
23
23
 
24
24
  subject {
25
25
  @ui = ZTK::UI.new(:stdout => StringIO.new, :stderr => StringIO.new)
26
- @testlab = TestLab.new(:labfile => LABFILE, :ui => @ui)
26
+ @testlab = TestLab.new(:labfile_path => LABFILE, :ui => @ui)
27
27
  TestLab::Container.first('server-shell')
28
28
  }
29
29
 
@@ -42,7 +42,9 @@ describe TestLab::Provisioner::Shell do
42
42
  it "should provision the container" do
43
43
  subject.node.ssh.stub(:file).and_yield(StringIO.new)
44
44
  subject.stub(:fs_root) { "/var/lib/lxc/#{subject.id}/rootfs" }
45
+ subject.ssh.stub(:bootstrap) { "" }
45
46
  subject.lxc.stub(:bootstrap) { "" }
47
+ subject.lxc_clone.stub(:exists?) { false }
46
48
 
47
49
  p = TestLab::Provisioner::Shell.new(subject.config, @ui)
48
50
  p.on_container_setup(subject)
@@ -53,7 +55,9 @@ describe TestLab::Provisioner::Shell do
53
55
  it "should raise an exception" do
54
56
  subject.node.ssh.stub(:file).and_yield(StringIO.new)
55
57
  subject.stub(:fs_root) { "/var/lib/lxc/#{subject.id}/rootfs" }
58
+ subject.ssh.stub(:bootstrap) { "" }
56
59
  subject.lxc.stub(:bootstrap) { "" }
60
+ subject.lxc_clone.stub(:exists?) { false }
57
61
 
58
62
  p = TestLab::Provisioner::Shell.new(Hash.new, @ui)
59
63
  lambda{ p.on_container_setup(subject) }.should raise_error TestLab::Provisioner::ShellError
data/spec/testlab_spec.rb CHANGED
@@ -23,7 +23,7 @@ describe TestLab do
23
23
 
24
24
  subject {
25
25
  @ui = ZTK::UI.new(:stdout => StringIO.new, :stderr => StringIO.new)
26
- @testlab = TestLab.new(:labfile => LABFILE, :ui => @ui)
26
+ @testlab = TestLab.new(:labfile_path => LABFILE, :ui => @ui)
27
27
  }
28
28
 
29
29
  describe "class" do
data/testlab.gemspec CHANGED
@@ -45,6 +45,7 @@ Gem::Specification.new do |spec|
45
45
  spec.add_development_dependency("pry")
46
46
  spec.add_development_dependency("rake")
47
47
  spec.add_development_dependency("redcarpet")
48
+ spec.add_development_dependency("aruba")
48
49
  spec.add_development_dependency("rspec")
49
50
  spec.add_development_dependency("yard")
50
51
  spec.add_development_dependency("coveralls")
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.7.4
4
+ version: 0.7.5
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-07-01 00:00:00.000000000 Z
12
+ date: 2013-07-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: gli
@@ -139,6 +139,22 @@ dependencies:
139
139
  - - ! '>='
140
140
  - !ruby/object:Gem::Version
141
141
  version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: aruba
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
142
158
  - !ruby/object:Gem::Dependency
143
159
  name: rspec
144
160
  requirement: !ruby/object:Gem::Requirement
@@ -206,6 +222,13 @@ files:
206
222
  - README.md
207
223
  - Rakefile
208
224
  - bin/tl
225
+ - features/step_definitions/container_steps.rb
226
+ - features/step_definitions/network_steps.rb
227
+ - features/step_definitions/node_steps.rb
228
+ - features/step_definitions/testlab_steps.rb
229
+ - features/support/Labfile
230
+ - features/support/env.rb
231
+ - features/testlab.feature
209
232
  - lib/commands/container.rb
210
233
  - lib/commands/network.rb
211
234
  - lib/commands/node.rb
@@ -303,7 +326,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
303
326
  version: '0'
304
327
  segments:
305
328
  - 0
306
- hash: -2914304308752783600
329
+ hash: -1054202206124374407
307
330
  required_rubygems_version: !ruby/object:Gem::Requirement
308
331
  none: false
309
332
  requirements:
@@ -312,7 +335,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
312
335
  version: '0'
313
336
  segments:
314
337
  - 0
315
- hash: -2914304308752783600
338
+ hash: -1054202206124374407
316
339
  requirements: []
317
340
  rubyforge_project:
318
341
  rubygems_version: 1.8.25
@@ -320,6 +343,13 @@ signing_key:
320
343
  specification_version: 3
321
344
  summary: A toolkit for building virtual computer labs
322
345
  test_files:
346
+ - features/step_definitions/container_steps.rb
347
+ - features/step_definitions/network_steps.rb
348
+ - features/step_definitions/node_steps.rb
349
+ - features/step_definitions/testlab_steps.rb
350
+ - features/support/Labfile
351
+ - features/support/env.rb
352
+ - features/testlab.feature
323
353
  - spec/container_spec.rb
324
354
  - spec/network_spec.rb
325
355
  - spec/node_spec.rb