knife-playground 0.1

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