lib/cucumber/chef/provisioner.rb in cucumber-chef-1.0.3 vs lib/cucumber/chef/provisioner.rb in cucumber-chef-2.0.0.pre

- old
+ new

@@ -1,92 +1,298 @@ -require "digest" +################################################################################ +# +# Author: Stephen Nelson-Smith <stephen@atalanta-systems.com> +# Author: Zachary Patten <zachary@jovelabs.com> +# Copyright: Copyright (c) 2011-2012 Atalanta Systems Ltd +# License: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +################################################################################ module Cucumber module Chef - class ProvisionerError < Error ; end + class ProvisionerError < Error; end + class Provisioner - def initialize - @cookbook_path = File.join(File.dirname(__FILE__), "../../../cookbooks/cucumber-chef") + attr_accessor :stdout, :stderr, :stdin + + HOSTNAME = "cucumber-chef.test-lab" + PASSWORD = "p@ssw0rd1" + +################################################################################ + + def initialize(server, stdout=STDOUT, stderr=STDERR, stdin=STDIN) + @server = server + @stdout, @stderr, @stdin = stdout, stderr, stdin + @stdout.sync = true if @stdout.respond_to?(:sync=) + + @ssh = Cucumber::Chef::SSH.new(@stdout, @stderr, @stdin) + @ssh.config[:host] = @server.public_ip_address + @ssh.config[:ssh_user] = "ubuntu" + @ssh.config[:identity_file] = Cucumber::Chef::Config[:aws][:identity_file] + + @command = Cucumber::Chef::Command.new(@stdout, @stderr, @stdin) + + @cookbooks_path = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "chef_repo", "cookbooks")) + @roles_path = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "chef_repo", "roles")) end - def bootstrap_node(dns_name, config) - template_file = File.join(File.dirname(__FILE__), "templates/ubuntu10.04-gems.erb") - bootstrap = ::Chef::Knife::Bootstrap.new - @stdout, @stderr, @stdout = StringIO.new, StringIO.new, StringIO.new - ui = ::Chef::Knife::UI.new(@stdout, @stderr, @stdout, bootstrap.config) - bootstrap.ui = ui - nodename = chef_node_name(config) - bootstrap.name_args = [dns_name] - bootstrap.config[:run_list] = "role[test_lab_test]" - bootstrap.config[:ssh_user] = "ubuntu" - bootstrap.config[:identity_file] = config[:knife][:identity_file] - bootstrap.config[:chef_node_name] = nodename - bootstrap.config[:use_sudo] = true - bootstrap.config[:template_file] = template_file - bootstrap.config[:validation_client_name] = config["validation_client_name"] - bootstrap.config[:validation_key] = config["validation_key"] - bootstrap.config[:chef_server_url] = config["chef_server_url"] - bootstrap.run - tag_node(config) +################################################################################ + + def build + template_file = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "lib", "cucumber", "chef", "templates", "bootstrap", "ubuntu-precise-test-lab.erb")) + + bootstrap(template_file) + wait_for_chef_server + + download_chef_credentials + render_knife_rb + + upload_cookbook + upload_role + tag_node + add_node_role + + chef_first_run + + download_proxy_ssh_credentials + + reboot_test_lab end - def build_controller(dns_name, config) - template_file = File.join(File.dirname(__FILE__), "templates/controller.erb") - bootstrap = ::Chef::Knife::Bootstrap.new - @stdout, @stderr, @stdout = StringIO.new, StringIO.new, StringIO.new - ui = ::Chef::Knife::UI.new(@stdout, @stderr, @stdout, bootstrap.config) - bootstrap.ui = ui - bootstrap.name_args = [dns_name] - bootstrap.config[:ssh_user] = "ubuntu" - bootstrap.config[:identity_file] = config[:knife][:identity_file] - bootstrap.config[:chef_node_name] = "cucumber-chef-controller" - bootstrap.config[:use_sudo] = true - bootstrap.config[:template_file] = template_file - bootstrap.config[:validation_client_name] = config["validation_client_name"] - bootstrap.config[:validation_key] = config["validation_key"] - bootstrap.config[:chef_server_url] = config["chef_server_url"] - bootstrap.run - bootstrap + +################################################################################ + private +################################################################################ + + def bootstrap(template_file) + raise ProvisionerError, "You must have the environment variable 'USER' set." if !Cucumber::Chef::Config[:user] + + @stdout.print("Bootstrapping AWS EC2 instance...") + Cucumber::Chef.spinner do + attributes = { + "run_list" => "role[test_lab]", + "cucumber_chef" => { + "version" => Cucumber::Chef::VERSION, + "prerelease" => Cucumber::Chef::Config[:prerelease] + } + } + + bootstrap = Cucumber::Chef::Bootstrap.new(@stdout, @stderr, @stdin) + bootstrap.config[:host] = @server.public_ip_address + bootstrap.config[:ssh_user] = "ubuntu" + bootstrap.config[:use_sudo] = true + bootstrap.config[:identity_file] = Cucumber::Chef::Config[:aws][:identity_file] + bootstrap.config[:template_file] = template_file + bootstrap.config[:context][:hostname] = HOSTNAME + bootstrap.config[:context][:chef_server] = HOSTNAME + bootstrap.config[:context][:amqp_password] = PASSWORD + bootstrap.config[:context][:admin_password] = PASSWORD + bootstrap.config[:context][:user] = Cucumber::Chef::Config[:user] + bootstrap.config[:context][:attributes] = attributes + bootstrap.run + end + @stdout.print("done.\n") end - def upload_cookbook(config) - version_loader = ::Chef::Cookbook::CookbookVersionLoader.new(@cookbook_path) - version_loader.load_cookbooks - uploader = ::Chef::CookbookUploader.new(version_loader.cookbook_version, - @cookbook_path) - uploader.upload_cookbook +################################################################################ + + def download_chef_credentials + @stdout.print("Downloading chef-server credentials...") + Cucumber::Chef.spinner do + local_path = Cucumber::Chef.locate(:directory, ".cucumber-chef") + remote_path = File.join("/", "home", @ssh.config[:ssh_user], ".chef") + + files = [ "#{Cucumber::Chef::Config[:user]}.pem", "validation.pem" ] + files.each do |file| + @ssh.download(File.join(remote_path, file), File.join(local_path, file)) + end + end + @stdout.print("done.\n") end - def upload_role(config) - role_path = File.join(@cookbook_path, "roles") - ::Chef::Config[:role_path] = role_path - role = ::Chef::Role.from_disk("test_lab_test") - role.save - role = ::Chef::Role.from_disk("controller") - role.save +################################################################################ + + def download_proxy_ssh_credentials + @stdout.print("Downloading container SSH credentials...") + Cucumber::Chef.spinner do + local_path = Cucumber::Chef.locate(:directory, ".cucumber-chef") + remote_path = File.join("/", "home", @ssh.config[:ssh_user], ".ssh") + + files = { "id_rsa" => "id_rsa-ubuntu" } + files.each do |remote_file, local_file| + local = File.join(local_path, local_file) + @ssh.download(File.join(remote_path, remote_file), local) + File.chmod(0600, local) + end + end + @stdout.print("done.\n") end - - def build_test_lab(config, output) - TestLab.new(config).build(output) + +################################################################################ + + def render_knife_rb + @stdout.print("Building 'cc-knife' configuration...") + Cucumber::Chef.spinner do + template_file = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "lib", "cucumber", "chef", "templates", "cucumber-chef", "knife-rb.erb")) + knife_rb = File.expand_path(File.join(Cucumber::Chef.locate(:directory, ".cucumber-chef"), "knife.rb")) + + context = { + :chef_server => @server.public_ip_address, + :librarian_chef => Cucumber::Chef::Config[:librarian_chef], + :user => Cucumber::Chef::Config[:user] + } + + File.open(knife_rb, 'w') do |f| + f.puts(Cucumber::Chef::Template.render(template_file, context)) + end + end + @stdout.print("done.\n") end - def tag_node(config) - node = ::Chef::Node.load(chef_node_name) - node.tags << (config.test_mode? ? 'test' : 'user') - node.save +################################################################################ + + def upload_cookbook + $logger.debug { "Uploading cucumber-chef cookbooks..." } + @stdout.print("Uploading cucumber-chef cookbooks...") + + Cucumber::Chef.spinner do + Cucumber::Chef.load_knife_config + cookbook_repo = ::Chef::CookbookLoader.new(@cookbooks_path) + cookbook_repo.each do |name, cookbook| + $logger.debug { "::Chef::CookbookUploader(#{name}) ATTEMPT" } + ::Chef::CookbookUploader.new(cookbook, @cookbooks_path, :force => true).upload_cookbook + $logger.debug { "::Chef::CookbookUploader(#{name}) UPLOADED" } + end + #@command.knife([ "cookbook upload cucumber-chef", "-o", @cookbooks_path ], :silence => true) + end + + @stdout.print("done.\n") + $logger.debug { "Successfully uploaded cucumber-chef test lab cookbooks." } end - - private - - def chef_node_name(config=nil) - @node_name ||= begin - if config.test_mode? - "cucumber-chef-#{Digest::SHA1.hexdigest(Time.now.to_s)[0..7]}" - else - "cucumber-chef-test-lab" + +################################################################################ + + def upload_role + $logger.debug { "Uploading cucumber-chef test lab role..." } + @stdout.print("Uploading cucumber-chef test lab role...") + + Cucumber::Chef.spinner do + Cucumber::Chef.load_knife_config + ::Chef::Config[:role_path] = @roles_path + [ "test_lab" ].each do |name| + role = ::Chef::Role.from_disk(name) + role.save end + #@command.knife([ "role from file", File.join(@roles_path, "test_lab.rb") ], :silence => true) end + + @stdout.print("done.\n") + $logger.debug { "Successfully uploaded cucumber-chef test lab roles."} end + +################################################################################ + + def tag_node + $logger.debug { "Tagging cucumber-chef test lab node..." } + @stdout.print("Tagging cucumber-chef test lab node...") + + Cucumber::Chef.spinner do + Cucumber::Chef.load_knife_config + node = ::Chef::Node.load(HOSTNAME) + [ Cucumber::Chef::Config[:mode].to_s, Cucumber::Chef::Config[:user].to_s ].each do |tag| + node.tags << tag + node.save + end + #@command.knife([ "tag create", HOSTNAME, Cucumber::Chef::Config[:mode] ], :silence => true) + end + + @stdout.print("done.\n") + $logger.debug { "Successfully tagged cucumber-chef test lab node."} + end + +################################################################################ + + def add_node_role + $logger.debug { "Setting up cucumber-chef test lab run list..." } + @stdout.print("Setting up cucumber-chef test lab run list...") + + Cucumber::Chef.spinner do + Cucumber::Chef.load_knife_config + node = ::Chef::Node.load(HOSTNAME) + [ "role[test_lab]" ].each do |entry| + node.run_list << entry + end + node.save + #@command.knife([ "node run_list add", HOSTNAME, "\"role[test_lab]\"" ], :silence => true) + end + + $logger.debug { "Successfully added roles to cucumber-chef test lab."} + @stdout.print("done.\n") + end + +################################################################################ + + def chef_first_run + @stdout.print("Performing chef-client run to setup and configure the cucumber-chef test lab...") + Cucumber::Chef.spinner do + command = "/usr/bin/chef-client -j /etc/chef/first-boot.json -l debug" + command = "sudo #{command}" + @ssh.exec(command, :silence => true) + end + @stdout.print("done.\n") + end + +################################################################################ + + def wait_for_chef_server + @stdout.print("Waiting for Chef-Server...") + Cucumber::Chef.spinner do + Cucumber::Chef::TCPSocket.new(@server.public_ip_address, 4000, "GET").wait + end + @stdout.puts("done.\n") + + @stdout.print("Waiting for Chef-WebUI...") + Cucumber::Chef.spinner do + Cucumber::Chef::TCPSocket.new(@server.public_ip_address, 4040, "GET").wait + end + @stdout.puts("done.\n") + end + +################################################################################ + + def reboot_test_lab + @stdout.print("Rebooting test lab; please wait...") + Cucumber::Chef.spinner do + command = "sudo reboot" + @ssh.exec(command, :silence => true) + sleep(10) + end + @stdout.print("done.\n") + + @stdout.print("Waiting for SSHD...") + Cucumber::Chef.spinner do + Cucumber::Chef::TCPSocket.new(@server.public_ip_address, 22).wait + end + @stdout.puts("done.\n") + + wait_for_chef_server + end + end + end end + +################################################################################