cucumber-chef 1.0.3 → 2.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. data/.gitignore +8 -35
  2. data/.rspec +1 -0
  3. data/.rvmrc.template +2 -0
  4. data/.travis.yml +22 -0
  5. data/Gemfile +2 -15
  6. data/LICENSE +202 -201
  7. data/NOTICE +9 -0
  8. data/README.md +696 -92
  9. data/Rakefile +39 -25
  10. data/TODO.md +28 -0
  11. data/bin/cc-knife +32 -0
  12. data/bin/cucumber-chef +409 -79
  13. data/chef_repo/cookbooks/cucumber-chef/LICENSE +202 -0
  14. data/chef_repo/cookbooks/cucumber-chef/README.md +69 -0
  15. data/chef_repo/cookbooks/cucumber-chef/attributes/default.rb +27 -0
  16. data/chef_repo/cookbooks/cucumber-chef/metadata.rb +13 -0
  17. data/chef_repo/cookbooks/cucumber-chef/recipes/default.rb +23 -0
  18. data/chef_repo/cookbooks/cucumber-chef/recipes/lxc.rb +315 -0
  19. data/chef_repo/cookbooks/cucumber-chef/recipes/test_lab.rb +146 -0
  20. data/chef_repo/cookbooks/cucumber-chef/templates/default/db-168-192.erb +15 -0
  21. data/chef_repo/cookbooks/cucumber-chef/templates/default/db-test-lab.erb +15 -0
  22. data/chef_repo/cookbooks/cucumber-chef/templates/default/dhcpd-conf.erb +44 -0
  23. data/chef_repo/cookbooks/cucumber-chef/templates/default/gemrc.erb +10 -0
  24. data/{cookbooks/cucumber-chef/files/default/cucumber-net → chef_repo/cookbooks/cucumber-chef/templates/default/lxc-initializer-config.erb} +1 -1
  25. data/chef_repo/cookbooks/cucumber-chef/templates/default/lxc-install-chef.erb +3 -0
  26. data/chef_repo/cookbooks/cucumber-chef/templates/default/motd.erb +10 -0
  27. data/chef_repo/cookbooks/cucumber-chef/templates/default/named-conf-local.erb +25 -0
  28. data/{cookbooks/cucumber-chef/files/default/permissive-ssh-config → chef_repo/cookbooks/cucumber-chef/templates/default/ssh-config.erb} +3 -1
  29. data/chef_repo/roles/test_lab.rb +24 -0
  30. data/cucumber-chef.gemspec +50 -123
  31. data/examples/README.md +7 -0
  32. data/examples/users_add.feature +51 -0
  33. data/examples/users_auto_remove.feature +50 -0
  34. data/features/support/env.rb +3 -2
  35. data/lib/cucumber/chef/bootstrap.rb +94 -0
  36. data/lib/cucumber/chef/command.rb +78 -0
  37. data/lib/cucumber/chef/config.rb +143 -93
  38. data/lib/cucumber/chef/helpers/chef_client.rb +87 -0
  39. data/lib/cucumber/chef/helpers/chef_server.rb +90 -0
  40. data/lib/cucumber/chef/helpers/command.rb +57 -0
  41. data/lib/cucumber/chef/helpers/container.rb +154 -0
  42. data/lib/cucumber/chef/helpers/minitest.rb +35 -0
  43. data/lib/cucumber/chef/helpers/server.rb +81 -0
  44. data/lib/cucumber/chef/helpers/test_lab.rb +46 -0
  45. data/lib/cucumber/chef/helpers/utility.rb +73 -0
  46. data/lib/cucumber/chef/helpers.rb +56 -0
  47. data/lib/cucumber/chef/logger.rb +90 -0
  48. data/lib/cucumber/chef/provisioner.rb +275 -69
  49. data/lib/cucumber/chef/ssh.rb +190 -0
  50. data/lib/cucumber/chef/steps/chef_steps.rb +32 -0
  51. data/lib/cucumber/chef/steps/minitest_steps.rb +29 -0
  52. data/lib/cucumber/chef/steps/provision_steps.rb +60 -0
  53. data/lib/cucumber/chef/steps/ssh_steps.rb +95 -0
  54. data/lib/cucumber/chef/steps.rb +27 -0
  55. data/lib/cucumber/chef/tcp_socket.rb +83 -0
  56. data/lib/cucumber/chef/template.rb +57 -0
  57. data/lib/cucumber/chef/templates/bootstrap/ubuntu-precise-test-lab.erb +99 -0
  58. data/lib/cucumber/chef/templates/cucumber/env.rb +56 -0
  59. data/lib/cucumber/chef/templates/cucumber/example_feature.erb +49 -0
  60. data/lib/cucumber/chef/templates/cucumber/example_steps.erb +11 -0
  61. data/lib/cucumber/chef/templates/cucumber/readme-data_bags.erb +1 -0
  62. data/lib/cucumber/chef/templates/cucumber/readme-keys.erb +1 -0
  63. data/lib/cucumber/chef/templates/cucumber/readme-roles.erb +1 -0
  64. data/lib/cucumber/chef/templates/cucumber/readme.erb +18 -0
  65. data/lib/cucumber/chef/templates/cucumber-chef/config-rb.erb +33 -0
  66. data/lib/cucumber/chef/templates/cucumber-chef/cucumber-yml.erb +2 -0
  67. data/lib/cucumber/chef/templates/cucumber-chef/knife-rb.erb +18 -0
  68. data/lib/cucumber/chef/test_lab.rb +308 -52
  69. data/lib/cucumber/chef/test_runner.rb +86 -15
  70. data/lib/cucumber/chef/utility.rb +128 -0
  71. data/lib/cucumber/chef/version.rb +30 -1
  72. data/lib/cucumber/chef.rb +53 -20
  73. data/lib/cucumber-chef.rb +24 -1
  74. data/spec/cucumber/chef/config_spec.rb +144 -78
  75. data/spec/cucumber/chef/provisioner_spec.rb +60 -16
  76. data/spec/cucumber/chef/test_lab_spec.rb +62 -19
  77. data/spec/spec_helper.rb +30 -26
  78. data/todo.org +17 -0
  79. metadata +267 -163
  80. data/.document +0 -5
  81. data/VERSION +0 -1
  82. data/cookbooks/cucumber-chef/README.rdoc +0 -8
  83. data/cookbooks/cucumber-chef/files/default/add-git-identity +0 -2
  84. data/cookbooks/cucumber-chef/files/default/controller-first-boot +0 -1
  85. data/cookbooks/cucumber-chef/files/default/cucumber-private-key +0 -27
  86. data/cookbooks/cucumber-chef/files/default/cucumber-run_list +0 -1
  87. data/cookbooks/cucumber-chef/files/default/git-private-key +0 -27
  88. data/cookbooks/cucumber-chef/files/default/install-chef +0 -1
  89. data/cookbooks/cucumber-chef/files/default/lxc-controller-network-config +0 -5
  90. data/cookbooks/cucumber-chef/files/default/lxc-lucid-chef +0 -378
  91. data/cookbooks/cucumber-chef/metadata.rb +0 -6
  92. data/cookbooks/cucumber-chef/recipes/controller.rb +0 -51
  93. data/cookbooks/cucumber-chef/recipes/lxc.rb +0 -35
  94. data/cookbooks/cucumber-chef/recipes/test_lab.rb +0 -23
  95. data/cookbooks/cucumber-chef/recipes/testrunner.rb +0 -46
  96. data/cookbooks/cucumber-chef/roles/controller.rb +0 -7
  97. data/cookbooks/cucumber-chef/roles/test_lab_test.rb +0 -9
  98. data/cookbooks/cucumber-chef/templates/default/controller-client.erb +0 -5
  99. data/cookbooks/cucumber-chef/templates/default/lxc-lucid-chef +0 -385
  100. data/lib/cucumber/chef/handy.rb +0 -90
  101. data/lib/cucumber/chef/templates/controller.erb +0 -35
  102. data/lib/cucumber/chef/templates/env.rb +0 -16
  103. data/lib/cucumber/chef/templates/example_feature.erb +0 -11
  104. data/lib/cucumber/chef/templates/example_step.erb +0 -19
  105. data/lib/cucumber/chef/templates/readme.erb +0 -14
  106. data/lib/cucumber/chef/templates/ubuntu10.04-gems.erb +0 -43
  107. data/lib/cucumber/ec2_server_create.rb +0 -99
  108. data/website/website.html +0 -385
