knife-spork-berks 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/.gitignore +19 -0
  2. data/.ruby-gemset +1 -0
  3. data/.ruby-version +1 -0
  4. data/CHANGELOG.md +284 -0
  5. data/Gemfile +3 -0
  6. data/LICENSE +35 -0
  7. data/README.md +406 -0
  8. data/Rakefile +2 -0
  9. data/knife-spork-berks.gemspec +22 -0
  10. data/lib/chef/knife/spork-bump.rb +86 -0
  11. data/lib/chef/knife/spork-check.rb +149 -0
  12. data/lib/chef/knife/spork-databag-create.rb +53 -0
  13. data/lib/chef/knife/spork-databag-delete.rb +51 -0
  14. data/lib/chef/knife/spork-databag-edit.rb +54 -0
  15. data/lib/chef/knife/spork-databag-fromfile.rb +82 -0
  16. data/lib/chef/knife/spork-environment-create.rb +43 -0
  17. data/lib/chef/knife/spork-environment-delete.rb +36 -0
  18. data/lib/chef/knife/spork-environment-edit.rb +37 -0
  19. data/lib/chef/knife/spork-environment-fromfile.rb +42 -0
  20. data/lib/chef/knife/spork-info.rb +30 -0
  21. data/lib/chef/knife/spork-node-create.rb +38 -0
  22. data/lib/chef/knife/spork-node-delete.rb +38 -0
  23. data/lib/chef/knife/spork-node-edit.rb +45 -0
  24. data/lib/chef/knife/spork-node-fromfile.rb +43 -0
  25. data/lib/chef/knife/spork-node-runlistadd.rb +48 -0
  26. data/lib/chef/knife/spork-node-runlistremove.rb +42 -0
  27. data/lib/chef/knife/spork-node-runlistset.rb +42 -0
  28. data/lib/chef/knife/spork-omni.rb +107 -0
  29. data/lib/chef/knife/spork-promote.rb +166 -0
  30. data/lib/chef/knife/spork-role-create.rb +43 -0
  31. data/lib/chef/knife/spork-role-delete.rb +36 -0
  32. data/lib/chef/knife/spork-role-edit.rb +37 -0
  33. data/lib/chef/knife/spork-role-fromfile.rb +42 -0
  34. data/lib/chef/knife/spork-upload.rb +129 -0
  35. data/lib/knife-spork.rb +3 -0
  36. data/lib/knife-spork/plugins.rb +25 -0
  37. data/lib/knife-spork/plugins/campfire.rb +211 -0
  38. data/lib/knife-spork/plugins/eventinator.rb +317 -0
  39. data/lib/knife-spork/plugins/foodcritic.rb +46 -0
  40. data/lib/knife-spork/plugins/git.rb +197 -0
  41. data/lib/knife-spork/plugins/graphite.rb +25 -0
  42. data/lib/knife-spork/plugins/grove.rb +161 -0
  43. data/lib/knife-spork/plugins/hipchat.rb +131 -0
  44. data/lib/knife-spork/plugins/irccat.rb +298 -0
  45. data/lib/knife-spork/plugins/jabber.rb +129 -0
  46. data/lib/knife-spork/plugins/plugin.rb +105 -0
  47. data/lib/knife-spork/plugins/statusnet.rb +118 -0
  48. data/lib/knife-spork/runner.rb +277 -0
  49. data/plugins/Campfire.md +43 -0
  50. data/plugins/Eventinator.md +30 -0
  51. data/plugins/Foodcritic.md +53 -0
  52. data/plugins/Git.md +46 -0
  53. data/plugins/Graphite.md +30 -0
  54. data/plugins/Grove.md +31 -0
  55. data/plugins/HipChat.md +50 -0
  56. data/plugins/Irccat.md +50 -0
  57. data/plugins/Jabber.md +61 -0
  58. data/plugins/README.md +70 -0
  59. data/plugins/StatusNet.md +41 -0
  60. data/plugins/Template.md +34 -0
  61. metadata +170 -0
