[chef] Re: Re: Re: Infrastructure Testing


Chronological Thread 
  • From: Sachin Sagar Rai < >
  • To:
  • Subject: [chef] Re: Re: Re: Infrastructure Testing
  • Date: Sat, 3 Mar 2012 17:04:26 +0545
  • Authentication-results: mr.google.com; spf=pass (google.com: domain of designates 10.68.231.133 as permitted sender) ; dkim=pass

@Jamie, that looks cool among the experimental chef testings attempt at all.
 
This one too looks promising

-------------------------------------------
@millisami
~ Sachin Sagar Rai
Ruby on Rails Developer
http://tfm.com.np
http://nepalonrails.tumblr.com
Sent with Sparrow

On Friday, March 2, 2012 at 9:57 PM, Jamie van Dyke wrote:

I decided to do something simple for now, and work on it as I go.  That way I have a better idea of requirements and how I will actually use it.  My thinking was simple, I want to boot something, ssh into it, run tests against the structure, and shut it down.  I don't want to boot/terminate it every run though, so I separate that piece.

So here's what I put together this morning, I installed rspec, and created the following folder/file structure:

spec
├── cookbooks
│   ├── build_essentials_spec.rb
│   └── generic_users_spec.rb
├── spec_helper.rb
└── support
    ├── matchers
    └── models
        └── host.rb

Here are the files:

spec_helper.rb

require 'bundler'
begin
  Bundler.setup
rescue Bundler::BundlerError => e
  $stderr.puts e.message
  $stderr.puts "Run `bundle install` to install missing gems"
  exit e.status_code
end

# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir.glob("./spec/support/**/*.rb").each {|f| require f}

RSpec.configure do |config|
  config.before(:all) do
    # Note: I use vagrant with chef-client provisioning, this is what it uses to run chef-client
    host.run_chef("chef-client -c /tmp/vagrant-chef-1/client.rb -j /tmp/vagrant-chef-1/dna.json")
  end
  
  config.after(:all) do
    host.close
  end
end


# start an instance to test
# run chef
# test the structure looks as we want it

# This represents the host we're running tests against
def host
  # I set up this hostname using vagrant ssh-config
  @host ||= Host.new 'cc.local', 'vagrant', :port => '2222'
end

host.rb

require 'net/ssh'

class Host
  def initialize(host, user, options={})
    @ssh = Net::SSH.start(host, user, options)
  end
  
  def close
    @ssh.close
  end
  
  def run_chef(chef_command="sudo chef-client")
    @ssh.exec! chef_command
  end
  
  def installed_packages
    @packages ||= @ssh.exec! 'dpkg --get-selections'
    @packages.split("\n").collect {|pkg| pkg.split(" ")[0] }
  end
  
  def users
    @users ||= @ssh.exec! 'cat /etc/passwd'
    @users.split("\n").collect {|u| u.split(":")[0] }
  end
end

Vagrantfile

Vagrant::Config.run do |config|
  config.vm.define :cc do |host|
    host.vm.box     = "cc.dev"
    host.vm.forward_port 80, 8080
    host.vm.forward_port 22, 2222
    host.vm.network :hostonly, "33.33.33.220"
    
    host.vm.provision :chef_client do |chef|
      # chef.log_level              = :debug
      chef.node_name            = "cc.dev"
      chef.chef_server_url        = "https://api.opscode.com/organizations/#{ENV['ORG']}"
      chef.environment            = "develop"
      chef.validation_client_name = ENV['validation_key_name']
      chef.validation_key_path    = ENV['my_key_location']
      
      chef.add_role 'base'
    end
  end
end

Rakefile

task :go       => ["vagrant:up", "spec"]
task :setup    => ["vagrant:up"]
task :teardown => ["vagrant:destroy", "knife:client_delete", "knife:node_delete"]

task :spec do
  sh 'bundle exec rspec spec/cookbooks/*.rb'
end

namespace :vagrant do
  task :up do
    safe_task do
      sh 'bundle exec vagrant up cc'
    end
  end
  
  task :destroy do
    safe_task do
      sh 'bundle exec vagrant destroy'
    end
  end
  
  task :provision do
    safe_task do
      sh 'bundle exec vagrant provision'
    end
  end
end

namespace :knife do
  task :client_delete do
    safe_task do
      sh 'bundle exec knife client delete cc.dev -y'
    end
  end
  
  task :node_delete do
    safe_task do
      sh 'bundle exec knife node delete cc.dev -y'
    end
  end
end

def safe_task(&block)
  begin
    yield
  rescue
    puts "Command failed.  Next!"
  end
end

Gemfile

source :rubygems

gem 'vagrant', '= 0.9.7'

# stop a clash between net-ssh dependencies (vagrant requires a higher version)
gem "chef",
  :ref => "ba4d58f4223"

gem 'rspec', '= 2.8.0'