data/Rakefile CHANGED
@@ -1,35 +1,49 @@
1
- require 'rubygems'
2
- require 'bundler'
3
- Bundler.setup(:default, :development)
4
- require 'rake'
1
+ ################################################################################
2
+ #
3
+ # Author: Stephen Nelson-Smith <stephen@atalanta-systems.com>
4
+ # Author: Zachary Patten <zachary@jovelabs.com>
5
+ # Copyright: Copyright (c) 2011-2012 Atalanta Systems Ltd
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
+ ################################################################################
21
+
22
+ require 'bundler/gem_tasks'
23
+
24
+ ################################################################################
5
25
 
6
26
  require 'rspec/core/rake_task'
7
27
  RSpec::Core::RakeTask.new(:spec)
8
28
  task :default => :spec
29
+ task :test => :spec
30
+
31
+ ################################################################################
9
32
 
10
33
  require 'cucumber/rake/task'
11
34
  Cucumber::Rake::Task.new(:cucumber)
12
35
 
13
- require 'jeweler'
14
- Jeweler::Tasks.new do |gem|
15
- # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
16
- gem.name = "cucumber-chef"
17
- gem.homepage = "http://cucumber-chef.org"
18
- gem.license = "Apache v2"
19
- gem.summary = "Tests Chef-built infrastructure"
20
- gem.description = "Framework for behaviour-drive infrastructure development."
21
- gem.email = "stephen@atalanta-systems.com"
22
- gem.authors = ["Stephen Nelson-Smith"]
23
- gem.has_rdoc = false
24
- gem.bindir = "bin"
25
- gem.files = `git ls-files`.split("\n")
26
- gem.executables = %w(cucumber-chef)
27
- end
28
- Jeweler::RubygemsDotOrgTasks.new
36
+ ################################################################################
29
37
 
