praegustator 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: