From 47bd72f2548814cf50fa4055663068b8152f4474 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Tue, 9 Aug 2016 09:56:32 -0700 Subject: [PATCH 1/2] Adding support for machine data generation * init sub command will now generate json file containing machine data at .vagrantspec_machine_data. This will be helpful for using orchestration tooling aside from ansible. * adding license info * Updating the README.md --- .gitignore | 1 + README.md | 12 ++++- lib/vagrant_spec/command/init.rb | 17 +++++-- lib/vagrant_spec/config.rb | 7 +-- lib/vagrant_spec/config/base.rb | 15 ++++-- lib/vagrant_spec/machine_data.rb | 34 +++++++++++++ lib/vagrant_spec/machine_finder.rb | 7 +++ lib/vagrant_spec/templates/spec_helper.erb | 2 +- scripts/poor_mans_smoke_test.sh | 1 + .../command_spec/init_spec.rb | 32 ++++++++++-- .../vagrant_spec_test/machine_data_spec.rb | 51 +++++++++++++++++++ vagrant_spec.gemspec | 1 + 12 files changed, 163 insertions(+), 17 deletions(-) create mode 100644 lib/vagrant_spec/machine_data.rb create mode 100644 spec/unit/vagrant_spec_test/machine_data_spec.rb diff --git a/.gitignore b/.gitignore index 96c9c68..76af88f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .vagrant Gemfile.lock vagrantspec_inventory +.vagrantspec_machine_data serverspec/spec_helper.rb coverage pkg diff --git a/README.md b/README.md index 306b9e6..062d50a 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # vagrant_spec +[![Gem Version](https://badge.fury.io/rb/vagrant_spec.svg)](https://badge.fury.io/rb/vagrant_spec) [![Build Status](https://travis-ci.org/miroswan/vagrant_spec.svg?branch=master)](https://travis-ci.org/miroswan/vagrant_spec) [![Coverage Status](https://coveralls.io/repos/github/miroswan/vagrant_spec/badge.svg?branch=master)](https://coveralls.io/github/miroswan/vagrant_spec?branch=master) -[![Gem Version](https://badge.fury.io/rb/vagrant_spec.svg)](https://badge.fury.io/rb/vagrant_spec) + Vagrant Spec is a Vagrant plugin that makes integration testing for deployments to clustered systems a breeze. It also separates the build and deployment steps @@ -71,7 +72,14 @@ defaults to serverspec. You can specify as many groups as you need. You can match nodes by regular expression or explicitly provide an array of node names. This will generate an vagrantspec_inventory based on your active nodes. You use this file for -running ansible playbooks against your Vagrant instances. +running ansible playbooks against your Vagrant instances. Use this configuration +directive if you use ansible for orchestration. + +* config.spec.generate_machine_data: a boolean. If true, the init sub-command +will generate a json file at .vagrantspec_machine_data containing relevant +ssh information for each of your nodes. This can be helpful when leveraging +orchestration tooling aside from ansible. You can use this data to direct your +orchestration to your local instances. The default is set to true. * config.spec.test_plan: an array of hashes. nodes can either be a regular expression object that matches your desired nodes or an explicit array of diff --git a/lib/vagrant_spec/command/init.rb b/lib/vagrant_spec/command/init.rb index 2a6ced8..e6ef7d3 100644 --- a/lib/vagrant_spec/command/init.rb +++ b/lib/vagrant_spec/command/init.rb @@ -2,6 +2,7 @@ require 'vagrant_spec/ansible_inventory' require 'vagrant_spec/spec_helper' +require 'vagrant_spec/machine_data' require 'vagrant_spec/config' module VagrantSpec @@ -15,12 +16,17 @@ class Init < Vagrant.plugin(2, :command) DEFAULTS = VagrantSpec::Config::DEFAULTS - attr_accessor :config, :directory, :ansible_inventory + attr_accessor :config + attr_accessor :directory + attr_accessor :ansible_inventory + attr_accessor :generate_machine_data + def initialize(argv, env) super - @config = VagrantSpec::Config.load env - @directory = @config.spec.directory - @ansible_inventory = @config.spec.ansible_inventory + @config = VagrantSpec::Config.load env + @directory = @config.spec.directory + @ansible_inventory = @config.spec.ansible_inventory + @generate_machine_data = @config.spec.generate_machine_data end def execute @@ -29,6 +35,9 @@ def execute unless @ansible_inventory == DEFAULTS['ansible_inventory'] VagrantSpec::AnsibleInventory.new(@env).generate end + if @generate_machine_data == DEFAULTS['generate_machine_data'] + VagrantSpec::MachineData.new(@env).generate + end end def parse_opts diff --git a/lib/vagrant_spec/config.rb b/lib/vagrant_spec/config.rb index e92a13f..2a2e74b 100644 --- a/lib/vagrant_spec/config.rb +++ b/lib/vagrant_spec/config.rb @@ -6,9 +6,10 @@ module Config autoload :Base, 'vagrant_spec/config/base' DEFAULTS = { - 'directory' => 'serverspec', - 'ansible_inventory' => {}, - 'test_plan' => [] + 'directory' => 'serverspec', + 'ansible_inventory' => {}, + 'test_plan' => [], + 'generate_machine_data' => true }.freeze class << self diff --git a/lib/vagrant_spec/config/base.rb b/lib/vagrant_spec/config/base.rb index 84ad39c..f3e5430 100644 --- a/lib/vagrant_spec/config/base.rb +++ b/lib/vagrant_spec/config/base.rb @@ -11,11 +11,13 @@ class Base < Vagrant.plugin(2, :config) attr_accessor :directory attr_accessor :ansible_inventory attr_accessor :test_plan + attr_accessor :generate_machine_data def initialize - @directory = UNSET_VALUE - @ansible_inventory = UNSET_VALUE - @test_plan = UNSET_VALUE + @directory = UNSET_VALUE + @ansible_inventory = UNSET_VALUE + @test_plan = UNSET_VALUE + @generate_machine_data = UNSET_VALUE end def final_directory @@ -32,10 +34,17 @@ def final_test_plan @test_plan = DEFAULTS['test_plan'] if @test_plan == UNSET_VALUE end + def final_generate_machine_data + if @generate_machine_data == UNSET_VALUE + @generate_machine_data = DEFAULTS['generate_machine_data'] + end + end + def finalize! final_directory final_ansible_inventory final_test_plan + final_generate_machine_data end end end diff --git a/lib/vagrant_spec/machine_data.rb b/lib/vagrant_spec/machine_data.rb new file mode 100644 index 0000000..84cfcae --- /dev/null +++ b/lib/vagrant_spec/machine_data.rb @@ -0,0 +1,34 @@ +# encoding: UTF-8 + +require 'json' +require 'vagrant_spec/machine_finder' + +module VagrantSpec + # Handle machine data generation + class MachineData + attr_accessor :env, :m_finder, :data + def initialize(env) + @env = env + @m_finder = VagrantSpec::MachineFinder.new(@env) + @data = [] + end + + def generate + populate_data + IO.write('.vagrantspec_machine_data', JSON.pretty_generate(@data)) + end + + def populate_data + @m_finder.machines do |m| + private_key = m.ssh_info[:private_key_path][0] + @data << { + name: m.name, + host: m.ssh_info[:host], + port: m.ssh_info[:port], + username: m.ssh_info[:username], + private_key: private_key + } + end + end + end +end diff --git a/lib/vagrant_spec/machine_finder.rb b/lib/vagrant_spec/machine_finder.rb index b3ee78e..c1fc117 100644 --- a/lib/vagrant_spec/machine_finder.rb +++ b/lib/vagrant_spec/machine_finder.rb @@ -20,6 +20,13 @@ def machine(name) nil end + # Yield active machines + # + # yield [Vagrant::Machine] + def machines + @env.active_machines.each { |m| yield @env.machine(*m) } + end + # reg [Regexp] # # return [Array] diff --git a/lib/vagrant_spec/templates/spec_helper.erb b/lib/vagrant_spec/templates/spec_helper.erb index a8deb23..af44b2f 100644 --- a/lib/vagrant_spec/templates/spec_helper.erb +++ b/lib/vagrant_spec/templates/spec_helper.erb @@ -16,4 +16,4 @@ options[:keys] = ENV['VAGRANT_KEY'] options[:port] = ENV['VAGRANT_PORT'] set :host, host -set :ssh_options, options \ No newline at end of file +set :ssh_options, options diff --git a/scripts/poor_mans_smoke_test.sh b/scripts/poor_mans_smoke_test.sh index 6554e56..2cc911a 100755 --- a/scripts/poor_mans_smoke_test.sh +++ b/scripts/poor_mans_smoke_test.sh @@ -13,6 +13,7 @@ bundle exec vagrant spec init -h bundle exec vagrant spec test -h bundle exec vagrant spec no_command -h rm -f serverspec/spec_helper.rb +rm -f .vagrantspec_machine_data bundle exec vagrant up bundle exec vagrant spec init ansible-playbook site.yml -i vagrantspec_inventory diff --git a/spec/unit/vagrant_spec_test/command_spec/init_spec.rb b/spec/unit/vagrant_spec_test/command_spec/init_spec.rb index d5feffa..e6f0cb2 100644 --- a/spec/unit/vagrant_spec_test/command_spec/init_spec.rb +++ b/spec/unit/vagrant_spec_test/command_spec/init_spec.rb @@ -15,8 +15,13 @@ double(VagrantSpec::AnsibleInventory) end + let(:mock_machine_data) do + double(VagrantSpec::MachineData) + end + before do allow(mock_spec).to receive(:ansible_inventory) { { 'all' => /node/ } } + allow(mock_spec).to receive(:generate_machine_data) end subject { VagrantSpec::Command::Init.new([], iso_env) } @@ -29,8 +34,10 @@ def execute_proc proc do allow_any_instance_of(VagrantSpec::SpecHelper).to receive(:generate) allow_any_instance_of(VagrantSpec::AnsibleInventory).to receive(:generate) - allow(mock_spec_helper).to receive(:generate) + allow_any_instance_of(VagrantSpec::MachineData).to receive(:generate) + allow(mock_spec_helper).to receive(:generate) allow(mock_ansible_inventory).to receive(:generate) + allow(mock_machine_data).to receive(:generate) end end @@ -46,12 +53,11 @@ def execute_proc def execute_protection_proc proc do allow(subject).to receive(:parse_opts) { 'not_nil' } - allow(VagrantSpec::SpecHelper).to receive(:new) do - mock_spec_helper - end + allow(VagrantSpec::SpecHelper).to receive(:new) { mock_spec_helper } allow(VagrantSpec::AnsibleInventory).to receive(:new) do mock_ansible_inventory end + allow(VagrantSpec::MachineData).to receive(:new) { mock_machine_data } execute_proc.call end end @@ -77,6 +83,24 @@ def execute_protection_proc subject.execute end end + + context 'and when @generate_machine_data is true' do + it '#execute generates a .vagrantspec_machine_data file' do + execute_protection_proc.call + subject.generate_machine_data = true + expect(mock_machine_data).to receive(:generate) + subject.execute + end + end + + context 'and when @generate_machine_data is false' do + it '#execute does not generate a .vagrantspec_machine_data file' do + execute_protection_proc.call + subject.generate_machine_data = false + expect(mock_machine_data).not_to receive(:generate) + subject.execute + end + end end it '#parse_opts calls parse_options' do diff --git a/spec/unit/vagrant_spec_test/machine_data_spec.rb b/spec/unit/vagrant_spec_test/machine_data_spec.rb new file mode 100644 index 0000000..b2b64ac --- /dev/null +++ b/spec/unit/vagrant_spec_test/machine_data_spec.rb @@ -0,0 +1,51 @@ +# encoding: UTF-8 + +require 'spec_helper' +require 'vagrant_spec/machine_data' + +describe VagrantSpec::MachineData do + include_context 'unit' + include_examples 'shared_mocks' + + let(:mock_machine_ssh_config) do + { + host: '127.0.0.1', + port: '2222', + username: 'vagrant', + private_key_path: %w(mock_key) + } + end + + let(:mock_data) do + { + name: 'default', + host: '127.0.0.1', + port: '2222', + username: 'vagrant', + private_key: 'mock_key' + } + end + + before do + allow(mock_node).to receive(:name) { 'default' } + allow(mock_node).to receive(:ssh_info) { mock_machine_ssh_config } + end + + subject { VagrantSpec::MachineData.new(iso_env) } + + it '#generate create the .vagrantspec_machine_data file' do + allow(subject).to receive(:populate_data) + allow(IO).to receive(:write) + .with('.vagrantspec_machine_data', JSON.pretty_generate(subject.data)) + expect(IO).to receive(:write) + .with('.vagrantspec_machine_data', JSON.pretty_generate(subject.data)) + subject.generate + end + + it '#populate_data stores json data' do + allow(subject.m_finder).to receive(:machines).and_yield(mock_node) + allow(mock_node).to receive(:ssh_info) { mock_machine_ssh_config } + expect(subject.data).to receive(:<<).with(mock_data) + subject.populate_data + end +end diff --git a/vagrant_spec.gemspec b/vagrant_spec.gemspec index 6d90839..5d5ee71 100644 --- a/vagrant_spec.gemspec +++ b/vagrant_spec.gemspec @@ -6,6 +6,7 @@ Gem::Specification.new do |s| s.name = 'vagrant_spec' s.version = VagrantSpec::VERSION s.platform = Gem::Platform::RUBY + s.licenses = %w(Apache2) s.authors = %w(Demitri Swan) s.email = %w(demitriswan@gmail.com) s.homepage = 'http://github.com/miroswan/vagrant_spec' From 5709054324ebcadb131ec616ebef9d0ee8560868 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Tue, 9 Aug 2016 10:29:35 -0700 Subject: [PATCH 2/2] Adding docs to help output --- lib/vagrant_spec/command/init.rb | 3 ++- lib/vagrant_spec/command/test.rb | 5 ++++- lib/vagrant_spec/templates/init_help | 16 ++++++++++++++++ lib/vagrant_spec/templates/test_help | 21 +++++++++++++++++++++ 4 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 lib/vagrant_spec/templates/init_help create mode 100644 lib/vagrant_spec/templates/test_help diff --git a/lib/vagrant_spec/command/init.rb b/lib/vagrant_spec/command/init.rb index e6ef7d3..bc779d9 100644 --- a/lib/vagrant_spec/command/init.rb +++ b/lib/vagrant_spec/command/init.rb @@ -42,10 +42,11 @@ def execute def parse_opts opts = OptionParser.new do |o| - o.banner = "\nCreates the serverspec/spec_helper.rb file for testing" + o.banner = "\nInit: Initializes state configuration" o.separator '' o.separator 'Usage: vagrant spec init' o.separator '' + o.separator IO.read(File.join(template_dir, 'init_help')) end parse_options(opts) end diff --git a/lib/vagrant_spec/command/test.rb b/lib/vagrant_spec/command/test.rb index f04448a..e9715c0 100644 --- a/lib/vagrant_spec/command/test.rb +++ b/lib/vagrant_spec/command/test.rb @@ -1,6 +1,7 @@ # encoding: UTF-8 require 'vagrant_spec/test_plan' +require 'vagrant_spec/utils' module VagrantSpec module Command @@ -9,6 +10,7 @@ module Command # argv # env [Vagrant::Environment] class Test < Vagrant.plugin(2, :command) + include VagrantSpec::Utils def initialize(argv, env) super end @@ -20,10 +22,11 @@ def execute def parse_opts opts = OptionParser.new do |o| - o.banner = "\nRun the tests configured in the Vagrantfile" + o.banner = "\nTest: Run the tests configured in the Vagrantfile" o.separator '' o.separator 'Usage: vagrant spec test' o.separator '' + o.separator IO.read(File.join(template_dir, 'test_help')) end parse_options(opts) end diff --git a/lib/vagrant_spec/templates/init_help b/lib/vagrant_spec/templates/init_help new file mode 100644 index 0000000..22bab07 --- /dev/null +++ b/lib/vagrant_spec/templates/init_help @@ -0,0 +1,16 @@ +The init subcommand initializes state-based configuration for vagrant_spec. + +It creates a spec_helper.rb file under the configured serverspec directory. This +file is used to setup serverspec backend configuration and ease serverspec +testing. + +If config.spec.ansible_inventory configuration directive is used within the +Vagrantfile, then init will generate a test inventory file +vagrantspec_inventory. This file can be used for ansible orchestration against +the vagrant instances. + +By default, init will generate a json file containing machine data for each +vagrant instance at .vagrantspec_machine_data. This file can be used by +orchestration tooling outside of ansible to map events to vagrant nodes. The +config.spec.generate_machine_data configuration parameter controls the +generation of this file. diff --git a/lib/vagrant_spec/templates/test_help b/lib/vagrant_spec/templates/test_help new file mode 100644 index 0000000..942c501 --- /dev/null +++ b/lib/vagrant_spec/templates/test_help @@ -0,0 +1,21 @@ +The test subcommand will execute the serverspec tests configured in the +Vagrantfile under the config.spec.test_plan directive. This directive accepts +an array of hashes. For example: + +config.spec.test_plan = [ + { + 'nodes' => /nginx/, + 'flags' => '--format documentation --pattern serverspec/nginx*' + }, + { + 'nodes' => %w(app1 app2), + 'flags' => '--format documentation --pattern serverspec/app*' + } +] + +Each hash have two required keys: nodes and flags. The nodes key accepts a +regular expression object matching the names of the vagrant machines defined in +the Vagrantfile. Alternatively, you can explicility pass an array of node names. +The flags key accepts a string of command line arguments to pass to rspec. Any +of the acceptable rspec options and parameters are leagle. +