30
- require 'rcov/rcovtask'
31
- Rcov::RcovTask.new do |test|
32
- test.libs << 'test'
33
- test.pattern = 'test/**/test_*.rb'
34
- test.verbose = true
38
+ desc "Run RSpec with code coverage"
39
+ task :coverage do
40
+ `rake spec COVERAGE=true`
41
+ case RUBY_PLATFORM
42
+ when /darwin/
43
+ `open coverage/index.html`
44
+ when /linux/
45
+ `google-chrome coverage/index.html`
46
+ end
35
47
  end
48
+
49
+ ################################################################################
data/TODO.md ADDED
@@ -0,0 +1,28 @@
1
+
2
+ ## TODO
3
+
4
+ * Vagrant LXC Basebox
5
+ * Various OS baseboxes
6
+ * Make it easy to use OSC server
7
+ * Provide a libvirt capability
8
+ * Ship a release candidate
9
+ * Write test for cucumber-chef ssh
10
+ * Write tests for the stuff pulled from cucumber-nagios
11
+ * Make recipes food-critic compliant
12
+ * Fork ubuntu ami to 'amy'
13
+ * Merge Zach's changes
14
+ * Get cukes passing
15
+ * Improve cuke coverage to include all functionality described in the help task
16
+ * Plug into Relish
17
+ * Refactor ssh and connect into one ssh task that takes an optional container
18
+
19
+ ## DONE
20
+
21
+ * RC branch
22
+ * Go through the bugs
23
+
24
+ ## IDEAS
25
+
26
+ * Move from thor to mixlib cli?
27
+ * Make recipes use chef-solo?
28
+ * Make it easy to use chef-solo
data/bin/cc-knife ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+ require 'cucumber-chef'
3
+
4
+ $logger = Cucumber::Chef::Logger.new
5
+ $logger.level = (Cucumber::Chef.is_rc? ? Cucumber::Chef::Logger::DEBUG : Cucumber::Chef::Logger::INFO)
6
+
7
+ # if we have bundler binstubs use that; otherwise attempt to detect
8
+ knife = (Cucumber::Chef.locate(:file, "bin", "knife") rescue nil)
9
+ knife = "/usr/bin/env knife" unless knife
10
+
11
+ message = "cc-knife v#{Cucumber::Chef::VERSION}"
12
+ puts(message)
13
+ $logger.info { message }
14
+
15
+ Cucumber::Chef::Config.load
16
+ if (test_lab = Cucumber::Chef::TestLab.new) && (test_lab.labs_running.count > 0)
17
+
18
+ knife_rb = Cucumber::Chef.locate(:file, ".cucumber-chef", "knife.rb")
19
+ if File.exists?(knife_rb)
20
+ command = [knife, ARGV, "-s http://#{test_lab.labs_running.first.public_ip_address}:4000", "-c #{knife_rb}", "2>&1"].flatten.compact.join(" ")
21
+ puts(command)
22
+ puts(%x(#{command}))
23
+ exit($?.to_i)
24
+ else
25
+ puts("Could not find your Cucumber-Chef 'knife.rb'. Did you setup your test lab?")
26
+ exit(255)
27
+ end
28
+
29
+ else
30
+ puts("No running cucumber-chef test labs to connect to!")
31
+ exit(1)
32
+ end
data/bin/cucumber-chef CHANGED
@@ -1,6 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
- require 'pathname'
3
- require 'fileutils'
4
2
 
5
3
  require 'thor'
6
4
  require 'cucumber-chef'
@@ -9,115 +7,447 @@ class CucumberChef < Thor
9
7
  include Thor::Actions
10
8
 
11
9
  no_tasks do
12
- def create_directory_structure(project_dir)
13
- %w{step_definitions support}.each do |dir|
14
- FileUtils.mkdir_p(project_dir + "features" + dir)
10
+
11
+ def initalize_config
12
+ source_dir = File.expand_path(File.join(File.dirname(__FILE__), "..", "lib", "cucumber", "chef", "templates", "cucumber-chef"))
13
+ destination_dir = File.expand_path(File.join(Cucumber::Chef.locate_parent(".chef"), ".cucumber-chef"))
14
+ FileUtils.mkdir_p(destination_dir)
15
+
16
+ CucumberChef.source_root(source_dir)
17
+
18
+ get_aws_credentials
19
+
20
+ templates = {
21
+ "config-rb.erb" => "config.rb"
22
+ }
23
+
24
+ templates.each do |source, destination|
25
+ template(source, File.join(destination_dir, destination))
15
26
  end
27
+ puts
28
+ say "Ucanhaz Cucumber-Chef now! Rock on.", :green
16
29
  end
17
30
 
18
- def generate_project_skeleton(project_dir)
19
- template_dir = Pathname.new(__FILE__).parent.parent + 'lib' + 'cucumber' + 'chef' + 'templates'
20
- CucumberChef.source_root template_dir.realpath
31
+ def create_project(project)
32
+ @project = project
33
+ source_dir = File.expand_path(File.join(File.dirname(__FILE__), "..", "lib", "cucumber", "chef", "templates", "cucumber"))
34
+ destination_dir = Cucumber::Chef.locate_parent(".chef")
35
+
36
+ CucumberChef.source_root source_dir
21
37
  templates = {
22
- "readme.erb" => 'README',
23
- "example_feature.erb" => 'features/example.feature',
24
- "example_step.erb" => 'features/step_definitions/example_step.rb',
25
- "env.rb" => "features/support/env.rb"
38
+ "readme.erb" => "features/#{project}/README",
39
+ "example_feature.erb" => "features/#{project}/#{project}.feature",
40
+ "example_steps.erb" => "features/#{project}/step_definitions/#{project}_steps.rb",
41
+ "env.rb" => "features/support/env.rb",
42
+ "readme-data_bags.erb" => "features/support/data_bags/README",
43
+ "readme-roles.erb" => "features/support/roles/README",
44
+ "readme-keys.erb" => "features/support/keys/README"
26
45
  }
27
- templates.each do |filename, destination|
28
- template(filename, project_dir + destination)
46
+
47
+ templates.each do |source, destination|
48
+ template(source, File.join(destination_dir, destination))
29
49
  end
30
50
  end
31
51
 
32
- def config
33
- @config ||= begin
34
- options.test? ? Cucumber::Chef::Config.test_config : Cucumber::Chef::Config.new
52
+ def load_config
53
+
54
+ chef_repo = (Cucumber::Chef.locate_parent(".chef") rescue nil)
55
+ if ( !chef_repo || ( !File.exists?(chef_repo) || !File.directory?(chef_repo) ) )
56
+ fatal("Doesn't look like your inside a chef-repo! Please relocate to one and execute your command again!")
57
+ exit(255)
35
58
  end
59
+
60
+ $logger = Cucumber::Chef::Logger.new
61
+ $logger.level = (Cucumber::Chef.is_rc? ? Cucumber::Chef::Logger::DEBUG : Cucumber::Chef::Logger::INFO)
62
+
63
+ message = "cucumber-chef v#{Cucumber::Chef::VERSION}"
64
+ puts(set_color(message, :green, true))
65
+
66
+ $logger.info { "================================================================================" }
67
+ $logger.info { message }
68
+ $logger.info { "UNAME: %s" % %x( uname -a ).chomp.strip }
69
+ $logger.info { "Ruby Version: #{RUBY_VERSION}" }
70
+ $logger.info { "Ruby Patch Level: #{RUBY_PATCHLEVEL}" }
71
+ $logger.info { "Ruby Platform: #{RUBY_PLATFORM}" }
72
+ $logger.info { "Ruby Engine: #{RUBY_ENGINE}" }
73
+ $logger.info { "================================================================================" }
74
+
75
+ @options.test? ? Cucumber::Chef::Config.test : Cucumber::Chef::Config.load
76
+ end
77
+
78
+ def fatal(message)
79
+ puts(set_color(message, :red, :bold))
80
+ exit(255)
36
81
  end
37
82
 
38
- def error(message)
39
- warn message
40
- exit 255
83
+ def get_aws_credentials
84
+ say "OHAI!", :magenta
85
+ puts
86
+ say "Cucumber-Chef uses Amazon Web Services to build a test lab for automated infrastructure testing."
87
+ say "I need a few details before I can set up your test lab for you."
88
+ puts
89
+ say "We're going to use symmetric keys to authenticate with the AWS API."
90
+ say "First, I need your access key."
91
+ say "Your access key identifies you as you make API calls. It's not a secret."
92
+ say "You can find it under 'Access Credentials', on https://aws-portal.amazon.com/gp/aws/securityCredentials"
93
+ puts
94
+ @aws_access_key = ask "What is your AWS Access Key?", :bold
95
+ puts
96
+ say "Now I need your secret access key. This *is* a secret. The clue's in the name."
97
+ say "This is just a string of characters used to create the digital signature included in your API requests."
98
+ say "You can also find this under 'Access Credentials', on https://aws-portal.amazon.com/gp/aws/securityCredentials"
99
+ puts
100
+ @aws_secret_access_key = ask "What is your AWS Secret Access Key?", :bold
101
+ puts
102
+ say "Right. Now I need to know about the ssh key pair you want to use to connect to EC2 machines."
103
+ say "I need the name of the key pair. You can see this on the AWS management console, under Network & Security > Key Pairs"
104
+ puts
105
+ @aws_ssh_id = ask "What is your AWS Key Pair called?", :bold
106
+ puts
107
+ say "I also need to know what the ssh key is called - the actual name of the file on your local machine, eg #{@aws_ssh_id}.pem"
108
+ puts
109
+ @aws_ssh_key = ask "What's the filename of your ssh key?", :bold
110
+ puts
111
+ say "And, finally, I need to know where you keep it, on the file system. Often this is ~/.ssh"
112
+ puts
113
+ @aws_ssh_key_dir = ask "What directory contains your ssh key?", :bold
114
+ puts
115
+ say "OK, nearly there. AWS uses different keys depending on which region you use."
116
+ say "For example, 'us-east', 'us-west', or 'eu-west'"
117
+ puts
118
+ @region = ask "Which region are you using?", :bold
119
+ puts
120
+ say("One last thing. If your using librarian-chef, we want to be sure all the hooks are in place.")
121
+ puts
122
+ @librarian_chef = yes?("Does this chef-repo use librarian-chef?", :bold)
123
+ puts
124
+ say "Awesome. Thank you!"
125
+ puts
41
126
  end
127
+
42
128
  end
43
129
 
44
- desc "project <project name>" , "Create a project template for testing an infrastructure"
45
- def project(project_name)
46
- @project = project_name
47
- project_dir = Pathname.new(".") + "cucumber-chef" + @project
48
- create_directory_structure(project_dir)
49
- generate_project_skeleton(project_dir)
130
+ ################################################################################
131
+
132
+ desc "init", "Initalize cucumber-chef configuration"
133
+ def init
134
+ initalize_config
50
135
  end
51
136
 
52
- desc "setup", "Set up a cucumber-chef test lab in Amazon EC2"
53
- method_option :test, :type => :boolean
137
+ ################################################################################
138
+
139
+ desc "setup", "Setup cucumber-chef test lab in Amazon EC2"
140
+ method_option :test, :type => :boolean, :desc => "INTERNAL USE ONLY"
54
141
  def setup
55
- begin
56
- config.verify
57
- $stdout.sync
58
- provisioner = ::Cucumber::Chef::Provisioner.new
59
- server = provisioner.build_test_lab(config, $stdout)
60
- sleep(10)
61
- provisioner.upload_cookbook(config)
62
- provisioner.upload_role(config)
63
- provisioner.bootstrap_node(server.dns_name, config)
64
- rescue ::Cucumber::Chef::Error => err
65
- error(err.message)
142
+ load_config
143
+
144
+ puts
145
+ if (test_lab = Cucumber::Chef::TestLab.new)
146
+ if (server = test_lab.create)
147
+ if (provisioner = Cucumber::Chef::Provisioner.new(server))
148
+
149
+ provisioner.build
150
+
151
+ puts
152
+ puts("Your cucumber-chef test lab has now been provisioned!")
153
+ puts
154
+ puts("Be sure to log into the chef-server webui and change the default credentials.")
155
+ puts
156
+ puts(" Chef-Server WebUI:")
157
+ puts(" http://#{server.public_ip_address}:4040/")
158
+ puts(" Username:")
159
+ puts(" admin")
160
+ puts(" Password:")
161
+ puts(" #{Cucumber::Chef::Provisioner::PASSWORD}")
162
+
163
+ else
164
+ puts(set_color("Could not create the provisioner!", :red, true))
165
+ end
166
+ else
167
+ puts(set_color("Could not create the server!", :red, true))
168
+ end
169
+ else
170
+ puts(set_color("Could not create a new instance of test lab!", :red, true))
66
171
  end
172
+ puts
173
+
174
+ rescue Cucumber::Chef::Error => e
175
+ $logger.fatal { e.backtrace.join("\n") }
176
+ fatal(e.message)
67
177
  end
68
178
 
69
- desc "connect", "Connect to a container in your test lab"
70
- def connect
71
- puts "Not implemented. For now, find the IP of your test lab using the info task, and connect manually."
179
+ ################################################################################
180
+
181
+ desc "teardown", "Teardown cucumber-chef test lab in Amazon EC2"
182
+ method_option :test, :type => :boolean, :desc => "INTERNAL USE ONLY"
183
+ def teardown
184
+ load_config
185
+
186
+ puts
187
+ if (test_lab = Cucumber::Chef::TestLab.new)
188
+ if yes?(set_color("Are you sure you want to teardown your cucumber-chef test lab?", :red, true))
189
+ count_down_colors = { 5 => :green, 4 => :yellow, 3 => :yellow, 2 => :red, 1 => :red }
190
+ puts
191
+ puts(set_color("You have 5 seconds to abort!", :green, true))
192
+ puts
193
+ print(set_color("Self-destructing in", :green, true))
194
+ 5.downto(1) do |x|
195
+ print(set_color("...#{x}", count_down_colors[x], true))
196
+ sleep(1)
197
+ end
198
+ puts(set_color("...BOOM!", :red, true))
199
+ puts
200
+ test_lab.destroy
201
+ else
202
+ puts
203
+ puts(set_color("Whew! That was close!", :green, true))
204
+ end
205
+ else
206
+ puts(set_color("Could not find a cucumber-chef test lab to teardown!", :red, true))
207
+ end
208
+ puts
209
+
210
+ rescue Cucumber::Chef::Error => e
211
+ $logger.fatal { e.backtrace.join("\n") }
212
+ fatal(e)
72
213
  end
73
214
 
74
- desc "displayconfig", "Display the current config from knife.rb"
75
- method_option :test, :type => :boolean
76
- def displayconfig
77
- puts config.list.join("\n")
78
- config.verify
79
- rescue ::Cucumber::Chef::Error => err
80
- error(err.message)
215
+ ################################################################################
216
+
217
+ desc "up", "Startup the cucumber-chef test lab"
218
+ def up
219
+ load_config
220
+
221
+ puts
222
+ if (test_lab = Cucumber::Chef::TestLab.new)
223
+ test_lab.start
224
+ else
225
+ puts(set_color("Could not find a cucumber-chef test lab to startup!", :red, true))
226
+ end
227
+ puts
228
+
229
+ rescue Cucumber::Chef::Error => e
230
+ $logger.fatal { e.backtrace.join("\n") }
231
+ fatal(e)
81
232
  end
82
233
 
83
- desc "info", "Display information about the current test lab"
84
- method_option :test, :type => :boolean
85
- def info
86
- config.verify
87
- lab = Cucumber::Chef::TestLab.new(config)
88
- puts lab.info
89
- rescue ::Cucumber::Chef::Error => err
90
- error(err.message)
234
+ ################################################################################
235
+
236
+ desc "down", "Shutdown the cucumber-chef test lab"
237
+ def down
238
+ load_config
239
+
240
+ puts
241
+ if (test_lab = Cucumber::Chef::TestLab.new)
242
+ test_lab.stop
243
+ else
244
+ puts(set_color("Could not find a cucumber-chef test lab to shutdown!", :red, true))
245
+ end
246
+ puts
247
+
248
+ rescue Cucumber::Chef::Error => e
249
+ $logger.fatal { e.backtrace.join("\n") }
250
+ fatal(e)
251
+ end
252
+
253
+ ################################################################################
254
+
255
+ desc "ssh [container]", "SSH to cucumber-chef test lab or [container] if specified."
256
+ method_option :test, :type => :boolean, :desc => "INTERNAL USE ONLY"
257
+ def ssh(*args)
258
+ load_config
259
+
260
+ puts
261
+ if (test_lab = Cucumber::Chef::TestLab.new) && (test_lab.labs_running.count > 0)
262
+ ssh = Cucumber::Chef::SSH.new
263
+
264
+ if args.size == 0
265
+ ssh.config[:host] = test_lab.labs_running.first.public_ip_address
266
+ ssh.config[:ssh_user] = "ubuntu"
267
+ ssh.config[:identity_file] = Cucumber::Chef.locate(:file, ".cucumber-chef", "id_rsa-#{ssh.config[:ssh_user]}")
268
+
269
+ puts([set_color("Attempting SSH connection to cucumber-chef '", :blue, true), set_color("test lab", :cyan, true), set_color("'...", :blue, true)].join)
270
+ ssh.console
271
+ else
272
+ container = args[0]
273
+
274
+ ssh.config[:proxy] = true
275
+ ssh.config[:proxy_host] = test_lab.labs_running.first.public_ip_address
276
+ ssh.config[:proxy_ssh_user] = "ubuntu"
277
+ ssh.config[:proxy_identity_file] = Cucumber::Chef.locate(:file, ".cucumber-chef", "id_rsa-#{ssh.config[:proxy_ssh_user]}")
278
+
279
+ ssh.config[:host] = container
280
+ ssh.config[:ssh_user] = "root"
281
+ ssh.config[:identity_file] = Cucumber::Chef.locate(:file, ".cucumber-chef", "id_rsa-#{ssh.config[:proxy_ssh_user]}")
282
+
283
+ puts([set_color("Attempting SSH connection to cucumber-chef container '", :blue, true), set_color(container, :cyan, true), set_color("'...", :blue, true)].join)
284
+ ssh.console
285
+ end
286
+
287
+ else
288
+ puts(set_color("No cucumber-chef test labs available to ssh to!", :red, true))
289
+ end
290
+ puts
291
+
292
+ rescue Cucumber::Chef::Error => e
293
+ $logger.fatal { e.backtrace.join("\n") }
294
+ fatal(e)
295
+ end
296
+
297
+ ################################################################################
298
+
299
+ desc "diagnose <container>", "Provide diagnostics from the chef-client on the specified container."
300
+ method_option :strace, :type => :boolean, :desc => "output the chef-client 'chef-stacktrace.out'", :aliases => "-s", :default => true
301
+ method_option :log, :type => :boolean, :desc => "output the chef-client 'chef.log'", :aliases => "-l", :default => true
302
+ method_option :lines, :type => :numeric, :desc => "output the last N lines of the chef-client 'chef.log'", :aliases => "-n", :default => 1
303
+ def diagnose(container)
304
+ load_config
305
+
306
+ puts
307
+ if (test_lab = Cucumber::Chef::TestLab.new) && (test_lab.labs_running.count > 0)
308
+ ssh = Cucumber::Chef::SSH.new
309
+
310
+ ssh.config[:proxy] = true
311
+ ssh.config[:proxy_host] = test_lab.labs_running.first.public_ip_address
312
+ ssh.config[:proxy_ssh_user] = "ubuntu"
313
+ ssh.config[:proxy_identity_file] = Cucumber::Chef.locate(:file, ".cucumber-chef", "id_rsa-#{ssh.config[:proxy_ssh_user]}")
314
+
315
+ ssh.config[:host] = container
316
+ ssh.config[:ssh_user] = "root"
317
+ ssh.config[:identity_file] = Cucumber::Chef.locate(:file, ".cucumber-chef", "id_rsa-#{ssh.config[:proxy_ssh_user]}")
318
+
319
+ puts([set_color("Attempting to collect diagnostic information on cucumber-chef container '", :blue, true), set_color(container, :cyan, true), set_color("'...", :blue, true)].join)
320
+ if @options.strace?
321
+ puts
322
+ puts("chef-stacktrace.out:")
323
+ puts(set_color("============================================================================", :bold))
324
+ ssh.exec("[[ -e /var/chef/cache/chef-stacktrace.out ]] && cat /var/chef/cache/chef-stacktrace.out")
325
+ print("\n")
326
+ end
327
+ if @options.log?
328
+ puts
329
+ puts("chef.log:")
330
+ puts(set_color("============================================================================", :bold))
331
+ ssh.exec("[[ -e /var/log/chef/client.log ]] && tail -n #{@options.lines} /var/log/chef/client.log")
332
+ end
333
+ end
334
+ puts
335
+
336
+ rescue Cucumber::Chef::Error => e
337
+ $logger.fatal { e.backtrace.join("\n") }
338
+ fatal(e)
91
339
  end
92
340
 
93
- desc "destroy", "Destroy running test labs"
94
- method_option :test, :type => :boolean
95
- def destroy
96
- config.verify
97
- lab = Cucumber::Chef::TestLab.new(config)
98
- lab.destroy
341
+ ################################################################################
342
+
343
+ desc "displayconfig", "Display the current cucumber-chef config."
344
+ method_option :test, :type => :boolean, :desc => "INTERNAL USE ONLY"
345
+ def displayconfig
346
+ load_config
347
+
348
+ puts
349
+ say(Cucumber::Chef::Config.configuration.to_yaml, :bold)
350
+ puts
351
+
352
+ rescue Cucumber::Chef::Error => e
353
+ $logger.fatal { e.backtrace.join("\n") }
354
+ fatal(e.message)
99
355
  end
100
356
 
101
- desc "upload <project name>", "Upload a cucumber-chef test suite to the test lab platform"
102
- def upload(project_name)
103
- project_dir = Pathname.new(".") + "cucumber-chef" + project_name
104
- unless File.exists?(project_dir)
105
- raise "Project dir '#{project_dir}' does not exist."
357
+ ################################################################################
358
+
359
+ desc "ps [ps-options]", "Snapshot of the current cucumber-chef test lab container processes."
360
+ def ps(*args)
361
+ load_config
362
+
363
+ puts
364
+ if (test_lab = Cucumber::Chef::TestLab.new) && (test_lab.labs_running.count > 0)
365
+ ssh = Cucumber::Chef::SSH.new
366
+
367
+ ssh.config[:host] = test_lab.labs_running.first.public_ip_address
368
+ ssh.config[:ssh_user] = "ubuntu"
369
+ ssh.config[:identity_file] = Cucumber::Chef.locate(:file, ".cucumber-chef", "id_rsa-#{ssh.config[:ssh_user]}")
370
+
371
+ puts(set_color("Getting container processes from cucumber-chef test lab...", :blue, true))
372
+ puts
373
+ puts(set_color("============================================================================", :bold))
374
+ ssh.exec("lxc-ps --lxc -- #{args.join(" ")}")
375
+ print("\n")
106
376
  end
107
- config.verify
108
- runner = Cucumber::Chef::TestRunner.new(project_dir, config)
109
- runner.upload_project
377
+ puts
378
+
379
+ rescue Cucumber::Chef::Error => e
380
+ $logger.fatal { e.backtrace.join("\n") }
381
+ fatal(e)
110
382
  end
111
383
 
112
- desc "test", "Run a cucumber-chef test suite from a workstation."
113
- def test(project_name)
114
- project_dir = Pathname.new(".") + "cucumber-chef" + project_name
115
- unless File.exists?(project_dir)
116
- raise "Project dir '#{project_dir}' does not exist."
384
+ ################################################################################
385
+
386
+ desc "info", "Display information about the current test lab."
387
+ method_option :test, :type => :boolean, :desc => "INTERNAL USE ONLY"
388
+ def info
389
+ load_config
390
+
391
+ puts
392
+ if (test_lab = Cucumber::Chef::TestLab.new)
393
+ test_lab.info
117
394
  end
118
- config.verify
119
- runner = Cucumber::Chef::TestRunner.new(project_dir, config)
120
- runner.run
395
+ puts
396
+
397
+ rescue Cucumber::Chef::Error => e
398
+ $logger.fatal { e.backtrace.join("\n") }
399
+ fatal(e.message)
400
+ end
401
+
402
+ ################################################################################
403
+
404
+ desc "create <project>" , "Create a project template for testing an infrastructure."
405
+ def create(project)
406
+
407
+ create_project(project)
408
+ root_dir = Cucumber::Chef.locate_parent(".chef")
409
+ features_dir = File.join(root_dir, "features")
410
+ feature = File.join(features_dir, "#{project}.feature")
411
+ steps = File.join(features_dir, "step_definitions", "#{project}.steps")
412
+
413
+ puts
414
+ puts(set_color("Project created!", :green, true))
415
+ say("Please look at the README in '#{features_dir}/#{project}/', and the example features (#{File.basename(feature)}) and steps (#{File.basename(steps)}), which I have autogenerated for you.", :green)
416
+ puts
417
+
418
+ rescue Cucumber::Chef::Error => e
419
+ $logger.fatal { e.backtrace.join("\n") }
420
+ fatal(e)
121
421
  end
422
+
423
+ ################################################################################
424
+
425
+ desc "test [cucumber-options]", "Test a project using the cucumber-chef test suite."
426
+ method_option :destroy, :type => :boolean, :desc => "destroy all containers before test run", :aliases => "-z", :default => false
427
+ def test(*args)
428
+ load_config
429
+
430
+ puts
431
+ root_path = Cucumber::Chef.locate_parent(".cucumber-chef")
432
+ features_path = File.expand_path(File.join(root_path, "features"))
433
+
434
+ unless (File.exists?(features_path) && File.directory?(features_path))
435
+ raise Error, "Features directory '#{features_path}' does not exist."
436
+ else
437
+ puts("Using features directory: #{features_path}")
438
+ end
439
+
440
+ runner = Cucumber::Chef::TestRunner.new(features_path)
441
+ runner.run(@options.destroy?, args)
442
+ puts
443
+
444
+ rescue Cucumber::Chef::Error => e
445
+ $logger.fatal { e.backtrace.join("\n") }
446
+ fatal(e)
447
+ end
448
+
449
+ ################################################################################
450
+
122
451
  end
452
+
123
453
  CucumberChef.start