@@ -0,0 +1,118 @@
1
+ require 'knife-spork/plugins/plugin'
2
+
3
+ module KnifeSpork
4
+ module Plugins
5
+ class StatusNet < Plugin
6
+ name :statusnet
7
+
8
+ def perform; end
9
+
10
+ def after_upload
11
+ statusnet "#{organization}#{current_user} uploaded the following cookbooks:\n#{cookbooks.collect{ |c| " #{c.name}@#{c.version}" }.join("\n")}"
12
+ end
13
+
14
+ def after_promote_remote
15
+ statusnet "#{organization}#{current_user} promoted the following cookbooks:\n#{cookbooks.collect{ |c| " #{c.name}@#{c.version}" }.join("\n")} to #{environments.collect{ |e| "#{e.name}" }.join(", ")}"
16
+ end
17
+
18
+ def after_environmentfromfile
19
+ statusnet "#{organization}#{current_user} uploaded environment #{object_name}"
20
+ end
21
+
22
+ def after_environmentedit
23
+ statusnet "#{organization}#{current_user} edited environment #{object_name}"
24
+ end
25
+
26
+ def after_environmentcreate
27
+ statusnet "#{organization}#{current_user} created environment #{object_name}"
28
+ end
29
+
30
+ def after_environmentdelete
31
+ statusnet "#{organization}#{current_user} deleted environment #{object_name}"
32
+ end
33
+
34
+ def after_rolefromfile
35
+ statusnet "#{organization}#{current_user} uploaded role #{object_name}"
36
+ end
37
+
38
+ def after_roleedit
39
+ statusnet "#{organization}#{current_user} edited role #{object_name}"
40
+ end
41
+
42
+ def after_rolecreate
43
+ statusnet "#{organization}#{current_user} created role #{object_name}"
44
+ end
45
+
46
+ def after_roledelete
47
+ statusnet "#{organization}#{current_user} deleted role #{object_name}"
48
+ end
49
+
50
+ def after_databagedit
51
+ statusnet "#{organization}#{current_user} edited data bag item #{object_name}:#{object_secondary_name}"
52
+ end
53
+
54
+ def after_databagcreate
55
+ statusnet "#{organization}#{current_user} created data bag #{object_name}"
56
+ end
57
+
58
+ def after_databagdelete
59
+ statusnet "#{organization}#{current_user} deleted data bag #{object_name}"
60
+ end
61
+
62
+ def after_databagitemdelete
63
+ statusnet "#{organization}#{current_user} deleted data bag item #{object_name}:#{object_secondary_name}"
64
+ end
65
+
66
+ def after_databagfromfile
67
+ statusnet "#{organization}#{current_user} uploaded data bag item #{object_name}:#{object_secondary_name}"
68
+ end
69
+
70
+ def after_nodeedit
71
+ statusnet "#{organization}#{current_user} edited node #{object_name}"
72
+ end
73
+
74
+ def after_nodedelete
75
+ statusnet "#{organization}#{current_user} deleted node #{object_name}"
76
+ end
77
+
78
+ def after_nodecreate
79
+ statusnet "#{organization}#{current_user} created node #{object_name}"
80
+ end
81
+
82
+ def after_nodefromfile
83
+ statusnet "#{organization}#{current_user} uploaded node #{object_name}"
84
+ end
85
+
86
+ def after_noderunlistadd
87
+ statusnet "#{organization}#{current_user} added run_list items to #{object_name}: #{object_secondary_name}"
88
+ end
89
+
90
+ def after_noderunlistremove
91
+ statusnet "#{organization}#{current_user} removed run_list items from #{object_name}: #{object_secondary_name}"
92
+ end
93
+
94
+ def after_noderunlistset
95
+ statusnet "#{organization}#{current_user} set the run_list for #{object_name} to #{object_secondary_name}"
96
+ end
97
+
98
+ private
99
+
100
+ def statusnet(message)
101
+ safe_require 'curb'
102
+
103
+ begin
104
+ c = Curl::Easy.new(config.url)
105
+ c.http_auth_types = :basic
106
+ c.username = config.username
107
+ c.password = config.password
108
+ c.post_body = message
109
+ c.perform
110
+ rescue Exception => e
111
+ ui.error 'Something went wrong sending to StatusNet.'
112
+ ui.error e.to_s
113
+ end
114
+ end
115
+
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,277 @@
1
+ require 'app_conf'
2
+ require 'diffy'
3
+ require 'json'
4
+
5
+ require 'chef/cookbook_loader'
6
+ require 'chef/knife/core/object_loader'
7
+ require 'knife-spork/plugins'
8
+
9
+ begin
10
+ require 'berkshelf'
11
+ rescue LoadError
12
+ end
13
+
14
+ module KnifeSpork
15
+ module Runner
16
+ module ClassMethods; end
17
+
18
+ module InstanceMethods
19
+ def spork_config
20
+ return @spork_config unless @spork_config.nil?
21
+
22
+ @spork_config = AppConf.new
23
+ load_paths = [ File.expand_path("#{cookbook_path.gsub('cookbooks','')}/config/spork-config.yml"), File.expand_path('config/spork-config.yml'), '/etc/spork-config.yml', File.expand_path('~/.chef/spork-config.yml') ]
24
+ load_paths.each do |load_path|
25
+ if File.exists?(load_path)
26
+ @spork_config.load(load_path)
27
+ end
28
+ end
29
+
30
+ @spork_config
31
+ end
32
+
33
+ def run_plugins(hook)
34
+ cookbooks = [ @cookbooks || @cookbook ].flatten.compact.collect{|cookbook| cookbook.is_a?(::Chef::CookbookVersion) ? cookbook : load_cookbook(cookbook)}.sort{|a,b| a.name.to_s <=> b.name.to_s}
35
+
36
+ # Affects promote only:
37
+ # Set loaded cookbook version if the -v or --version parameter was specified
38
+ # Otherwise the version on disk, often more recent will be used.
39
+ # We know cookbooks will only contain one cookbook in the case of promote.
40
+ cookbooks.map{|c|c.version = config[:version]} if config[:version]
41
+
42
+ environments = [ @environments || @environment ].flatten.compact.collect{|environment| environment.is_a?(::Chef::Environment) ? environment : load_environment_from_file(environment)}.sort{|a,b| a.name.to_s <=> b.name.to_s}
43
+ environment_diffs = @environment_diffs
44
+
45
+ KnifeSpork::Plugins.run(
46
+ :config => spork_config,
47
+ :hook => hook.to_sym,
48
+ :cookbooks => cookbooks,
49
+ :environments => environments,
50
+ :environment_diffs => environment_diffs,
51
+ :environment_path => environment_path,
52
+ :cookbook_path => cookbook_path,
53
+ :object_name => @object_name,
54
+ :object_secondary_name => @object_secondary_name,
55
+ :object_difference => @object_difference,
56
+ :ui => ui
57
+ )
58
+ end
59
+
60
+ def load_environments_and_cookbook
61
+ ensure_environment_provided!
62
+
63
+ if @name_args.size == 2
64
+ environments = load_specified_environment_group(@name_args[0])
65
+ [ environments, @name_args[1] ]
66
+ elsif @name_args.size == 1
67
+ [ [default_environments].flatten, @name_args[0] ]
68
+ end
69
+ end
70
+
71
+ def load_specified_environment_group(name)
72
+ if !spork_config.environment_groups.nil? && spork_config.environment_groups.keys.include?(name)
73
+ spork_config.environment_groups[name]
74
+ else
75
+ [name]
76
+ end
77
+ end
78
+
79
+ def ensure_environment_provided!
80
+ if default_environments.empty? && @name_args.size < 2
81
+ ui.error('You must specify an environment or environment group and a cookbook name')
82
+ exit(1)
83
+ end
84
+ end
85
+
86
+ def default_environments
87
+ [ spork_config.default_environment || spork_config.default_environments ].flatten.compact
88
+ end
89
+
90
+ def pretty_print_json(json)
91
+ JSON.pretty_generate(json)
92
+ end
93
+
94
+ def valid_version?(version)
95
+ version_keys = version.split('.')
96
+ return false unless version_keys.size == 3 && version_keys.any?{ |k| begin Float(k); rescue false; else true; end }
97
+ true
98
+ end
99
+
100
+ def validate_version!(version)
101
+ if version && !valid_version?(version)
102
+ ui.error("#{version} is not a valid version!")
103
+ exit(1)
104
+ end
105
+ end
106
+
107
+ def environment_loader
108
+ @environment_loader ||= Chef::Knife::Core::ObjectLoader.new(::Chef::Environment, ui)
109
+ end
110
+
111
+ def role_loader
112
+ @role_loader ||= Chef::Knife::Core::ObjectLoader.new(::Chef::Role, ui)
113
+ end
114
+
115
+ # It's not feasible to try and "guess" which cookbook path to use, so we will
116
+ # always just use the first one in the path.
117
+ def cookbook_path
118
+ ensure_cookbook_path!
119
+ [config[:cookbook_path] ||= ::Chef::Config.cookbook_path].flatten[0]
120
+ end
121
+
122
+ def environment_path
123
+ spork_config[:environment_path] || cookbook_path.gsub("/cookbooks","/environments")
124
+ end
125
+
126
+ def role_path
127
+ spork_config[:role_path] || cookbook_path.gsub("/cookbooks","/roles")
128
+ end
129
+
130
+ def all_cookbooks
131
+ ::Chef::CookbookLoader.new(::Chef::Config.cookbook_path)
132
+ end
133
+
134
+ def load_cookbook(name)
135
+ return name if name.is_a?(Chef::CookbookVersion)
136
+
137
+ cookbook = load_from_chef(name) || load_from_berkshelf(name) || load_from_librarian(name)
138
+
139
+ cookbook || raise(Chef::Exceptions::CookbookNotFound,
140
+ "Could not find cookbook '#{name}' in any of the sources!")
141
+ end
142
+
143
+ def load_from_chef(name)
144
+ all_cookbooks[name]
145
+ rescue Chef::Exceptions::CookbookNotFound,
146
+ Chef::Exceptions::CookbookNotFoundInRepo
147
+ nil
148
+ end
149
+
150
+ def load_from_berkshelf(name)
151
+ return unless defined?(::Berkshelf)
152
+ berksfile = ::Berkshelf::Berksfile.from_file(self.config[:berksfile])
153
+ lockfile = ::Berkshelf::Lockfile.new(berksfile)
154
+
155
+ raise Berkshelf::BerkshelfError, "LockFileNotFound" unless File.exists?(lockfile.filepath)
156
+
157
+ cookbook = Berkshelf.ui.mute {
158
+ self.config[:skip_dependencies] ||= false
159
+ berksfile.resolve(lockfile.find(name), {skip_dependencies: self.config[:skip_dependencies]})[:solution].first
160
+ }
161
+
162
+ # convert Berkshelf::CachedCookbook to Chef::CookbookVersion by loading the cookback at the
163
+ # full path returned by Berkshelf (since a cookbook of name `x` is not necessarily in a
164
+ # local directory named `x`.)
165
+ cookbook_path = cookbook.path
166
+ chef_search_dir = ::File.dirname(cookbook_path)
167
+ cookbook_dirname = ::File.basename(cookbook_path)
168
+ ::Chef::CookbookLoader.new(chef_search_dir).load_cookbook(cookbook_dirname)
169
+
170
+ end
171
+
172
+ # @todo #opensource
173
+ def load_from_librarian(name)
174
+ # Your code here :)
175
+ nil
176
+ end
177
+
178
+ def load_cookbooks(cookbook_names)
179
+ cookbook_names = [cookbook_names].flatten
180
+ cookbook_names.collect{ |cookbook_name| load_cookbook(cookbook_name) }
181
+ end
182
+
183
+
184
+ def load_role_from_file(role_name)
185
+ role_loader.object_from_file("#{role_path}/#{role_name}.json")
186
+ end
187
+
188
+ def load_role(role_name)
189
+ Chef::Role.load(role_name)
190
+ end
191
+
192
+ def load_node(node)
193
+ Chef::Node.load(node)
194
+ end
195
+
196
+ def load_databag(bag)
197
+ Chef::DataBag.load(bag)
198
+ end
199
+
200
+ def load_databag_item(bag, item_name)
201
+ Chef::DataBagItem.load(bag, item_name)
202
+ end
203
+
204
+ def load_environment(environment_name)
205
+ Chef::Environment.load(environment_name)
206
+ end
207
+
208
+ def load_environment_from_file(environment_name)
209
+ environment_loader.object_from_file("#{environment_path}/#{environment_name}.json")
210
+ end
211
+
212
+ def load_remote_environment(environment_name)
213
+ begin
214
+ Chef::Environment.load(environment_name)
215
+ rescue Net::HTTPServerException => e
216
+ ui.error "Could not load #{environment_name} from Chef Server. You must upload the environment manually the first time."
217
+ exit(1)
218
+ end
219
+ end
220
+
221
+ def environment_diff(local_environment, remote_environment)
222
+ local_environment_versions = local_environment.to_hash['cookbook_versions']
223
+ remote_environment_versions = remote_environment.to_hash['cookbook_versions']
224
+ hash_diff remote_environment_versions, local_environment_versions
225
+ end
226
+
227
+ def json_diff(a, b)
228
+ pre_json = JSON.parse(a.respond_to?(:to_json) ? a.to_json : a)
229
+ post_json = JSON.parse(b.respond_to?(:to_json) ? b.to_json : b)
230
+ Diffy::Diff.new(JSON.pretty_generate(pre_json), JSON.pretty_generate(post_json), :diff=>"-U 3").to_s.gsub(/[()]/, '\\\\\0')
231
+ end
232
+
233
+ def hash_diff(hash, other)
234
+ hash.keys.inject({}) do |memo, key|
235
+ unless hash[key] == other[key]
236
+ memo[key] = "#{hash[key]} changed to #{other[key]}"
237
+ end
238
+ memo
239
+ end
240
+ end
241
+
242
+ def constraints_diff (environment_diff)
243
+ Hash[Hash[environment_diff.map{|k,v| [k, v.split(" changed to ").map{|x|x.gsub("= ","")}]}].map{|k,v|[k,calc_diff(k,v)]}]
244
+ end
245
+
246
+ def calc_diff(cookbook, version)
247
+ components = version.map{|v|v.split(".")}
248
+
249
+ if components.length < 2
250
+ ui.warn "#{cookbook} has no remote version to diff against!"
251
+ return 0
252
+ end
253
+
254
+ if components[1][0].to_i != components[0][0].to_i
255
+ return (components[1][0].to_i - components[0][0].to_i)*100
256
+ elsif components[1][1].to_i != components[0][1].to_i
257
+ return (components[1][1].to_i - components[0][1].to_i)*10
258
+ else
259
+ return (components[1][2].to_i - components[0][2].to_i)
260
+ end
261
+ end
262
+
263
+ def ensure_cookbook_path!
264
+ if !config.has_key?(:cookbook_path)
265
+ ui.fatal "No default cookbook_path; Specify with -o or fix your knife.rb."
266
+ show_usage
267
+ exit(1)
268
+ end
269
+ end
270
+ end
271
+
272
+ def self.included(receiver)
273
+ receiver.extend(ClassMethods)
274
+ receiver.send(:include, InstanceMethods)
275
+ end
276
+ end
277
+ end
@@ -0,0 +1,43 @@
1
+ Campfire
2
+ ========
3
+ Automatically posts informational messages to Campfire
4
+
5
+ Gem Requirements
6
+ ----------------
7
+ This plugin requires the following gems:
8
+
9
+ ```ruby
10
+ gem 'campy'
11
+ ```
12
+
13
+ Hooks
14
+ -----
15
+ - `after_promote`
16
+ - `after_upload`
17
+
18
+ Configuration
19
+ -------------
20
+ ```yaml
21
+ plugins:
22
+ campfire:
23
+ account: my_company
24
+ token: ABC123
25
+ rooms:
26
+ - General
27
+ - Web Operations
28
+ ```
29
+
30
+ #### account
31
+ This is your campfire account name. It is the subdomain part of your account.
32
+
33
+ - Type: `String`
34
+
35
+ #### token
36
+ This is the secure token you get from the Campfire configuration.
37
+
38
+ - Type: `String`
39
+
40
+ #### Rooms
41
+ This is an array of room names to post messages to.
42
+
43
+ - Type: `String`
@@ -0,0 +1,30 @@
1
+ Eventinator
2
+ ===========
3
+
4
+ Gem Requirements
5
+ ----------------
6
+ This plugin has no gem requirements.
7
+
8
+ Hooks
9
+ -----
10
+ - `after_upload`
11
+
12
+ Configuration
13
+ -------------
14
+ ```yaml
15
+ plugins:
16
+ eventinator:
17
+ url: www.example.com
18
+ read_timeout: 5
19
+ ```
20
+
21
+ #### url
22
+ The server to post to.
23
+
24
+ - Type: `String`
25
+
26
+ #### read_timeout
27
+ The timeout, in seconds, for the request to return.
28
+
29
+ - Type: `Integer`
30
+ - Default: `5`