knife-inspect 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/HISTORY.md +88 -0
- data/MIT-LICENSE +19 -0
- data/README.md +43 -0
- data/Rakefile +9 -0
- data/knife-inspect.gemspec +27 -0
- data/lib/chef/knife/cookbook_inspect.rb +36 -0
- data/lib/chef/knife/data_bag_inspect.rb +35 -0
- data/lib/chef/knife/environment_inspect.rb +25 -0
- data/lib/chef/knife/inspect.rb +20 -0
- data/lib/chef/knife/role_inspect.rb +25 -0
- data/lib/health_inspector.rb +18 -0
- data/lib/health_inspector/checklists/base.rb +136 -0
- data/lib/health_inspector/checklists/cookbooks.rb +134 -0
- data/lib/health_inspector/checklists/data_bag_items.rb +60 -0
- data/lib/health_inspector/checklists/data_bags.rb +38 -0
- data/lib/health_inspector/checklists/environments.rb +55 -0
- data/lib/health_inspector/checklists/roles.rb +51 -0
- data/lib/health_inspector/color.rb +31 -0
- data/lib/health_inspector/context.rb +27 -0
- data/lib/health_inspector/inspector.rb +20 -0
- data/lib/health_inspector/pairing.rb +73 -0
- data/lib/health_inspector/version.rb +3 -0
- data/spec/chef-repo/.chef/client.pem +0 -0
- data/spec/chef-repo/.chef/knife.rb +7 -0
- data/spec/cookbook_spec.rb +41 -0
- data/spec/data_bag_item_spec.rb +6 -0
- data/spec/data_bag_spec.rb +5 -0
- data/spec/environment_spec.rb +18 -0
- data/spec/role_spec.rb +6 -0
- data/spec/spec_helper.rb +56 -0
- metadata +140 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/HISTORY.md
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
## 0.6.0 ( 2012-11-1 )
|
2
|
+
|
3
|
+
* Add knife plugins for all existing functionality:
|
4
|
+
- knife inspect
|
5
|
+
- knife cookbook inspect [COOKBOOK]
|
6
|
+
- knife data bag inspect [BAG] [ITEM]
|
7
|
+
- knife environment inspect [ENVIRONMENT]
|
8
|
+
- knife role inspect [ROLE]
|
9
|
+
|
10
|
+
* Lost support for quiet-sucess option (We can add that back, or make a quiet
|
11
|
+
options that just returns exit status).
|
12
|
+
|
13
|
+
## 0.5.2 ( 2012-10-19 )
|
14
|
+
|
15
|
+
* Make loading of Chef Config a little more robust.
|
16
|
+
|
17
|
+
## 0.5.1 ( 2012-10-15 )
|
18
|
+
|
19
|
+
* Ignore _default environment if it only exists on the server.
|
20
|
+
|
21
|
+
## 0.5.0 ( 2012-10-14 )
|
22
|
+
|
23
|
+
* Switch to RSpec
|
24
|
+
* Add some test coverage (still needs much more).
|
25
|
+
* Add option to suppress terminal output on successful checks.
|
26
|
+
* Add option to not use ansi color output.
|
27
|
+
* Make cookbook version comparison use Chef's native version class.
|
28
|
+
|
29
|
+
## 0.4.1 ( 2012-09-28 )
|
30
|
+
|
31
|
+
* Fix a bug I created in last release when passing no component.
|
32
|
+
|
33
|
+
## 0.4.0 ( 2012-09-28 )
|
34
|
+
|
35
|
+
* Make `inspect` the default task
|
36
|
+
* Add ability to specify individual components:
|
37
|
+
|
38
|
+
health_inspector inspect cookbooks
|
39
|
+
|
40
|
+
## 0.3.1 ( 2012-09-27 )
|
41
|
+
|
42
|
+
* Stop shelling out for knife commands, use Chef API directly for everything.
|
43
|
+
|
44
|
+
## 0.3.0 ( 2012-09-26 )
|
45
|
+
|
46
|
+
* Add new check for cookbooks: checksum comparison for each file.
|
47
|
+
|
48
|
+
## 0.2.1 ( 2012-09-26 )
|
49
|
+
|
50
|
+
* Fix 1.8.7 incompatibility introduced in last release (String#prepend).
|
51
|
+
|
52
|
+
## 0.2.0 ( 2012-09-25 )
|
53
|
+
|
54
|
+
* Add a better diff output.
|
55
|
+
* Add diff output to data bag items.
|
56
|
+
* Switch to yajl-ruby to fix JSON parsing issues (Chef uses this also).
|
57
|
+
|
58
|
+
## 0.1.0 ( 2012-09-24 )
|
59
|
+
|
60
|
+
* Bump Chef dependency version up to 10.14
|
61
|
+
* Add support for JSON environments.
|
62
|
+
* Add support for JSON roles.
|
63
|
+
* Display the diff between JSONs when JSON data doesn't match.
|
64
|
+
|
65
|
+
## 0.0.6 ( 2012-05-23 )
|
66
|
+
|
67
|
+
* Depend on Chef 0.10.8, since it depends on a later version of the json gem.
|
68
|
+
An earlier version of the json gem was throwing incorrect parse errors.
|
69
|
+
|
70
|
+
## 0.0.5 ( 2012-04-13 )
|
71
|
+
|
72
|
+
* Fix #2, exception when a data bag item json file doesn't exist locally.
|
73
|
+
|
74
|
+
## 0.0.4 ( 2012-04-09 )
|
75
|
+
|
76
|
+
* Add checks for data bags, data bag items, environments, and roles.
|
77
|
+
|
78
|
+
## 0.0.3 ( 2012-03-27 )
|
79
|
+
|
80
|
+
* Read cookbook paths from knife config file instead of hardcoding /cookbooks.
|
81
|
+
|
82
|
+
## 0.0.2 ( 2012-03-27 )
|
83
|
+
|
84
|
+
* Make sure we iterate over actual cookbooks in cookbooks folder.
|
85
|
+
|
86
|
+
## 0.0.1 ( 2012-03-27 )
|
87
|
+
|
88
|
+
* Initial release.
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (C) 2012 Ben Marini
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
[![Build Status](https://secure.travis-ci.org/bmarini/health_inspector.png)](http://travis-ci.org/bmarini/health_inspector)
|
2
|
+
|
3
|
+
## Summary
|
4
|
+
|
5
|
+
`health_inspector` is a knife plugin that inspects your chef repo as it
|
6
|
+
compares to what is on your chef server. You can inspect your entire repo,
|
7
|
+
or individual components.
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
$ gem install health_inspector
|
12
|
+
$ cd [chef repo]
|
13
|
+
|
14
|
+
## Knife Commands
|
15
|
+
|
16
|
+
knife inspect
|
17
|
+
|
18
|
+
knife cookbook inspect
|
19
|
+
knife cookbook inspect [COOKBOOK]
|
20
|
+
|
21
|
+
knife data bag inspect
|
22
|
+
knife data bag inspect [BAG]
|
23
|
+
knife data bag inspect [BAG] [ITEM]
|
24
|
+
|
25
|
+
knife environment inspect
|
26
|
+
knife environment inspect [ENVIRONMENT]
|
27
|
+
|
28
|
+
knife role inspect
|
29
|
+
knife role inspect [ROLE]
|
30
|
+
|
31
|
+
## What it does
|
32
|
+
|
33
|
+
So far it checks if...
|
34
|
+
|
35
|
+
* your cookbooks are in sync
|
36
|
+
* you have uncommitted changes in a cookbook (assuming your cookbooks are in
|
37
|
+
their own git repos)
|
38
|
+
* you have commits in a cookbook that haven't been pushed to your remote
|
39
|
+
(assuming your cookbooks are in their own git repos)
|
40
|
+
* your data bags are in sync
|
41
|
+
* your data bag items are in sync
|
42
|
+
* your environments are in sync
|
43
|
+
* your roles are in sync
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "health_inspector/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "knife-inspect"
|
7
|
+
s.version = HealthInspector::VERSION
|
8
|
+
s.authors = ["Ben Marini"]
|
9
|
+
s.email = ["bmarini@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/bmarini/knife-inspect"
|
11
|
+
s.summary = %q{Inspect your chef repo as is compares to what is on your chef server}
|
12
|
+
s.description = %q{Inspect your chef repo as is compares to what is on your chef server}
|
13
|
+
|
14
|
+
s.rubyforge_project = "knife-inspect"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_development_dependency "rake"
|
22
|
+
s.add_development_dependency "rspec"
|
23
|
+
|
24
|
+
s.add_runtime_dependency "thor"
|
25
|
+
s.add_runtime_dependency "chef", "~> 10.14"
|
26
|
+
s.add_runtime_dependency "yajl-ruby"
|
27
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'chef/knife'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class CookbookInspect < Knife
|
6
|
+
|
7
|
+
deps do
|
8
|
+
require 'health_inspector'
|
9
|
+
require 'chef/json_compat'
|
10
|
+
require 'uri'
|
11
|
+
require 'chef/cookbook_version'
|
12
|
+
end
|
13
|
+
|
14
|
+
banner "knife cookbook inspect [COOKBOOK] (options)"
|
15
|
+
|
16
|
+
def run
|
17
|
+
case @name_args.length
|
18
|
+
when 1 # We are inspecting a cookbook
|
19
|
+
cookbook_name = @name_args[0]
|
20
|
+
# TODO: Support environments
|
21
|
+
# env = config[:environment]
|
22
|
+
# api_endpoint = env ? "environments/#{env}/cookbooks/#{cookbook_name}" : "cookbooks/#{cookbook_name}"
|
23
|
+
|
24
|
+
validator = HealthInspector::Checklists::Cookbooks.new(self)
|
25
|
+
validator.validate_item( validator.load_item(cookbook_name) )
|
26
|
+
when 0 # We are inspecting all the cookbooks
|
27
|
+
HealthInspector::Checklists::Cookbooks.run(self)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'chef/knife'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class DataBagInspect < Knife
|
6
|
+
|
7
|
+
deps do
|
8
|
+
require 'health_inspector'
|
9
|
+
end
|
10
|
+
|
11
|
+
banner "knife data bag inspect [BAG] [ITEM] (options)"
|
12
|
+
|
13
|
+
def run
|
14
|
+
case @name_args.length
|
15
|
+
when 2 # We are inspecting a data bag item
|
16
|
+
bag_name = @name_args[0]
|
17
|
+
item_name = @name_args[1]
|
18
|
+
|
19
|
+
validator = HealthInspector::Checklists::DataBagItems.new(self)
|
20
|
+
validator.validate_item( validator.load_item("#{bag_name}/#{item_name}") )
|
21
|
+
|
22
|
+
when 1 # We are inspecting a data bag
|
23
|
+
bag_name = @name_args[0]
|
24
|
+
|
25
|
+
validator = HealthInspector::Checklists::DataBags.new(self)
|
26
|
+
validator.validate_item( validator.load_item(bag_name) )
|
27
|
+
|
28
|
+
when 0 # We are inspecting all the data bags
|
29
|
+
HealthInspector::Checklists::DataBags.run(self)
|
30
|
+
HealthInspector::Checklists::DataBagItems.run(self)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'chef/knife'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class EnvironmentInspect < Knife
|
6
|
+
|
7
|
+
deps do
|
8
|
+
require 'health_inspector'
|
9
|
+
end
|
10
|
+
|
11
|
+
banner "knife environment inspect [ENVIRONMENT] (options)"
|
12
|
+
|
13
|
+
def run
|
14
|
+
case @name_args.length
|
15
|
+
when 1 # We are inspecting a environment
|
16
|
+
environment_name = @name_args[0]
|
17
|
+
validator = HealthInspector::Checklists::Environments.new(self)
|
18
|
+
validator.validate_item( validator.load_item(environment_name) )
|
19
|
+
when 0 # We are inspecting all the environments
|
20
|
+
HealthInspector::Checklists::Environments.run(self)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'chef/knife'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class Inspect < Knife
|
6
|
+
|
7
|
+
deps do
|
8
|
+
require "health_inspector"
|
9
|
+
end
|
10
|
+
|
11
|
+
banner "knife inspect"
|
12
|
+
|
13
|
+
def run
|
14
|
+
%w[ Cookbooks DataBags DataBagItems Environments Roles ].each do |checklist|
|
15
|
+
HealthInspector::Checklists.const_get(checklist).run(self)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'chef/knife'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class RoleInspect < Knife
|
6
|
+
|
7
|
+
deps do
|
8
|
+
require 'health_inspector'
|
9
|
+
end
|
10
|
+
|
11
|
+
banner "knife role inspect [ROLE] (options)"
|
12
|
+
|
13
|
+
def run
|
14
|
+
case @name_args.length
|
15
|
+
when 1 # We are inspecting a role
|
16
|
+
role_name = @name_args[0]
|
17
|
+
validator = HealthInspector::Checklists::Roles.new(self)
|
18
|
+
validator.validate_item( validator.load_item(role_name) )
|
19
|
+
when 0 # We are inspecting all the roles
|
20
|
+
HealthInspector::Checklists::Roles.run(self)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require "health_inspector/version"
|
3
|
+
require "health_inspector/color"
|
4
|
+
require "health_inspector/context"
|
5
|
+
require "health_inspector/pairing"
|
6
|
+
require "health_inspector/inspector"
|
7
|
+
require "health_inspector/checklists/base"
|
8
|
+
require "health_inspector/checklists/cookbooks"
|
9
|
+
require "health_inspector/checklists/data_bags"
|
10
|
+
require "health_inspector/checklists/data_bag_items"
|
11
|
+
require "health_inspector/checklists/environments"
|
12
|
+
require "health_inspector/checklists/roles"
|
13
|
+
require 'chef/rest'
|
14
|
+
require 'chef/checksum_cache'
|
15
|
+
require 'chef/version'
|
16
|
+
|
17
|
+
module HealthInspector
|
18
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require "pathname"
|
3
|
+
|
4
|
+
module HealthInspector
|
5
|
+
module Checklists
|
6
|
+
class Base
|
7
|
+
include Color
|
8
|
+
|
9
|
+
class << self
|
10
|
+
attr_reader :title
|
11
|
+
|
12
|
+
def title(val=nil)
|
13
|
+
val.nil? ? @title : @title = val
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.run(knife)
|
18
|
+
new(knife).run
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(knife)
|
22
|
+
@context = Context.new(knife)
|
23
|
+
end
|
24
|
+
|
25
|
+
def ui
|
26
|
+
@context.knife.ui
|
27
|
+
end
|
28
|
+
|
29
|
+
def all_item_names
|
30
|
+
( server_items + local_items ).uniq.sort
|
31
|
+
end
|
32
|
+
|
33
|
+
# Subclasses should collect all items from the server and the local repo,
|
34
|
+
# and for each item pair, yield an object that contains a reference to
|
35
|
+
# the server item, and the local repo item. A reference can be nil if it does
|
36
|
+
# not exist in one of the locations.
|
37
|
+
def each_item
|
38
|
+
raise NotImplementedError, "You must implement this method in a subclass"
|
39
|
+
end
|
40
|
+
|
41
|
+
def run
|
42
|
+
banner "Inspecting #{self.class.title}"
|
43
|
+
|
44
|
+
each_item do |item|
|
45
|
+
validate_item(item)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def validate_item(item)
|
50
|
+
item.validate
|
51
|
+
failures = item.errors
|
52
|
+
|
53
|
+
if failures.empty?
|
54
|
+
print_success(item.name) # unless @context.quiet_success
|
55
|
+
else
|
56
|
+
print_failures(item.name, failures)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def banner(message)
|
61
|
+
ui.msg ""
|
62
|
+
ui.msg message
|
63
|
+
ui.msg "-" * 80
|
64
|
+
end
|
65
|
+
|
66
|
+
def print_success(subject)
|
67
|
+
ui.msg color('bright pass', "✓") + " #{subject}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def print_failures(subject, failures)
|
71
|
+
ui.msg color('bright fail', "- #{subject}")
|
72
|
+
|
73
|
+
failures.each do |message|
|
74
|
+
if message.kind_of? Hash
|
75
|
+
puts color('bright yellow'," has the following values mismatched on the server and repo\n")
|
76
|
+
print_failures_from_hash(message)
|
77
|
+
else
|
78
|
+
puts color('bright yellow', " #{message}")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def print_failures_from_hash(message, depth=2)
|
84
|
+
message.keys.each do |key|
|
85
|
+
print_key(key,depth)
|
86
|
+
|
87
|
+
if message[key].include? "server"
|
88
|
+
print_value_diff(message[key],depth)
|
89
|
+
message[key].delete_if { |k,v| k == "server" || "local" }
|
90
|
+
print_failures_from_hash(message[key], depth + 1) unless message[key].empty?
|
91
|
+
else
|
92
|
+
print_failures_from_hash(message[key], depth + 1)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def print_key(key, depth)
|
98
|
+
ui.msg indent( color('bright yellow',"#{key} : "), depth )
|
99
|
+
end
|
100
|
+
|
101
|
+
def print_value_diff(value, depth)
|
102
|
+
print indent( color('bright fail',"server value = "), depth + 1 )
|
103
|
+
print value["server"]
|
104
|
+
print "\n"
|
105
|
+
print indent( color('bright fail',"local value = "), depth + 1 )
|
106
|
+
print value["local"]
|
107
|
+
print "\n\n"
|
108
|
+
end
|
109
|
+
|
110
|
+
def load_ruby_or_json_from_local(chef_class, folder, name)
|
111
|
+
path_template = "#{@context.repo_path}/#{folder}/#{name}.%s"
|
112
|
+
ruby_pathname = Pathname.new(path_template % "rb")
|
113
|
+
json_pathname = Pathname.new(path_template % "json")
|
114
|
+
js_pathname = Pathname.new(path_template % "js")
|
115
|
+
|
116
|
+
if ruby_pathname.exist?
|
117
|
+
instance = chef_class.new
|
118
|
+
instance.from_file(ruby_pathname.to_s)
|
119
|
+
elsif json_pathname.exist?
|
120
|
+
instance = chef_class.json_create( Yajl::Parser.parse( json_pathname.read ) )
|
121
|
+
elsif js_pathname.exist?
|
122
|
+
instance = chef_class.json_create( Yajl::Parser.parse( js_pathname.read ) )
|
123
|
+
end
|
124
|
+
|
125
|
+
instance ? instance.to_hash : nil
|
126
|
+
rescue IOError
|
127
|
+
nil
|
128
|
+
end
|
129
|
+
|
130
|
+
def indent(string, depth)
|
131
|
+
(' ' * 2 * depth) + string
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
module HealthInspector
|
2
|
+
module Checklists
|
3
|
+
|
4
|
+
class Cookbook < Pairing
|
5
|
+
include ExistenceValidations
|
6
|
+
|
7
|
+
def validate_versions
|
8
|
+
if versions_exist? && !versions_match?
|
9
|
+
errors.add "chef server has #{server} but local version is #{local}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def validate_uncommited_changes
|
14
|
+
if git_repo?
|
15
|
+
result = `cd #{cookbook_path} && git status -s`
|
16
|
+
|
17
|
+
unless result.empty?
|
18
|
+
errors.add "Uncommitted changes:\n#{result.chomp}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def validate_commits_not_pushed_to_remote
|
24
|
+
if git_repo?
|
25
|
+
result = `cd #{cookbook_path} && git status`
|
26
|
+
|
27
|
+
if result =~ /Your branch is ahead of (.+)/
|
28
|
+
errors.add "ahead of #{$1}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# TODO: Check files that exist locally but not in manifest on server
|
34
|
+
def validate_changes_on_the_server_not_in_the_repo
|
35
|
+
if versions_exist? && versions_match?
|
36
|
+
|
37
|
+
begin
|
38
|
+
cookbook = context.rest.get_rest("/cookbooks/#{name}/#{local}")
|
39
|
+
messages = []
|
40
|
+
|
41
|
+
Chef::CookbookVersion::COOKBOOK_SEGMENTS.each do |segment|
|
42
|
+
cookbook.manifest[segment].each do |manifest_record|
|
43
|
+
path = cookbook_path.join("#{manifest_record["path"]}")
|
44
|
+
|
45
|
+
if path.exist?
|
46
|
+
checksum = checksum_cookbook_file(path)
|
47
|
+
messages << "#{manifest_record['path']}" if checksum != manifest_record['checksum']
|
48
|
+
else
|
49
|
+
messages << "#{manifest_record['path']} does not exist in the repo"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
unless messages.empty?
|
55
|
+
message = "has a checksum mismatch between server and repo in\n"
|
56
|
+
message << messages.map { |f| " #{f}" }.join("\n")
|
57
|
+
errors.add message
|
58
|
+
end
|
59
|
+
|
60
|
+
rescue Net::HTTPServerException => e
|
61
|
+
errors.add "Could not find cookbook #{name} on the server"
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def versions_exist?
|
68
|
+
local && server
|
69
|
+
end
|
70
|
+
|
71
|
+
def versions_match?
|
72
|
+
local == server
|
73
|
+
end
|
74
|
+
|
75
|
+
def git_repo?
|
76
|
+
cookbook_path && File.exist?("#{cookbook_path}/.git")
|
77
|
+
end
|
78
|
+
|
79
|
+
def cookbook_path
|
80
|
+
path = context.cookbook_path.find { |f| File.exist?("#{f}/#{name}") }
|
81
|
+
path ? Pathname.new(path).join(name) : nil
|
82
|
+
end
|
83
|
+
|
84
|
+
def checksum_cookbook_file(filepath)
|
85
|
+
Chef::CookbookVersion.checksum_cookbook_file(filepath)
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
class Cookbooks < Base
|
91
|
+
|
92
|
+
title "cookbooks"
|
93
|
+
|
94
|
+
def each_item
|
95
|
+
all_cookbook_names = ( server_cookbooks.keys + local_cookbooks.keys ).uniq.sort
|
96
|
+
|
97
|
+
all_cookbook_names.each do |name|
|
98
|
+
yield load_item(name)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def load_item(name)
|
103
|
+
Cookbook.new(@context,
|
104
|
+
:name => name,
|
105
|
+
:server => server_cookbooks[name],
|
106
|
+
:local => local_cookbooks[name]
|
107
|
+
)
|
108
|
+
end
|
109
|
+
|
110
|
+
def server_cookbooks
|
111
|
+
@context.rest.get_rest("/cookbooks").inject({}) do |hsh, (name,version)|
|
112
|
+
hsh[name] = Chef::Version.new(version["versions"].first["version"])
|
113
|
+
hsh
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def local_cookbooks
|
118
|
+
@context.cookbook_path.
|
119
|
+
map { |path| Dir["#{path}/*"] }.
|
120
|
+
flatten.
|
121
|
+
select { |path| File.exists?("#{path}/metadata.rb") }.
|
122
|
+
inject({}) do |hsh, path|
|
123
|
+
|
124
|
+
name = File.basename(path)
|
125
|
+
version = (`grep '^version' #{path}/metadata.rb`).split.last[1...-1]
|
126
|
+
|
127
|
+
hsh[name] = Chef::Version.new(version)
|
128
|
+
hsh
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require "chef/data_bag"
|
2
|
+
|
3
|
+
module HealthInspector
|
4
|
+
module Checklists
|
5
|
+
class DataBagItem < Pairing
|
6
|
+
include ExistenceValidations
|
7
|
+
include JsonValidations
|
8
|
+
end
|
9
|
+
|
10
|
+
class DataBagItems < Base
|
11
|
+
title "data bag items"
|
12
|
+
|
13
|
+
def each_item
|
14
|
+
all_item_names.each do |name|
|
15
|
+
yield load_item(name)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def load_item(name)
|
20
|
+
DataBagItem.new(@context,
|
21
|
+
:name => name,
|
22
|
+
:server => load_item_from_server(name),
|
23
|
+
:local => load_item_from_local(name)
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
def server_items
|
28
|
+
@server_items ||= Chef::DataBag.list.keys.map do |bag_name|
|
29
|
+
[ bag_name, Chef::DataBag.load(bag_name) ]
|
30
|
+
end.inject([]) do |arr, (bag_name, data_bag)|
|
31
|
+
arr += data_bag.keys.map { |item_name| "#{bag_name}/#{item_name}"}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def local_items
|
36
|
+
entries = nil
|
37
|
+
|
38
|
+
Dir.chdir("#{@context.repo_path}/data_bags") do
|
39
|
+
entries = Dir["**/*.json"].map { |entry| entry.gsub('.json', '') }
|
40
|
+
end
|
41
|
+
|
42
|
+
return entries
|
43
|
+
end
|
44
|
+
|
45
|
+
def load_item_from_server(name)
|
46
|
+
bag_name, item_name = name.split("/")
|
47
|
+
Chef::DataBagItem.load(bag_name, item_name).raw_data
|
48
|
+
rescue
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def load_item_from_local(name)
|
53
|
+
Yajl::Parser.parse( File.read("#{@context.repo_path}/data_bags/#{name}.json") )
|
54
|
+
rescue IOError, Errno::ENOENT
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "chef/data_bag"
|
2
|
+
|
3
|
+
module HealthInspector
|
4
|
+
module Checklists
|
5
|
+
class DataBag < Pairing
|
6
|
+
include ExistenceValidations
|
7
|
+
end
|
8
|
+
|
9
|
+
class DataBags < Base
|
10
|
+
title "data bags"
|
11
|
+
|
12
|
+
def each_item
|
13
|
+
all_item_names.each do |name|
|
14
|
+
yield load_item(name)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def load_item(name)
|
19
|
+
DataBag.new(@context,
|
20
|
+
:name => name,
|
21
|
+
:server => server_items.include?(name),
|
22
|
+
:local => local_items.include?(name)
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def server_items
|
27
|
+
@server_items ||= Chef::DataBag.list.keys
|
28
|
+
end
|
29
|
+
|
30
|
+
def local_items
|
31
|
+
@local_items ||= Dir["#{@context.repo_path}/data_bags/*"].entries.
|
32
|
+
select { |e| File.directory?(e) }.
|
33
|
+
map { |e| File.basename(e) }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "chef/environment"
|
2
|
+
|
3
|
+
module HealthInspector
|
4
|
+
module Checklists
|
5
|
+
class Environment < Pairing
|
6
|
+
include ExistenceValidations
|
7
|
+
include JsonValidations
|
8
|
+
|
9
|
+
# Override to ignore _default environment if it is missing locally
|
10
|
+
def validate_local_copy_exists
|
11
|
+
super unless name == '_default'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Environments < Base
|
16
|
+
title "environments"
|
17
|
+
|
18
|
+
def each_item
|
19
|
+
all_item_names.each do |name|
|
20
|
+
yield load_item(name)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def load_item(name)
|
25
|
+
Environment.new(@context,
|
26
|
+
:name => name,
|
27
|
+
:server => load_item_from_server(name),
|
28
|
+
:local => load_item_from_local(name)
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
def server_items
|
33
|
+
@server_items ||= Chef::Environment.list.keys
|
34
|
+
end
|
35
|
+
|
36
|
+
def local_items
|
37
|
+
Dir.chdir("#{@context.repo_path}/environments") do
|
38
|
+
Dir["*.{rb,json,js}"].map { |e| e.gsub(/\.(rb|json|js)/,"") }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def load_item_from_server(name)
|
43
|
+
env = Chef::Environment.load(name)
|
44
|
+
env.to_hash
|
45
|
+
rescue
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def load_item_from_local(name)
|
50
|
+
load_ruby_or_json_from_local(Chef::Environment, "environments", name)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "chef/role"
|
2
|
+
require 'yajl'
|
3
|
+
|
4
|
+
module HealthInspector
|
5
|
+
module Checklists
|
6
|
+
class Role < Pairing
|
7
|
+
include ExistenceValidations
|
8
|
+
include JsonValidations
|
9
|
+
end
|
10
|
+
|
11
|
+
class Roles < Base
|
12
|
+
title "roles"
|
13
|
+
|
14
|
+
def each_item
|
15
|
+
all_item_names.each do |name|
|
16
|
+
yield load_item(name)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def load_item(name)
|
21
|
+
Role.new(@context,
|
22
|
+
:name => name,
|
23
|
+
:server => load_item_from_server(name),
|
24
|
+
:local => load_item_from_local(name)
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
def server_items
|
29
|
+
@server_items ||= Chef::Role.list.keys
|
30
|
+
end
|
31
|
+
|
32
|
+
def local_items
|
33
|
+
Dir.chdir("#{@context.repo_path}/roles") do
|
34
|
+
Dir["*.{rb,json,js}"].map { |e| e.gsub(/\.(rb|json|js)/, '') }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def load_item_from_server(name)
|
39
|
+
role = Chef::Role.load(name)
|
40
|
+
role.to_hash
|
41
|
+
rescue
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def load_item_from_local(name)
|
46
|
+
load_ruby_or_json_from_local(Chef::Role, "roles", name)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module HealthInspector
|
2
|
+
module Color
|
3
|
+
|
4
|
+
# TODO: Use a highline color scheme here instead
|
5
|
+
def color(type, str)
|
6
|
+
colors = {
|
7
|
+
'pass' => [:green],# 90,
|
8
|
+
'fail' => [:red],# 31,
|
9
|
+
'bright pass' => [:bold, :green],# 92,
|
10
|
+
'bright fail' => [:bold, :red],# 91,
|
11
|
+
'bright yellow' => [:bold, :yellow],# 93,
|
12
|
+
'pending' => [:yellow],# 36,
|
13
|
+
'suite' => [],# 0,
|
14
|
+
'error title' => [],# 0,
|
15
|
+
'error message' => [:red],# 31,
|
16
|
+
'error stack' => [:green],# 90,
|
17
|
+
'checkmark' => [:green],# 32,
|
18
|
+
'fast' => [:green],# 90,
|
19
|
+
'medium' => [:green],# 33,
|
20
|
+
'slow' => [:red],# 31,
|
21
|
+
'green' => [:green],# 32,
|
22
|
+
'light' => [:green],# 90,
|
23
|
+
'diff gutter' => [:green],# 90,
|
24
|
+
'diff added' => [:green],# 42,
|
25
|
+
'diff removed' => [:red]# 41
|
26
|
+
}
|
27
|
+
|
28
|
+
@context.knife.ui.color( str, *colors[type] )
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "chef/config"
|
2
|
+
|
3
|
+
module HealthInspector
|
4
|
+
class Context
|
5
|
+
attr_accessor :knife
|
6
|
+
|
7
|
+
def initialize(knife)
|
8
|
+
@knife = knife
|
9
|
+
end
|
10
|
+
|
11
|
+
def cookbook_path
|
12
|
+
Array( config.cookbook_path )
|
13
|
+
end
|
14
|
+
|
15
|
+
def config
|
16
|
+
Chef::Config
|
17
|
+
end
|
18
|
+
|
19
|
+
def rest
|
20
|
+
@knife.rest
|
21
|
+
end
|
22
|
+
|
23
|
+
def repo_path
|
24
|
+
ENV['PWD']
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module HealthInspector
|
2
|
+
class Inspector
|
3
|
+
def self.inspect(checklists, options)
|
4
|
+
new(options).inspect( checklists )
|
5
|
+
end
|
6
|
+
|
7
|
+
def initialize(options)
|
8
|
+
@context = Context.new( options[:repopath], options[:configpath] )
|
9
|
+
@context.quiet_success = options[:'quiet-success']
|
10
|
+
@context.no_color = options[:'no-color']
|
11
|
+
@context.configure
|
12
|
+
end
|
13
|
+
|
14
|
+
def inspect(checklists)
|
15
|
+
checklists.each do |checklist|
|
16
|
+
Checklists.const_get(checklist).run(@context) if Checklists.const_defined?(checklist)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module HealthInspector
|
2
|
+
class Errors
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@errors = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def add(message)
|
10
|
+
@errors << message
|
11
|
+
end
|
12
|
+
|
13
|
+
def each
|
14
|
+
@errors.each { |e| yield(e) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def empty?
|
18
|
+
@errors.empty?
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Pairing
|
23
|
+
attr_accessor :name, :local, :server
|
24
|
+
attr_reader :context, :errors
|
25
|
+
|
26
|
+
def initialize(context, opts={})
|
27
|
+
@context = context
|
28
|
+
@name = opts[:name]
|
29
|
+
@local = opts[:local]
|
30
|
+
@server = opts[:server]
|
31
|
+
|
32
|
+
@validations = []
|
33
|
+
@errors = Errors.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def validate
|
37
|
+
self.methods.grep(/^validate_/).each { |meth| send(meth) }
|
38
|
+
end
|
39
|
+
|
40
|
+
def hash_diff(original, other)
|
41
|
+
(original.keys + other.keys).uniq.inject({}) do |memo, key|
|
42
|
+
unless original[key] == other[key]
|
43
|
+
if original[key].kind_of?(Hash) && other[key].kind_of?(Hash)
|
44
|
+
memo[key] = hash_diff(original[key], other[key])
|
45
|
+
else
|
46
|
+
memo[key] = {"server" => original[key],"local" => other[key]}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
memo
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Mixins for common validations across pairings
|
55
|
+
module ExistenceValidations
|
56
|
+
def validate_local_copy_exists
|
57
|
+
errors.add "exists on server but not locally" if local.nil?
|
58
|
+
end
|
59
|
+
|
60
|
+
def validate_server_copy_exists
|
61
|
+
errors.add "exists locally but not on server" if server.nil?
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
module JsonValidations
|
66
|
+
def validate_items_are_the_same
|
67
|
+
if server && local
|
68
|
+
differences = hash_diff(server, local)
|
69
|
+
errors.add differences unless differences.empty?
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
File without changes
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe HealthInspector::Checklists::Cookbook do
|
4
|
+
let(:pairing) { described_class.new(health_inspector_context, :name => "dummy") }
|
5
|
+
|
6
|
+
it "should detect if an item does not exist locally" do
|
7
|
+
pairing.server = "0.0.1"
|
8
|
+
pairing.local = nil
|
9
|
+
pairing.validate
|
10
|
+
|
11
|
+
pairing.errors.should_not be_empty
|
12
|
+
pairing.errors.first.should == "exists on server but not locally"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should detect if an item does not exist on server" do
|
16
|
+
pairing.server = nil
|
17
|
+
pairing.local = "0.0.1"
|
18
|
+
pairing.validate
|
19
|
+
|
20
|
+
pairing.errors.should_not be_empty
|
21
|
+
pairing.errors.first.should == "exists locally but not on server"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should detect if an item is different" do
|
25
|
+
pairing.server = "0.0.1"
|
26
|
+
pairing.local = "0.0.2"
|
27
|
+
pairing.validate
|
28
|
+
|
29
|
+
pairing.errors.should_not be_empty
|
30
|
+
pairing.errors.first.should == "chef server has 0.0.1 but local version is 0.0.2"
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should detect if an item is the same" do
|
34
|
+
pairing.should_receive(:validate_changes_on_the_server_not_in_the_repo)
|
35
|
+
pairing.server = "0.0.1"
|
36
|
+
pairing.local = "0.0.1"
|
37
|
+
pairing.validate
|
38
|
+
|
39
|
+
pairing.errors.should be_empty
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe HealthInspector::Checklists::Environment do
|
4
|
+
let(:pairing) { described_class.new(health_inspector_context) }
|
5
|
+
|
6
|
+
it_behaves_like "a chef model"
|
7
|
+
it_behaves_like "a chef model that can be respresented in json"
|
8
|
+
|
9
|
+
it "should ignore _default environment if it only exists on server" do
|
10
|
+
pairing.name = "_default"
|
11
|
+
pairing.server = {}
|
12
|
+
pairing.local = nil
|
13
|
+
pairing.validate
|
14
|
+
|
15
|
+
pairing.errors.should be_empty
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
data/spec/role_spec.rb
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'health_inspector'
|
4
|
+
|
5
|
+
module HealthInspector::SpecHelpers
|
6
|
+
def health_inspector_context
|
7
|
+
@health_inspector_context ||= HealthInspector::Context.new(nil)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
RSpec.configure do |c|
|
12
|
+
c.include HealthInspector::SpecHelpers
|
13
|
+
end
|
14
|
+
|
15
|
+
shared_examples "a chef model" do
|
16
|
+
let(:pairing) { described_class.new(health_inspector_context, :name => "dummy") }
|
17
|
+
|
18
|
+
it "should detect if an item does not exist locally" do
|
19
|
+
pairing.server = {}
|
20
|
+
pairing.local = nil
|
21
|
+
pairing.validate
|
22
|
+
|
23
|
+
pairing.errors.should_not be_empty
|
24
|
+
pairing.errors.first.should == "exists on server but not locally"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should detect if an item does not exist on server" do
|
28
|
+
pairing.server = nil
|
29
|
+
pairing.local = {}
|
30
|
+
pairing.validate
|
31
|
+
|
32
|
+
pairing.errors.should_not be_empty
|
33
|
+
pairing.errors.first.should == "exists locally but not on server"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
shared_examples "a chef model that can be respresented in json" do
|
38
|
+
let(:pairing) { described_class.new(health_inspector_context, :name => "dummy") }
|
39
|
+
|
40
|
+
it "should detect if an item is different" do
|
41
|
+
pairing.server = {"foo" => "bar"}
|
42
|
+
pairing.local = {"foo" => "baz"}
|
43
|
+
pairing.validate
|
44
|
+
|
45
|
+
pairing.errors.should_not be_empty
|
46
|
+
pairing.errors.first.should == {"foo"=>{"server"=>"bar", "local"=>"baz"}}
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should detect if an item is the same" do
|
50
|
+
pairing.server = {"foo" => "bar"}
|
51
|
+
pairing.local = {"foo" => "bar"}
|
52
|
+
pairing.validate
|
53
|
+
|
54
|
+
pairing.errors.should be_empty
|
55
|
+
end
|
56
|
+
end
|
metadata
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: knife-inspect
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ben Marini
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-02 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: &70274490577920 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70274490577920
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
requirement: &70274490573900 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70274490573900
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: thor
|
38
|
+
requirement: &70274490581800 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70274490581800
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: chef
|
49
|
+
requirement: &70274490608360 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.14'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70274490608360
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: yajl-ruby
|
60
|
+
requirement: &70274490625560 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :runtime
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70274490625560
|
69
|
+
description: Inspect your chef repo as is compares to what is on your chef server
|
70
|
+
email:
|
71
|
+
- bmarini@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- .gitignore
|
77
|
+
- Gemfile
|
78
|
+
- HISTORY.md
|
79
|
+
- MIT-LICENSE
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- knife-inspect.gemspec
|
83
|
+
- lib/chef/knife/cookbook_inspect.rb
|
84
|
+
- lib/chef/knife/data_bag_inspect.rb
|
85
|
+
- lib/chef/knife/environment_inspect.rb
|
86
|
+
- lib/chef/knife/inspect.rb
|
87
|
+
- lib/chef/knife/role_inspect.rb
|
88
|
+
- lib/health_inspector.rb
|
89
|
+
- lib/health_inspector/checklists/base.rb
|
90
|
+
- lib/health_inspector/checklists/cookbooks.rb
|
91
|
+
- lib/health_inspector/checklists/data_bag_items.rb
|
92
|
+
- lib/health_inspector/checklists/data_bags.rb
|
93
|
+
- lib/health_inspector/checklists/environments.rb
|
94
|
+
- lib/health_inspector/checklists/roles.rb
|
95
|
+
- lib/health_inspector/color.rb
|
96
|
+
- lib/health_inspector/context.rb
|
97
|
+
- lib/health_inspector/inspector.rb
|
98
|
+
- lib/health_inspector/pairing.rb
|
99
|
+
- lib/health_inspector/version.rb
|
100
|
+
- spec/chef-repo/.chef/client.pem
|
101
|
+
- spec/chef-repo/.chef/knife.rb
|
102
|
+
- spec/cookbook_spec.rb
|
103
|
+
- spec/data_bag_item_spec.rb
|
104
|
+
- spec/data_bag_spec.rb
|
105
|
+
- spec/environment_spec.rb
|
106
|
+
- spec/role_spec.rb
|
107
|
+
- spec/spec_helper.rb
|
108
|
+
homepage: https://github.com/bmarini/knife-inspect
|
109
|
+
licenses: []
|
110
|
+
post_install_message:
|
111
|
+
rdoc_options: []
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ! '>='
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
requirements: []
|
127
|
+
rubyforge_project: knife-inspect
|
128
|
+
rubygems_version: 1.8.11
|
129
|
+
signing_key:
|
130
|
+
specification_version: 3
|
131
|
+
summary: Inspect your chef repo as is compares to what is on your chef server
|
132
|
+
test_files:
|
133
|
+
- spec/chef-repo/.chef/client.pem
|
134
|
+
- spec/chef-repo/.chef/knife.rb
|
135
|
+
- spec/cookbook_spec.rb
|
136
|
+
- spec/data_bag_item_spec.rb
|
137
|
+
- spec/data_bag_spec.rb
|
138
|
+
- spec/environment_spec.rb
|
139
|
+
- spec/role_spec.rb
|
140
|
+
- spec/spec_helper.rb
|