praegustator 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +2 -0
  3. data/.rspec +3 -0
  4. data/Gemfile +8 -0
  5. data/Gemfile.lock +152 -0
  6. data/Rakefile +1 -0
  7. data/bin/praeg +6 -0
  8. data/features/praegustator.feature +8 -0
  9. data/features/support/env.rb +7 -0
  10. data/features/support/setup.rb +4 -0
  11. data/features_setup/recipies/checks/basic_commands.rb +11 -0
  12. data/features_setup/recipies/checks/nginx.rb +14 -0
  13. data/features_setup/recipies/test_recipe.rb +10 -0
  14. data/features_setup/setup/.praegustator.yml +9 -0
  15. data/features_setup/setup/.vagrant/machines/default/berkshelf +1 -0
  16. data/features_setup/setup/.vagrant/machines/default/virtualbox/id +1 -0
  17. data/features_setup/setup/.zero-knife.rb +3 -0
  18. data/features_setup/setup/Berksfile +4 -0
  19. data/features_setup/setup/Berksfile.lock +13 -0
  20. data/features_setup/setup/Vagrantfile +15 -0
  21. data/features_setup/setup/metadata.rb +1 -0
  22. data/lib/praegustator.rb +77 -0
  23. data/lib/praegustator/cli.rb +36 -0
  24. data/lib/praegustator/dsl.rb +42 -0
  25. data/lib/praegustator/executor.rb +13 -0
  26. data/lib/praegustator/node.rb +10 -0
  27. data/lib/praegustator/setup.rb +77 -0
  28. data/lib/praegustator/test_suite.rb +20 -0
  29. data/lib/praegustator/version.rb +3 -0
  30. data/lib/praegustator/wrappers/chef.rb +16 -0
  31. data/lib/praegustator/wrappers/output_parser.rb +21 -0
  32. data/lib/praegustator/wrappers/server_spec.rb +61 -0
  33. data/praegustator.gemspec +32 -0
  34. data/readme.md +76 -0
  35. data/spec/praegustator/cli_spec.rb +23 -0
  36. data/spec/praegustator/dsl_spec.rb +41 -0
  37. data/spec/praegustator/resources/sample/config/praegustator.yml +9 -0
  38. data/spec/praegustator/resources/sample/suits/default.rb +15 -0
  39. data/spec/praegustator/resources/sample/suits/staging.rb +17 -0
  40. data/spec/praegustator_spec.rb +54 -0
  41. data/spec/spec_helper.rb +8 -0
  42. data/test.md +14 -0
  43. data/tmp/aruba/.praegustator.yml +9 -0
  44. metadata +239 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YWU3YTdhODI3ODg3MWNlOTZmNjMxYzllN2M1OTQ5NTNhNjkyNjVmYw==