Obviously this is the start of something, not a finished idea.  It's just to show whoever wants to try this out, how easy it actually is.  The Host class is the start of something that needs more thinking, or might just end up being replaced with something else.

Thoughts?

Cheers
Jamie van Dyke

On Mar 1, 2012, at 5:28 PM, Bryan Baugher wrote:

I had similar thoughts about these methods. I really hoped I could make cucumber-chef work but we don't use EC2 here.

So I wrote a vagrant cookbook which provides a vagrant resource to specify what kind of OS you want to use, what recipes/roles you want to run, and any additional configuration. I wrote it to support running chef-solo or chef-client, and it can even create an embedded chef-server to connect your instance to or you can specify a remote chef-server instance.

I also created an rspec cookbook which would allow you to write rspec tests inside your recipes.

The main idea is to create separate cookbooks or recipes which test the cookbook using vagrant/virtualbox to create a virtual host to install the cookbooks and then using RSpec to test the functionality during the chef run.

This project is still quite new, I don't actually have any real tests written at the moment but here is a sample idea. We also haven't made this project/cookbooks available publicly but I am hoping to shortly.

vagrant-test::default
{

vagrant "CENT55-32" do
  action :up
  recipes ["vagrant-test::beforeTest", "tomcat", "vagrant-test::afterTest"]
end

}

vagrant-test::beforeTest
{
require 'net/http'
 
rspec "Tomcat" do
  it "should not be running" do
    lambda {Net::HTTP.get_response(URI.parse('http://localhost:8080'))}.should raise_error
  end
end
}

vagrant-test::afterTest
{
require 'net/http'
 
rspec "Tomcat" do
  it "should connect successfully" do
    puts "Waiting for tomcat to start"
    sleep 10
    Net::HTTP.get_response(URI.parse('http://localhost:8080')).is_a?(Net::HTTPSuccess).should be(true)
  end
end
}

On Thu, Mar 1, 2012 at 11:13 AM, Jamie van Dyke < "> > wrote:
Hi all,

I've been beating myself up over the last few days trying to figure out the best way of testing our build automation.  I've played with cucumber-chef, minitest_handler, rspec-chef and more.  All of them were either broken in implementation or theory.  Here are my thoughts:

Cucumber-chef
-------------

I fought for 2 days with the gem trying to get it to work, and after multiple gem incompatibilities I thought I had it sorted.  Until I got to the part where it builds an EC2 instance, which then failed miserably as well.  In the end I dumped it because if it's this hard to get running initially, how can I trust it to continue to work in the future without another 2+ days worth of headaches.  In theory this was probably what I want, but over-complicated and brittle.

Rspec-chef
----------

I've possibly misunderstood the concept behind this one, but it looks like it's testing that I've written the correct pieces in my recipes rather than my infrastructure has turned out correctly after a run.

Minitest-Handler
----------------

There are 2 variants on this idea, 1 is strictly for chef-solo and the other is for chef-client.  I don't run chef-solo so I opted to try out the cookbook_file based method that was recently discussed on the mailing list (http://lists.opscode.com/sympa/arc/chef/2012-03/msg00000.html).  I'm struggling to understand why you'd follow this method.  Here's why:  A cookbook is generic and can be passed around between users/environments, for example the 'generic-users' cookbook.  To add a files/default/tests/minitest/moo_test.rb to this cookbook and upload it, means I've just made that test specific to my environment but included it within a generic cookbook.  It's completely possible that I've not grasped the methodology on this, but it seems fundamentally wrong because the 2 should be separate.

What I'm looking for is something as simple as this workflow, and if it comes to light that nobody else has done this and I need to write my won then I'm happy to do so:

1 - I write my infrastructure test, describing what change I am about to implement
2 - I run my test suite
 a - a vagrant/ec2/heroku/whatever instance is started
 b - chef-client is run on that server as I would expect in production
 c - my test suite looks to see if changes I want are in place
 d - it fails
3 - I write my cookbook changes to implement the small change I want
4 - is our test instance running?
 a - no, GOTO 2a
 b - yes, GOTO 2b
5 - did it pass?
 a - yes, GOTO 1
 b - no, GOTO 3


I would expect my test suite to simply describe what should and should not be in place, running, or open.  Have I completely missed the boat here and this already exists in a nice simple structure, or are we at the stage in Chef specific DevOps where we need to write something that fulfils this?

To be clear, I would expect such a tool to have a generic 'setup' step that isn't automatic and needs a little work.  Something like 'start a vagrant instance with the role X' or similar.  I would also expect to have to write my own 'teardown'.

Again, I must stress that I feel like this si such a simple thing that needs to be in place that I must have missed some vital information I need to get cracking.  Apologies if it comes across as whiney, I'm just frustrated and need to know if I embark on such a project, would I be re-inventing a wheel I missed somewhere.

Cheers
Jamie van Dyke



--
-Bryan





Archive powered by MHonArc 2.6.16.

§