knife-playground 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (6) hide show
  1. data/LICENSE.txt +20 -0
  2. data/README.md +27 -0
  3. data/Rakefile +33 -0
  4. data/VERSION +1 -0
  5. data/lib/chef/knife/pg.rb +271 -0
  6. metadata +97 -0
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Sergio Rubio <rubiojr@frameos.org>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # knife-playground
2
+
3
+ Playground for various Opscode Chef Knife plugins
4
+
5
+ # Installing
6
+
7
+ gem install knife-playground
8
+
9
+ # Commands Available
10
+
11
+ *knife pg clientnode delete CLIENT*
12
+
13
+ Delete client + node from Opscode Chef Server
14
+
15
+ knife pg clientnode delete bluepill
16
+
17
+ *knife pg gitcook upload Git-URL1 GitURL2 ...*
18
+
19
+ Upload a cookbook to a Chef Server downloading it first from a Git repository
20
+
21
+ knife pg git cookbook upload git://github.com/rubiojr/yum-stress-cookbook.git
22
+
23
+ # Copyright
24
+
25
+ Copyright (c) 2011 Sergio Rubio. See LICENSE.txt for
26
+ further details.
27
+
data/Rakefile ADDED
@@ -0,0 +1,33 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |gem|
6
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
7
+ gem.version = File.exist?('VERSION') ? File.read('VERSION') : ""
8
+ gem.name = "knife-playground"
9
+ gem.homepage = "http://github.com/rubiojr/knife-playground"
10
+ gem.license = "MIT"
11
+ gem.summary = %Q{Opscode Knife plugin with useful tools}
12
+ gem.description = %Q{Misc tools for Opscode Chef Knife}
13
+ gem.email = "rubiojr@frameos.org"
14
+ gem.authors = ["Sergio Rubio"]
15
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
16
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
17
+ gem.add_runtime_dependency 'chef', '>= 0.10'
18
+ gem.add_runtime_dependency 'git'
19
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
20
+ end
21
+ Jeweler::RubygemsDotOrgTasks.new
22
+
23
+ task :default => :build
24
+
25
+ require 'rdoc/task'
26
+ Rake::RDocTask.new do |rdoc|
27
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
28
+
29
+ rdoc.rdoc_dir = 'rdoc'
30
+ rdoc.title = "Knife Playground #{version}"
31
+ rdoc.rdoc_files.include('README*')
32
+ rdoc.rdoc_files.include('lib/**/*.rb')
33
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1
@@ -0,0 +1,271 @@
1
+ require 'chef'
2
+
3
+ module KnifePlayground
4
+ class PgClientnodeDelete < Chef::Knife
5
+ banner "knife pg clientnode delete CLIENT"
6
+
7
+ deps do
8
+ require 'chef/node'
9
+ require 'chef/api_client'
10
+ require 'chef/json_compat'
11
+ end
12
+
13
+ def run
14
+ @client_name = @name_args[0]
15
+ if @client_name.nil?
16
+ show_usage
17
+ ui.fatal("You must specify a client name")
18
+ exit 1
19
+ end
20
+ ui.info "Deleting CLIENT #{@client_name}..."
21
+ delete_object(Chef::ApiClient, @client_name)
22
+
23
+ @node_name = @name_args[0]
24
+ if @node_name.nil?
25
+ show_usage
26
+ ui.fatal("You must specify a node name")
27
+ exit 1
28
+ end
29
+ ui.info "Deleting NODE #{@node_name}..."
30
+ delete_object(Chef::Node, @node_name)
31
+
32
+ end
33
+
34
+ class PgGitCookbookUpload < Chef::Knife
35
+
36
+ CHECKSUM = "checksum"
37
+ MATCH_CHECKSUM = /[0-9a-f]{32,}/
38
+
39
+ deps do
40
+ require 'chef/exceptions'
41
+ require 'chef/cookbook_loader'
42
+ require 'chef/cookbook_uploader'
43
+ end
44
+
45
+ banner "knife pg git cookbook upload [COOKBOOKS...] (options)"
46
+
47
+ option :cookbook_path,
48
+ :short => "-o PATH:PATH",
49
+ :long => "--cookbook-path PATH:PATH",
50
+ :description => "A colon-separated path to look for cookbooks in",
51
+ :proc => lambda { |o| o.split(":") }
52
+
53
+ option :freeze,
54
+ :long => '--freeze',
55
+ :description => 'Freeze this version of the cookbook so that it cannot be overwritten',
56
+ :boolean => true
57
+
58
+ option :all,
59
+ :short => "-a",
60
+ :long => "--all",
61
+ :description => "Upload all cookbooks, rather than just a single cookbook"
62
+
63
+ option :force,
64
+ :long => '--force',
65
+ :boolean => true,
66
+ :description => "Update cookbook versions even if they have been frozen"
67
+
68
+ option :environment,
69
+ :short => '-E',
70
+ :long => '--environment ENVIRONMENT',
71
+ :description => "Set ENVIRONMENT's version dependency match the version you're uploading.",
72
+ :default => nil
73
+
74
+ option :depends,
75
+ :short => "-d",
76
+ :long => "--include-dependencies",
77
+ :description => "Also upload cookbook dependencies"
78
+
79
+ def run
80
+ git_urls = @name_args.dup
81
+ git_urls.each do |n|
82
+ if n =~ /^git:\/\//
83
+ @name_args.delete n
84
+ git_repo = n
85
+ git_clone(git_repo)
86
+ end
87
+ end
88
+
89
+ config[:cookbook_path] ||= Chef::Config[:cookbook_path]
90
+
91
+ assert_environment_valid!
92
+ warn_about_cookbook_shadowing
93
+ version_constraints_to_update = {}
94
+ # Get a list of cookbooks and their versions from the server
95
+ # for checking existence of dependending cookbooks.
96
+ @server_side_cookbooks = Chef::CookbookVersion.list
97
+
98
+ if config[:all]
99
+ justify_width = cookbook_repo.cookbook_names.map {|name| name.size}.max.to_i + 2
100
+ cookbook_repo.each do |cookbook_name, cookbook|
101
+ cookbook.freeze_version if config[:freeze]
102
+ upload(cookbook, justify_width)
103
+ version_constraints_to_update[cookbook_name] = cookbook.version
104
+ end
105
+ else
106
+ if @name_args.empty?
107
+ show_usage
108
+ ui.error("You must specify the --all flag or at least one cookbook name")
109
+ exit 1
110
+ end
111
+ justify_width = @name_args.map {|name| name.size }.max.to_i + 2
112
+ @name_args.each do |cookbook_name|
113
+ begin
114
+ cookbook = cookbook_repo[cookbook_name]
115
+ if config[:depends]
116
+ cookbook.metadata.dependencies.each do |dep, versions|
117
+ @name_args.push dep
118
+ end
119
+ end
120
+ cookbook.freeze_version if config[:freeze]
121
+ upload(cookbook, justify_width)
122
+ version_constraints_to_update[cookbook_name] = cookbook.version
123
+ rescue Exceptions::CookbookNotFoundInRepo => e
124
+ ui.error("Could not find cookbook #{cookbook_name} in your cookbook path, skipping it")
125
+ Log.debug(e)
126
+ end
127
+ end
128
+ end
129
+
130
+ ui.info "upload complete"
131
+ update_version_constraints(version_constraints_to_update) if config[:environment]
132
+ end
133
+
134
+ def cookbook_repo
135
+ @cookbook_loader ||= begin
136
+ Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest, config[:cookbook_path]) }
137
+ Chef::CookbookLoader.new(config[:cookbook_path])
138
+ end
139
+ end
140
+
141
+ def update_version_constraints(new_version_constraints)
142
+ new_version_constraints.each do |cookbook_name, version|
143
+ environment.cookbook_versions[cookbook_name] = "= #{version}"
144
+ end
145
+ environment.save
146
+ end
147
+
148
+
149
+ def environment
150
+ @environment ||= config[:environment] ? Environment.load(config[:environment]) : nil
151
+ end
152
+
153
+ def warn_about_cookbook_shadowing
154
+ unless cookbook_repo.merged_cookbooks.empty?
155
+ ui.warn "* " * 40
156
+ ui.warn(<<-WARNING)
157
+ The cookbooks: #{cookbook_repo.merged_cookbooks.join(', ')} exist in multiple places in your cookbook_path.
158
+ A composite version of these cookbooks has been compiled for uploading.
159
+
160
+ #{ui.color('IMPORTANT:', :red, :bold)} In a future version of Chef, this behavior will be removed and you will no longer
161
+ be able to have the same version of a cookbook in multiple places in your cookbook_path.
162
+ WARNING
163
+ ui.warn "The affected cookbooks are located:"
164
+ ui.output ui.format_for_display(cookbook_repo.merged_cookbook_paths)
165
+ ui.warn "* " * 40
166
+ end
167
+ end
168
+
169
+ private
170
+
171
+ def git_clone(url, opts = {})
172
+ repo = File.basename(URI.parse(url).path.split('/').last, '.git')
173
+ @name_args << repo
174
+ path = File.join(Chef::Config[:cookbook_path], repo)
175
+ # Error if previous checkout exist
176
+ if File.directory?(path + '/.git')
177
+ ui.info "Cookbook #{repo} already downloaded."
178
+ else
179
+ ui.info "Downloading cookbook from #{url}"
180
+ Git.clone url, path, opts
181
+ end
182
+ end
183
+
184
+ def assert_environment_valid!
185
+ environment
186
+ rescue Net::HTTPServerException => e
187
+ if e.response.code.to_s == "404"
188
+ ui.error "The environment #{config[:environment]} does not exist on the server, aborting."
189
+ Log.debug(e)
190
+ exit 1
191
+ else
192
+ raise
193
+ end
194
+ end
195
+
196
+ def upload(cookbook, justify_width)
197
+ ui.info("Uploading #{cookbook.name.to_s.ljust(justify_width + 10)} [#{cookbook.version}]")
198
+
199
+ check_for_broken_links(cookbook)
200
+ check_dependencies(cookbook)
201
+ Chef::CookbookUploader.new(cookbook, config[:cookbook_path], :force => config[:force]).upload_cookbook
202
+ rescue Net::HTTPServerException => e
203
+ case e.response.code
204
+ when "409"
205
+ ui.error "Version #{cookbook.version} of cookbook #{cookbook.name} is frozen. Use --force to override."
206
+ Log.debug(e)
207
+ else
208
+ raise
209
+ end
210
+ end
211
+
212
+ # if only you people wouldn't put broken symlinks in your cookbooks in
213
+ # the first place. ;)
214
+ def check_for_broken_links(cookbook)
215
+ # MUST!! dup the cookbook version object--it memoizes its
216
+ # manifest object, but the manifest becomes invalid when you
217
+ # regenerate the metadata
218
+ broken_files = cookbook.dup.manifest_records_by_path.select do |path, info|
219
+ info[CHECKSUM].nil? || info[CHECKSUM] !~ MATCH_CHECKSUM
220
+ end
221
+ unless broken_files.empty?
222
+ broken_filenames = Array(broken_files).map {|path, info| path}
223
+ ui.error "The cookbook #{cookbook.name} has one or more broken files"
224
+ ui.info "This is probably caused by broken symlinks in the cookbook directory"
225
+ ui.info "The broken file(s) are: #{broken_filenames.join(' ')}"
226
+ exit 1
227
+ end
228
+ end
229
+
230
+ def check_dependencies(cookbook)
231
+ # for each dependency, check if the version is on the server, or
232
+ # the version is in the cookbooks being uploaded. If not, exit and warn the user.
233
+ cookbook.metadata.dependencies.each do |cookbook_name, version|
234
+ unless check_server_side_cookbooks(cookbook_name, version) || check_uploading_cookbooks(cookbook_name, version)
235
+ # warn the user and exit
236
+ ui.error "Cookbook #{cookbook.name} depends on cookbook #{cookbook_name} version #{version},"
237
+ ui.error "which is not currently being uploaded and cannot be found on the server."
238
+ exit 1
239
+ end
240
+ end
241
+ end
242
+
243
+ def check_server_side_cookbooks(cookbook_name, version)
244
+ if @server_side_cookbooks[cookbook_name].nil?
245
+ false
246
+ else
247
+ @server_side_cookbooks[cookbook_name]["versions"].each do |versions_hash|
248
+ return true if Chef::VersionConstraint.new(version).include?(versions_hash["version"])
249
+ end
250
+ false
251
+ end
252
+ end
253
+
254
+ def check_uploading_cookbooks(cookbook_name, version)
255
+ if config[:all]
256
+ # check from all local cookbooks in the path
257
+ unless cookbook_repo[cookbook_name].nil?
258
+ return Chef::VersionConstraint.new(version).include?(cookbook_repo[cookbook_name].version)
259
+ end
260
+ else
261
+ # check from only those in the command argument
262
+ if @name_args.include?(cookbook_name)
263
+ return Chef::VersionConstraint.new(version).include?(cookbook_repo[cookbook_name].version)
264
+ end
265
+ end
266
+ false
267
+ end
268
+
269
+ end
270
+ end
271
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: knife-playground
3
+ version: !ruby/object:Gem::Version
4
+ hash: 9
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ version: "0.1"
10
+ platform: ruby
11
+ authors:
12
+ - Sergio Rubio
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-08-10 00:00:00 Z
18
+ dependencies:
19
+ - !ruby/object:Gem::Dependency
20
+ name: chef
21
+ prerelease: false
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ hash: 31
28
+ segments:
29
+ - 0
30
+ - 10
31
+ version: "0.10"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: git
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ description: Misc tools for Opscode Chef Knife
49
+ email: rubiojr@frameos.org
50
+ executables: []
51
+
52
+ extensions: []
53
+
54
+ extra_rdoc_files:
55
+ - LICENSE.txt
56
+ - README.md
57
+ files:
58
+ - LICENSE.txt
59
+ - README.md
60
+ - Rakefile
61
+ - VERSION
62
+ - lib/chef/knife/pg.rb
63
+ homepage: http://github.com/rubiojr/knife-playground
64
+ licenses:
65
+ - MIT
66
+ post_install_message:
67
+ rdoc_options: []
68
+
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ hash: 3
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ hash: 3
86
+ segments:
87
+ - 0
88
+ version: "0"
89
+ requirements: []
90
+
91
+ rubyforge_project:
92
+ rubygems_version: 1.8.5
93
+ signing_key:
94
+ specification_version: 3
95
+ summary: Opscode Knife plugin with useful tools
96
+ test_files: []
97
+