5
+ data.tar.gz: !binary |-
6
+ YmE4NzBjMGE3ZGY0Y2NlODk5NWIwYjE2YzU0YmVjOWQwZTMyODJjMA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ NTYxMjViYWRlODljZTRlODExZmMyZDFmODA4MTk5NWM3OWU5YjcwNzYxYzY5
10
+ MjQzY2MyNzcwYWY5YTc5MzM4YWRkNzIyOTU1NTI3MDcxOTI2Y2MxNjcyYjFi
11
+ Nzk3NDBiNTIxY2UyNGNiMDk3YjRhMmNkNGFjOWU2ZWNlMWVmNDU=
12
+ data.tar.gz: !binary |-
13
+ OThlNzM5MzIxNTZiZTU2Y2EwZGE3ZDgyZGFhMjA4YzU0ZTlkMjQ5YzEyN2Uz
14
+ ODI5MWRhZmIyNGIzNjM3ODI1OGYyMDdlNjg5OTVkNzNkOGRjZWUxNmRiODcw
15
+ YzU1NzkyY2Y1YmYzY2JkZDJjMzViZjEwMzI0MmU1NmUzOTRkYjM=
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ pkg
2
+ demo
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format Fuubar
3
+
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ group :development, :test do
4
+ gem 'rspec'
5
+ gem 'berkshelf'
6
+ gem 'pry'
7
+ gem 'fuubar'
8
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,152 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ activesupport (3.2.15)
5
+ i18n (~> 0.6, >= 0.6.4)
6
+ multi_json (~> 1.0)
7
+ addressable (2.3.5)
8
+ akami (1.2.0)
9
+ gyoku (>= 0.4.0)
10
+ nokogiri (>= 1.4.0)
11
+ berkshelf (2.0.10)
12
+ activesupport (~> 3.2.0)
13
+ addressable (~> 2.3.4)
14
+ buff-shell_out (~> 0.1)
15
+ chozo (>= 0.6.1)
16
+ faraday (>= 0.8.5)
17
+ hashie (>= 2.0.2)
18
+ minitar (~> 0.5.4)
19
+ rbzip2 (~> 0.2.0)
20
+ retryable (~> 1.3.3)
21
+ ridley (~> 1.5.0)
22
+ solve (>= 0.5.0)
23
+ thor (~> 0.18.0)
24
+ buff-config (0.4.0)
25
+ buff-extensions (~> 0.3)
26
+ varia_model (~> 0.1)
27
+ buff-extensions (0.5.0)
28
+ buff-ignore (1.1.0)
29
+ buff-ruby_engine (0.1.0)
30
+ buff-shell_out (0.1.1)
31
+ buff-ruby_engine (~> 0.1.0)
32
+ builder (3.2.2)
33
+ celluloid (0.14.1)
34
+ timers (>= 1.0.0)
35
+ celluloid-io (0.14.1)
36
+ celluloid (>= 0.14.1)
37
+ nio4r (>= 0.4.5)
38
+ chozo (0.6.1)
39
+ activesupport (>= 3.2.0)
40
+ hashie (>= 2.0.2)
41
+ multi_json (>= 1.3.0)
42
+ coderay (1.0.9)
43
+ diff-lcs (1.2.5)
44
+ erubis (2.7.0)
45
+ faraday (0.8.8)
46
+ multipart-post (~> 1.2.0)
47
+ ffi (1.9.3)
48
+ fuubar (1.2.1)
49
+ rspec (~> 2.0)
50
+ rspec-instafail (~> 0.2.0)
51
+ ruby-progressbar (~> 1.0)
52
+ gssapi (1.0.3)
53
+ ffi (>= 1.0.1)
54
+ gyoku (1.1.0)
55
+ builder (>= 2.1.2)
56
+ hashie (2.0.5)
57
+ httpclient (2.3.4.1)
58
+ httpi (0.9.7)
59
+ rack
60
+ i18n (0.6.5)
61
+ json (1.8.1)
62
+ little-plugger (1.1.3)
63
+ logging (1.8.1)
64
+ little-plugger (>= 1.1.3)
65
+ multi_json (>= 1.3.6)
66
+ method_source (0.8.2)
67
+ mini_portile (0.5.2)
68
+ minitar (0.5.4)
69
+ mixlib-authentication (1.3.0)
70
+ mixlib-log
71
+ mixlib-log (1.6.0)
72
+ multi_json (1.8.2)
73
+ multipart-post (1.2.0)
74
+ net-http-persistent (2.9)
75
+ net-ssh (2.7.0)
76
+ nio4r (0.5.0)
77
+ nokogiri (1.6.0)
78
+ mini_portile (~> 0.5.0)
79
+ nori (1.1.5)
80
+ pry (0.9.12.2)
81
+ coderay (~> 1.0.5)
82
+ method_source (~> 0.8)
83
+ slop (~> 3.4)
84
+ rack (1.5.2)
85
+ rbzip2 (0.2.0)
86
+ retryable (1.3.3)
87
+ ridley (1.5.3)
88
+ addressable
89
+ buff-config (~> 0.2)
90
+ buff-extensions (~> 0.3)
91
+ buff-ignore (~> 1.1)
92
+ buff-shell_out (~> 0.1)
93
+ celluloid (~> 0.14.0)
94
+ celluloid-io (~> 0.14.0)
95
+ erubis
96
+ faraday (>= 0.8.4)
97
+ hashie (>= 2.0.2)
98
+ json (>= 1.7.7)
99
+ mixlib-authentication (>= 1.3.0)
100
+ net-http-persistent (>= 2.8)
101
+ net-ssh
102
+ nio4r (>= 0.5.0)
103
+ retryable
104
+ solve (>= 0.4.4)
105
+ varia_model (~> 0.1)
106
+ winrm (~> 1.1.0)
107
+ rspec (2.14.1)
108
+ rspec-core (~> 2.14.0)
109
+ rspec-expectations (~> 2.14.0)
110
+ rspec-mocks (~> 2.14.0)
111
+ rspec-core (2.14.7)
112
+ rspec-expectations (2.14.4)
113
+ diff-lcs (>= 1.1.3, < 2.0)
114
+ rspec-instafail (0.2.4)
115
+ rspec-mocks (2.14.4)
116
+ ruby-progressbar (1.2.0)
117
+ rubyntlm (0.1.1)
118
+ savon (0.9.5)
119
+ akami (~> 1.0)
120
+ builder (>= 2.1.2)
121
+ gyoku (>= 0.4.0)
122
+ httpi (~> 0.9)
123
+ nokogiri (>= 1.4.0)
124
+ nori (~> 1.0)
125
+ wasabi (~> 1.0)
126
+ slop (3.4.6)
127
+ solve (0.8.1)
128
+ thor (0.18.1)
129
+ timers (1.1.0)
130
+ uuidtools (2.1.4)
131
+ varia_model (0.2.0)
132
+ buff-extensions (~> 0.2)
133
+ hashie (>= 2.0.2)
134
+ wasabi (1.0.0)
135
+ nokogiri (>= 1.4.0)
136
+ winrm (1.1.3)
137
+ gssapi (~> 1.0.0)
138
+ httpclient (~> 2.2, >= 2.2.0.2)
139
+ logging (~> 1.6, >= 1.6.1)
140
+ nokogiri (~> 1.5)
141
+ rubyntlm (~> 0.1.1)
142
+ savon (= 0.9.5)
143
+ uuidtools (~> 2.1.2)
144
+
145
+ PLATFORMS
146
+ ruby
147
+
148
+ DEPENDENCIES
149
+ berkshelf
150
+ fuubar
151
+ pry
152
+ rspec
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/praeg ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
5
+ require 'praegustator/cli'
6
+ Praegustator::CLI.start
@@ -0,0 +1,8 @@
1
+ Feature: Praegustator
2
+ In order to validate environment
3
+ As a CLI
4
+ I want to check the apache installed
5
+
6
+ Scenario: check nginx
7
+ When I run `praeg validate`
8
+ Then the output should contain "should be installed"
@@ -0,0 +1,7 @@
1
+ require 'aruba/cucumber'
2
+ Aruba.configure do |config|
3
+ config.before_cmd do |cmd|
4
+ @aruba_timeout_seconds = 5
5
+ system "cp ../../features_setup/setup/.praegustator.yml ."
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ require 'aruba/cucumber'
2
+
3
+ system "cd features_setup/setup/ && vagrant up"
4
+ system "cp features_setup/setup/.praegustator.yml tmp/aruba"
@@ -0,0 +1,11 @@
1
+ describe command('whoami') do
2
+ it { should return_stdout 'vagrant' }
3
+ end
4
+
5
+ describe command('cat /etc/resolv.conf') do
6
+ it { should return_stdout /8\.8\.8\.8/ }
7
+ end
8
+
9
+ describe command('ls /foo') do
10
+ it { should return_stderr /No such file or directory/ }
11
+ end
@@ -0,0 +1,14 @@
1
+ describe "nginx" do
2
+ describe package('nginx') do
3
+ it { should be_installed }
4
+ end
5
+
6
+ describe service('nginx') do
7
+ it { should be_enabled }
8
+ it { should be_running }
9
+ end
10
+
11
+ describe port(80) do
12
+ it { should be_listening }
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ ip("*")do
2
+ check "basic_commands"
3
+ check "nginx"
4
+ end
5
+
6
+
7
+ role("web-server")do
8
+ check "basic_commands"
9
+ check "nginx"
10
+ end
@@ -0,0 +1,9 @@
1
+ spec:
2
+ recipes_dir: "../../features_setup/recipies/"
3
+ checks_dir: "../../features_setup/recipies/checks/"
4
+ chef:
5
+ knife_location: "../../features_setup/setup/.zero-knife.rb"
6
+ ssh:
7
+ user: "vagrant"
8
+ pasword: nil
9
+ keys: [ "~/.vagrant.d/insecure_private_key" ]
@@ -0,0 +1 @@
1
+ /Users/sumit/.berkshelf/default/vagrant/berkshelf-20131123-5298-1uxwddb-default
@@ -0,0 +1 @@
1
+ de0f2199-3fd8-424a-be0b-5eb59f24f961
@@ -0,0 +1,3 @@
1
+ chef_server_url 'http://192.168.43.247:4000'
2
+ node_name 'zero-host'
3
+ client_key '/var/folders/f7/2rps3hds45zd0gn2qvgbj_000000gn/T/fake_key/fake.pem'
@@ -0,0 +1,4 @@
1
+ site :opscode
2
+
3
+ metadata
4
+ cookbook 'nginx'
@@ -0,0 +1,13 @@
1
+ {
2
+ "sources": {
3
+ "setup": {
4
+ "path": "."
5
+ },
6
+ "apache": {
7
+ "locked_version": "0.0.5"
8
+ },
9
+ "apache2": {
10
+ "locked_version": "1.8.4"
11
+ }
12
+ }
13
+ }
@@ -0,0 +1,15 @@
1
+ Vagrant.configure("2") do |config|
2
+ config.vm.hostname = "tset-machine"
3
+ config.vm.box = "Berkshelf-CentOS-6.3-x86_64-minimal"
4
+ config.vm.box_url = "https://dl.dropbox.com/u/31081437/Berkshelf-CentOS-6.3-x86_64-minimal.box"
5
+ config.vm.network "public_network", :bridge => 'en1: Wi-Fi (AirPort)'
6
+ config.ssh.max_tries = 40
7
+ config.ssh.timeout = 120
8
+ config.berkshelf.enabled = true
9
+ config.chef_zero.enabled = true
10
+ config.vm.provision :chef_client do |chef|
11
+ chef.run_list = [
12
+ "recipe[nginx::default]"
13
+ ]
14
+ end
15
+ end
@@ -0,0 +1 @@
1
+ #depends 'apache'
@@ -0,0 +1,77 @@
1
+ require "praegustator/version"
2
+ require 'praegustator/cli'
3
+ require 'praegustator/executor'
4
+ require 'praegustator/test_suite'
5
+ require 'praegustator/dsl'
6
+ require 'praegustator/wrappers/chef'
7
+ require 'praegustator/node'
8
+ require 'praegustator/setup'
9
+ require 'yaml'
10
+ require 'colorize'
11
+
12
+ module Praegustator
13
+ @config = {
14
+ 'log_level' => 'info',
15
+ 'search_driver' => 'chef',
16
+ 'chef' => {
17
+ 'knife_location' => ENV['KNIFE_PATH'] || "~/.chef/knife.rb"
18
+ },
19
+ 'spec' => {
20
+ 'backend' => 'serverspec',
21
+ 'recipes_dir' => 'spec/',
22
+ 'checks_dir' => 'spec/shared',
23
+ },
24
+ 'ssh' => {
25
+ 'user' => 'root',
26
+ 'pasword' => nil,
27
+ 'keys' => []
28
+ }
29
+ }
30
+
31
+ def self.configure_with(path_to_yaml_file)
32
+ begin
33
+ config = YAML::load(IO.read(path_to_yaml_file))
34
+ rescue Psych::SyntaxError
35
+ p "error while parsing yaml configuration file. using defaults."; return
36
+ rescue Exception
37
+ p "yaml configuration file couldn't be found. using defaults."; return
38
+ end
39
+ configure(config) if config
40
+ end
41
+
42
+ def self.config
43
+ @config
44
+ end
45
+
46
+ private
47
+ def self.configure(opts = {})
48
+ valid_keys = @config.keys
49
+ opts.each {|k,v| @config[k] = v if valid_keys.include? k}
50
+ end
51
+ end
52
+
53
+
54
+ module RSpec
55
+ module Core
56
+ class Runner
57
+ def self.run_patched(args, err=$stderr, out=$stdout)
58
+ trap_interrupt
59
+ options = ConfigurationOptions.new(args)
60
+ options.parse_options
61
+
62
+ if options.options[:drb]
63
+ require 'rspec/core/drb_command_line'
64
+ begin
65
+ DRbCommandLine.new(options).run(err, out)
66
+ rescue DRb::DRbConnError
67
+ CommandLine.new(options).run(err, out)
68
+ end
69
+ else
70
+ CommandLine.new(options).run(err, out)
71
+ end
72
+ ensure
73
+ #RSpec.reset
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,36 @@
1
+ require "thor"
2
+ require 'praegustator'
3
+
4
+ module Praegustator
5
+ class CLI < Thor
6
+
7
+ desc "taste", "test an check against a chef query"
8
+ long_desc <<-LONGDESC
9
+ `praeg taste query check` will execute check against node returned by query
10
+ > $ praeg taste role:web nginx
11
+ LONGDESC
12
+ def taste(query,check)
13
+ config_file_path = Dir.pwd+"/.praegustator.yml"
14
+ Praegustator.configure_with config_file_path
15
+ recipes_dir = Praegustator.config['spec']['recipes_dir']
16
+ recipes = Dir[Dir.pwd+"/#{recipes_dir}/**/*_recipe.rb"] if recipes.empty?
17
+ Praegustator::Executor.new.execute(recipes)
18
+ end
19
+
20
+ desc "validate", "validate infrastructure by executing checks defined in recipe files"
21
+ def validate(*recipes)
22
+ config_file_path = Dir.pwd+"/.praegustator.yml"
23
+ Praegustator.configure_with config_file_path
24
+ recipes_dir = Praegustator.config['spec']['recipes_dir']
25
+ recipes = Dir[Dir.pwd+"/#{recipes_dir}/**/*_recipe.rb"] if recipes.empty?
26
+ Praegustator::Executor.new.execute(recipes)
27
+ end
28
+
29
+ desc "init" , "setup praegustator"
30
+ long_desc " > $ praeg setup spec_dir"
31
+ def init(spec_dir ='spec')
32
+ Praegustator::Setup.new.init(spec_dir)
33
+ end
34
+ end
35
+ end
36
+
@@ -0,0 +1,42 @@
1
+ require 'praegustator'
2
+ module Praegustator
3
+ class Dsl
4
+ def initialize
5
+ @suits = []
6
+ end
7
+
8
+ def environment(name, &block)
9
+ @environment = name
10
+ end
11
+
12
+ def role(name, &block)
13
+ search("role:#{name}",&block)
14
+ end
15
+
16
+ def recipe(name, &block)
17
+ name = name.gsub(":","\\:")
18
+ search("run_list:recipe\\[#{name}\\]",&block)
19
+ end
20
+
21
+ def ip(address, &block)
22
+ search("ipaddress:#{address}",&block)
23
+ end
24
+
25
+ def search(query, &block)
26
+ query = "#{query} AND chef_environment:#{@environment}" if @environment
27
+ suite = TestSuite.new(query)
28
+ suite.instance_eval(&block)
29
+ @suits << suite
30
+ suite.nodes
31
+ end
32
+
33
+ def parse_file(filename)
34
+ if File.exists?(filename) && File.readable?(filename)
35
+ self.instance_eval(IO.read(filename), filename, 1)
36
+ else
37
+ raise IOError, "Cannot open or read #{filename}!"
38
+ end
39
+ @suits
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,13 @@
1
+ require 'praegustator'
2
+
3
+ module Praegustator
4
+ class Executor
5
+ def execute(recipes)
6
+ recipes.each do |recipe|
7
+ suits = Praegustator::Dsl.new.parse_file(recipe)
8
+ suits.each(&:execute)
9
+ end
10
+ end
11
+ end
12
+ end
13
+
@@ -0,0 +1,10 @@
1
+ module Praegustator
2
+ class Node
3
+ attr_accessor :ipaddress, :name, :query
4
+ def initialize name,ipaddress,query
5
+ @name = name
6
+ @ipaddress =ipaddress
7
+ @query = query
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,77 @@
1
+ require 'praegustator'
2
+
3
+ module Praegustator
4
+ class Setup
5
+ def init(spec_dir)
6
+ mkdir(spec_dir)
7
+ create_spec spec_dir
8
+ create_recepie spec_dir
9
+ create_settings spec_dir
10
+ end
11
+
12
+ def mkdir dir
13
+ if File.exists? dir
14
+ $stderr.puts "!! #{dir} already exists"
15
+ else
16
+ FileUtils.mkdir dir
17
+ puts " + #{dir}/"
18
+ FileUtils.mkdir "#{dir}/checks"
19
+ puts " + #{dir}/checks"
20
+ end
21
+ end
22
+
23
+ def create_recepie dir
24
+ content = <<-EOF
25
+ role("*")do
26
+ check "basic_commands"
27
+ end
28
+ EOF
29
+ create_file "#{dir}/test_recipe.rb",content
30
+ end
31
+
32
+ def create_settings dir
33
+ content = <<-EOF
34
+ spec:
35
+ recipes_dir: "#{dir}/"
36
+ checks_dir: "#{dir}/checks/"
37
+ chef:
38
+ knife_location: "~/.chef/knife.rb"
39
+ # ssh:
40
+ # user: "root"
41
+ # pasword: nil
42
+ # keys: [ "~/.ssh/id_rsa" ]
43
+ EOF
44
+ create_file ".praegustator.yml",content
45
+ end
46
+
47
+ def create_spec dir
48
+ content = <<-EOF
49
+ describe command('whoami') do
50
+ it { should return_stdout 'vagrant' }
51
+ end
52
+
53
+ describe command('cat /etc/resolv.conf') do
54
+ it { should return_stdout /8\.8\.8\.8/ }
55
+ end
56
+
57
+ describe command('ls /foo') do
58
+ it { should return_stderr /No such file or directory/ }
59
+ end
60
+ EOF
61
+ create_file "#{dir}/checks/basic_commands.rb",content
62
+ end
63
+
64
+ private
65
+ def create_file file, content
66
+ if File.exists? file
67
+ $stderr.puts "!! #{file} exists"
68
+ else
69
+ File.open("#{file}", 'w') do |f|
70
+ f.puts content
71
+ end
72
+ puts " + #{file}"
73
+ end
74
+ end
75
+ end
76
+ end
77
+
@@ -0,0 +1,20 @@
1
+ require "praegustator"
2
+ module Praegustator
3
+ class TestSuite
4
+ attr_accessor :nodes
5
+ def initialize(query)
6
+ @query = query
7
+ @checks = {}
8
+ @nodes = Praegustator::Wrappers::Chef.search query
9
+ end
10
+ def check(name,opts={})
11
+ @checks[name] = {}
12
+ @checks[name][:opts] = opts
13
+ end
14
+ def execute
15
+ # lazy load
16
+ require 'praegustator/wrappers/server_spec'
17
+ Praegustator::Wrappers::ServerSpec.new.execute @nodes,@checks
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,3 @@
1
+ module Praegustator
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,16 @@
1
+ require 'praegustator'
2
+ require 'chef/rest'
3
+ require 'chef/search/query'
4
+
5
+ module Praegustator
6
+ module Wrappers
7
+ class Chef
8
+ def self.search query
9
+ ::Chef::Config.from_file(File.expand_path(Praegustator.config['chef']['knife_location']))
10
+ chef_query = ::Chef::Search::Query.new
11
+ nodes = chef_query.search('node',query).first rescue []
12
+ nodes.map{|n| Praegustator::Node.new n.name , n.ipaddress ,query}
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,21 @@
1
+ require 'praegustator'
2
+ require 'colorize'
3
+
4
+ module Praegustator
5
+ module Wrappers
6
+ class OutputParser
7
+ def parse node,json
8
+ $stdout.puts "Node: #{node.ipaddress.colorize(:blue)} ChefQuery: #{node.query.colorize(:blue)}"
9
+ $stdout.puts " Checks:"
10
+ return if json[:examples].nil?
11
+ json[:examples].each do |e|
12
+ if e[:status] == 'passed'
13
+ $stdout.puts " #{e[:full_description]} : #{e[:status]}".colorize(:green)
14
+ else
15
+ $stdout.puts " #{e[:full_description]} : #{e[:status]}".colorize(:red)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,61 @@
1
+ require 'praegustator'
2
+ require 'praegustator/wrappers/output_parser'
3
+ require 'net/ssh'
4
+ require 'serverspec'
5
+ require 'rspec/core/formatters/json_formatter'
6
+ require 'rspec/core/formatters/documentation_formatter'
7
+
8
+ include Serverspec::Helper::Ssh
9
+ include Serverspec::Helper::DetectOS
10
+
11
+ module Praegustator
12
+ module Wrappers
13
+ class ServerSpec
14
+
15
+ def initialize
16
+ @parser = Praegustator::Wrappers::OutputParser.new
17
+ end
18
+
19
+
20
+ def execute nodes,checks
21
+ nodes.each do |n|
22
+ ENV['TARGET_HOST'] = n.ipaddress
23
+ formatter = RSpec::Core::Formatters::JsonFormatter.new(nil)
24
+ #RSpec.reset
25
+ RSpec.clear_remaining_example_groups
26
+ load 'serverspec.rb'
27
+ begin
28
+ RSpec.configure do |c|
29
+ c.host = ENV['TARGET_HOST']
30
+ options = Net::SSH::Config.for(c.host)
31
+ user = options[:user] || Praegustator.config['ssh']['user']
32
+ options[:keys] = Praegustator.config['ssh']['keys'] if options[:keys].nil?
33
+ options[:timeout] = 10
34
+ c.ssh = Net::SSH.start(c.host, user, options)
35
+ c.os = backend.check_os
36
+ c.output = $stdout
37
+ c.color_enabled = true
38
+ c.tty = true
39
+ if Praegustator.config['log_level'] == 'debug'
40
+ formatter = RSpec::Core::Formatters::DocumentationFormatter.new(c.output)
41
+ end
42
+ reporter = RSpec::Core::Reporter.new(formatter)
43
+ c.instance_variable_set(:@reporter, reporter)
44
+ end
45
+ spec_files = checks.keys.map{|check| "#{Dir.pwd}/#{Praegustator.config['spec']['checks_dir']}/#{check}.rb" }
46
+ begin
47
+ RSpec::Core::Runner.run_patched(spec_files, $stderr, $stdout)
48
+ rescue Error => e
49
+ $stderr.puts "!! spec execution failed #{e.message}"
50
+ end
51
+ if Praegustator.config['log_level'] != 'debug'
52
+ @parser.parse n,formatter.output_hash
53
+ end
54
+ rescue Exception => e
55
+ $stderr.puts "!! failed for #{n.ipaddress} : #{e.message}"
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'praegustator/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "praegustator"
8
+ spec.version = Praegustator::VERSION
9
+ spec.authors = ["timusg"]
10
+ spec.email = ["timusga@gmail.com"]
11
+ spec.description = %q{taste and validate the chef cooked infrastructure}
12
+ spec.summary = %q{DSL over server spec to test chef powered infrastructure}
13
+ spec.homepage = "https://github.com/timusg/praegustator"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features|feautures_setup|tmp)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "thor"
22
+ spec.add_runtime_dependency "rspec", ">= 2.13.0"
23
+ spec.add_runtime_dependency "chef"
24
+ spec.add_runtime_dependency "serverspec"
25
+ spec.add_runtime_dependency "colorize"
26
+
27
+ spec.add_development_dependency "bundler", "~> 1.3"
28
+ spec.add_development_dependency "rake"
29
+ spec.add_development_dependency "rspec", "~> 2.6"
30
+ spec.add_development_dependency "cucumber"
31
+ spec.add_development_dependency "aruba"
32
+ end
data/readme.md ADDED
@@ -0,0 +1,76 @@
1
+ Praegustator provide simple dsl to validate and test chef managed infrastructure, it use remote ssh and utilize server spec in the backend to define checks.
2
+
3
+ ## install
4
+
5
+ gem install praegustator # install praegustator
6
+ praeg --help
7
+ praeg init spec_dir #setup praeg in current dir
8
+
9
+
10
+ ## run
11
+
12
+ praeg validate #run all recipes
13
+ praeg validate recipe # run recipe
14
+
15
+
16
+ ## basic structure
17
+
18
+ Praegustator provides chef conventions based methods like `role`, `recipe`, `ip`, `search` to group similar nodes for executing
19
+ check against them, whereas checks are rspec files with server-spec matchers defined in `spec_dir`
20
+
21
+ sample recipe for praegustator:
22
+
23
+ ``` ruby sample_recipe.rb
24
+ environment :staging
25
+
26
+ role("web-server") do
27
+ check "application/nginx"
28
+ check "application/puma"
29
+ check "application/s3cmd"
30
+ end
31
+ ```
32
+ The `role` method mark all nodes with that role . Within the
33
+ block passed to `role` you can declare checks using the `check` method.
34
+
35
+ Where check are server spec files:
36
+
37
+ ``` ruby #{spec_dir}/application/nginx.rb
38
+ describe package('nginx') do
39
+ it { should be_installed }
40
+ end
41
+
42
+ describe service('nginx') do
43
+ it { should be_enabled }
44
+ it { should be_running }
45
+ end
46
+ ```
47
+
48
+ ## `praeg` command
49
+
50
+ Praegustator gem installs the `praeg` command,
51
+ Run `praeg --help` to see the help.
52
+
53
+ ## praegustator configuration file `.praegustator.yml`
54
+
55
+ Provide way to override various praegustator related settings , like
56
+ spec dir , recipe dir and ssh configuration
57
+
58
+
59
+ ## ssh config file
60
+
61
+ `praegustator` picks ssh configuration for a node from `~/.ssh/config` file or
62
+ global ssh configuration defined in `.praegustator.yml`
63
+
64
+ ## Useful links
65
+
66
+ * [http://github.com/rspec/rspec](http://github.com/rspec/rspec)
67
+ * [http://serverspec.org/](http://serverspec.org/)
68
+
69
+
70
+ # Contributing
71
+
72
+ 1. Fork it
73
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
74
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
75
+ 4. Push to the branch (`git push origin my-new-feature`)
76
+ 5. Create new Pull Request
@@ -0,0 +1,23 @@
1
+ require "spec_helper"
2
+
3
+ describe Praegustator::CLI do
4
+ describe "#taste" do
5
+ end
6
+
7
+ describe "#validate" do
8
+ before do
9
+ Praegustator::Executor.any_instance.stub(:execute).and_return('foo')
10
+ end
11
+ subject { Praegustator::CLI.new.validate("foo") }
12
+ it { should eq 'foo' }
13
+ end
14
+
15
+ describe "#init" do
16
+ describe "#init" do
17
+ it "sets knife_location" do
18
+ Praegustator::Setup.any_instance.should_receive(:init).with("foo")
19
+ Praegustator::CLI.new.init("foo")
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,41 @@
1
+ require "spec_helper"
2
+
3
+ describe Praegustator::Dsl do
4
+ before :each do
5
+ Praegustator::Wrappers::Chef.stub(:search)
6
+ end
7
+
8
+ describe "#parse_file" do
9
+ let(:dsl){Praegustator::Dsl.new}
10
+ it "should parse and load objects from file with environment defined" do
11
+ suits = dsl.parse_file File.dirname(__FILE__)+"/resources/sample/suits/staging.rb"
12
+ suits.count.should == 3
13
+ first_suite = suits.first
14
+ first_suite.instance_variable_get(:@query).should == "role:web AND chef_environment:staging"
15
+ end
16
+
17
+ it "should parse and load objects from file without environment" do
18
+ suits = dsl.parse_file File.dirname(__FILE__)+"/resources/sample/suits/default.rb"
19
+ suits.count.should == 3
20
+ first_suite = suits.first
21
+ first_suite.instance_variable_get(:@query).should == "role:web"
22
+ end
23
+
24
+ it "should create testsuits objects with checks" do
25
+ suits = dsl.parse_file File.dirname(__FILE__)+"/resources/sample/suits/staging.rb"
26
+ suits.count.should == 3
27
+ first_suite = suits.first
28
+ checks = first_suite.instance_variable_get(:@checks)
29
+ checks.count.should == 3
30
+ end
31
+
32
+ it "should create testsuits objects with options provided" do
33
+ suits = dsl.parse_file File.dirname(__FILE__)+"/resources/sample/suits/staging.rb"
34
+ suits.count.should == 3
35
+ first_suite = suits.first
36
+ checks = first_suite.instance_variable_get(:@checks)
37
+ checks["nginx"].should_not nil
38
+ checks["puma"][:opts][:target].should == "10.210.10.1"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,9 @@
1
+ spec:
2
+ recipes_dir: "recipies/"
3
+ checks_dir: "recipies/checks/"
4
+ chef:
5
+ knife_location: ".zero-knife.rb"
6
+ ssh:
7
+ user: "vagrant"
8
+ pasword: nil
9
+ keys: [ "~/.vagrant.d/insecure_private_key" ]
@@ -0,0 +1,15 @@
1
+ role "web" do
2
+ check "nginx"
3
+ check "puma"
4
+ check "logrotate"
5
+ end
6
+
7
+ recipe "face::db" do
8
+ check "postgres"
9
+ end
10
+
11
+ search 'role:web AND run_list\:recipe\[foo\:\:bar\]' do
12
+ check "nginx"
13
+ check "puma"
14
+ check "logrotate"
15
+ end
@@ -0,0 +1,17 @@
1
+ environment :staging
2
+
3
+ role "web" do
4
+ check "nginx"
5
+ check "puma" , target: "10.210.10.1"
6
+ check "logrotate"
7
+ end
8
+
9
+ recipe "face::db" do
10
+ check "postgres"
11
+ end
12
+
13
+ search 'role:web AND run_list\:recipe\[foo\:\:bar\]' do
14
+ check "nginx"
15
+ check "puma"
16
+ check "logrotate"
17
+ end
@@ -0,0 +1,54 @@
1
+ require "spec_helper"
2
+
3
+ describe Praegustator do
4
+ describe ".config" do
5
+ subject(:config){Praegustator.config}
6
+ it "returnlog level" do
7
+ config['log_level'].should == 'info'
8
+ end
9
+
10
+ it "return knife_location" do
11
+ config['chef']['knife_location'].should == '~/.chef/knife.rb'
12
+ end
13
+
14
+ it "return search driver" do
15
+ config['search_driver'].should == 'chef'
16
+ end
17
+
18
+ it "return backend" do
19
+ config['spec']['backend'].should == 'serverspec'
20
+ end
21
+
22
+ it "return checks dir" do
23
+ config['spec']['checks_dir'].should == 'spec/shared'
24
+ end
25
+ end
26
+
27
+ describe ".configure_with" do
28
+ before :each do
29
+ Praegustator.configure_with File.dirname(__FILE__)+"/praegustator/resources/sample/config/praegustator.yml"
30
+ end
31
+ subject(:config){Praegustator.config}
32
+ it "sets log level" do
33
+ config['log_level'].should == 'info'
34
+ end
35
+
36
+ it "sets knife_location" do
37
+ config['chef']['knife_location'].should == '.zero-knife.rb'
38
+ end
39
+
40
+ it "sets search driver" do
41
+ config['search_driver'].should == 'chef'
42
+ end
43
+
44
+ it "sets recipie dir" do
45
+ config['spec']['recipes_dir'].should == 'recipies/'
46
+ end
47
+
48
+ it "sets checks dir" do
49
+ config['spec']['checks_dir'].should == 'recipies/checks/'
50
+ end
51
+
52
+ end
53
+
54
+ end
@@ -0,0 +1,8 @@
1
+ require "praegustator"
2
+ require 'pathname'
3
+
4
+ RSpec.configure do |config|
5
+ config.treat_symbols_as_metadata_keys_with_true_values = true
6
+ config.run_all_when_everything_filtered = true
7
+ config.filter_run :focus
8
+ end
data/test.md ADDED
@@ -0,0 +1,14 @@
1
+ ## Tools
2
+
3
+ rspec for units
4
+
5
+ aruba for features
6
+
7
+ ## Install
8
+
9
+ vagarnt
10
+ vagrant plugin install vagrant-berkshelf
11
+ vagrant plugin install vagrant-chef-zero
12
+
13
+
14
+ Manually destroy box with vagarnt halt in feauture/setup dir
@@ -0,0 +1,9 @@
1
+ spec:
2
+ recipes_dir: "../../features_setup/recipies/"
3
+ checks_dir: "../../features_setup/recipies/checks/"
4
+ chef:
5
+ knife_location: "../../features_setup/setup/.zero-knife.rb"
6
+ ssh:
7
+ user: "vagrant"
8
+ pasword: nil
9
+ keys: [ "~/.vagrant.d/insecure_private_key" ]
metadata ADDED
@@ -0,0 +1,239 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: praegustator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - timusg
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 2.13.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: 2.13.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: chef
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: serverspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: colorize
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '1.3'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '1.3'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: '2.6'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ~>
123
+ - !ruby/object:Gem::Version
124
+ version: '2.6'
125
+ - !ruby/object:Gem::Dependency
126
+ name: cucumber
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ! '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ! '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: aruba
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ! '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ! '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ description: taste and validate the chef cooked infrastructure
154
+ email:
155
+ - timusga@gmail.com
156
+ executables:
157
+ - praeg
158
+ extensions: []
159
+ extra_rdoc_files: []
160
+ files:
161
+ - .gitignore
162
+ - .rspec
163
+ - Gemfile
164
+ - Gemfile.lock
165
+ - Rakefile
166
+ - bin/praeg
167
+ - features/praegustator.feature
168
+ - features/support/env.rb
169
+ - features/support/setup.rb
170
+ - features_setup/recipies/checks/basic_commands.rb
171
+ - features_setup/recipies/checks/nginx.rb
172
+ - features_setup/recipies/test_recipe.rb
173
+ - features_setup/setup/.praegustator.yml
174
+ - features_setup/setup/.vagrant/machines/default/berkshelf
175
+ - features_setup/setup/.vagrant/machines/default/virtualbox/id
176
+ - features_setup/setup/.zero-knife.rb
177
+ - features_setup/setup/Berksfile
178
+ - features_setup/setup/Berksfile.lock
179
+ - features_setup/setup/Vagrantfile
180
+ - features_setup/setup/metadata.rb
181
+ - lib/praegustator.rb
182
+ - lib/praegustator/cli.rb
183
+ - lib/praegustator/dsl.rb
184
+ - lib/praegustator/executor.rb
185
+ - lib/praegustator/node.rb
186
+ - lib/praegustator/setup.rb
187
+ - lib/praegustator/test_suite.rb
188
+ - lib/praegustator/version.rb
189
+ - lib/praegustator/wrappers/chef.rb
190
+ - lib/praegustator/wrappers/output_parser.rb
191
+ - lib/praegustator/wrappers/server_spec.rb
192
+ - praegustator.gemspec
193
+ - readme.md
194
+ - spec/praegustator/cli_spec.rb
195
+ - spec/praegustator/dsl_spec.rb
196
+ - spec/praegustator/resources/sample/config/praegustator.yml
197
+ - spec/praegustator/resources/sample/suits/default.rb
198
+ - spec/praegustator/resources/sample/suits/staging.rb
199
+ - spec/praegustator_spec.rb
200
+ - spec/spec_helper.rb
201
+ - test.md
202
+ - tmp/aruba/.praegustator.yml
203
+ homepage: https://github.com/timusg/praegustator
204
+ licenses:
205
+ - MIT
206
+ metadata: {}
207
+ post_install_message:
208
+ rdoc_options: []
209
+ require_paths:
210
+ - lib
211
+ required_ruby_version: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - ! '>='
214
+ - !ruby/object:Gem::Version
215
+ version: '0'
216
+ required_rubygems_version: !ruby/object:Gem::Requirement
217
+ requirements:
218
+ - - ! '>='
219
+ - !ruby/object:Gem::Version
220
+ version: '0'
221
+ requirements: []
222
+ rubyforge_project:
223
+ rubygems_version: 2.1.11
224
+ signing_key:
225
+ specification_version: 4
226
+ summary: DSL over server spec to test chef powered infrastructure
227
+ test_files:
228
+ - features/praegustator.feature
229
+ - features/support/env.rb
230
+ - features/support/setup.rb
231
+ - spec/praegustator/cli_spec.rb
232
+ - spec/praegustator/dsl_spec.rb
233
+ - spec/praegustator/resources/sample/config/praegustator.yml
234
+ - spec/praegustator/resources/sample/suits/default.rb
235
+ - spec/praegustator/resources/sample/suits/staging.rb
236
+ - spec/praegustator_spec.rb
237
+ - spec/spec_helper.rb
238
+ - tmp/aruba/.praegustator.yml
239
+ has_rdoc: