knife-cookbook-sync 0.1.0

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.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YzM3ZTUyYjk4MjBjZDRiY2ZlMzYzNGZjOTU1YzYwNTk2MmI0YTI0MA==
5
+ data.tar.gz: !binary |-
6
+ YjkwNzQ4ODFhNzc5MzNmN2EyYTEwYWI0ZjM3NTVkMGEwMzdkM2Q4MQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ NmJjNDAwOTJkMzY2ZTQxNzgzMmUxNTM5ZTYyMGZmYWUzNjAzNjQzOTIxMTAy
10
+ ZWE3YWEyMDE4OTdjZmRjYTM0N2NiN2YzMmU1MDdhYTNhNWU1YmRlMzhiMGRi
11
+ MTU5MTIzOGM0ZjhkMWJhNDUzODA2NDRlZWZhZTdkMTZkZTU0Zjg=
12
+ data.tar.gz: !binary |-
13
+ YTVmNTA3MWI4MDRiOTI0NTEwNjJjOTIyNmY3ZmMwZTExYTlkZDNkNGQxZjY4
14
+ NjVmMjZkOTRkNTdhNmE1NzRhMDVkNjZiMTY4NTkyZjBiNjgyOTRmOGEwNTEx
15
+ ZjRjMTUxZTUzYWE5OWM3ZWJmZTU4NzE3NTZhMDhmY2RhYzM5M2M=
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,36 @@
1
+ 0.1.0 (04/13/2013)
2
+ ==================
3
+
4
+ * Gem has been renamed -- is now `knife-cookbook-sync`.
5
+ * Chef 11 now works, Chef 10 still works.
6
+ * Clarified exit statuses for various behavior.
7
+
8
+ 0.0.5
9
+ =====
10
+
11
+ * Deal with resolver bad behavior in a more appropriate way (abort).
12
+
13
+ 0.0.4
14
+ =====
15
+
16
+ * Deal with cookbook sync's exit statuses a little more clearly in the
17
+ chef-workflow task.
18
+
19
+ 0.0.3
20
+ =====
21
+
22
+ * Add chef-workflow support
23
+
24
+ 0.0.2
25
+ =====
26
+ * honor knife.rb configuration of cookbook path
27
+ * add a quiet option (-q). dry run is unaffected.
28
+ * check if the cookbook exists before loading it. vastly improves upload
29
+ performance for new cookbooks.
30
+ * exploit recent threaded upload improvements in Chef API: 0.1s slower than
31
+ knife cookbook upload for all new content (worst case for sync).
32
+
33
+ 0.0.1
34
+ =====
35
+
36
+ * First public release.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in knife_cookbook_sync.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Erik Hollensbe
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,102 @@
1
+ # knife cookbook sync
2
+
3
+ *Note*: this gem has been renamed to be more conformant with knife plugin
4
+ naming on rubygems.org. The original gem was named
5
+ [knife_cookbook_sync](https://rubygems.org/gems/knife_cookbook_sync).
6
+
7
+ Sync your cookbooks faster than `knife cookbook upload` or alternatives.
8
+
9
+ `knife cookbook sync` is primarily a development tool, but can be used for
10
+ production work with careful coordination with an external cookbook resolver.
11
+
12
+ Here's the meat. `knife cookbook sync` vs `knife cookbook upload` with a
13
+ pre-uploaded corpus of 39 cookbooks, using the standard unix `time` utility to
14
+ benchmark on MRI 1.9.3-p327 and chef 10.16.2:
15
+
16
+ * `knife cookbook sync -a`: 1.31s user 0.15s system 72% cpu **2.020 total**
17
+ * `knife cookbook upload -a`: 1.34s user 0.15s system 15% cpu **9.684 total**
18
+
19
+ Instead of resolving and uploading everything (or even what you ask to upload),
20
+ it uses the cryptographic sums chef already generates to determine what needs
21
+ to be uploaded, and only uploads what's different.
22
+
23
+ This means it **does not check versions and dependencies**. It cheats, so you
24
+ should be sure you have your ducks in a row before uploading by using a
25
+ cookbook resolver. This only matters for determining what to upload -- cookbooks
26
+ uploaded with `knife cookbook sync` are no different otherwise (and in fact use
27
+ chef's own cookbook uploading tooling to do it).
28
+
29
+ Unsurprisingly, the more that has changed, the more the performance will
30
+ decrease slowly towards `knife cookbook upload` performance. The gains are
31
+ really only seen when you need to sync whole repositories where most or all of
32
+ the product that's on-disk has already been uploaded. It's particularly nice
33
+ for fast test cycles where you just don't want to care just yet what cookbooks
34
+ have changed. However, you can use it for all uploading without any real issue.
35
+
36
+ `knife cookbook sync` has no dependencies other than chef, and is compatible
37
+ with both chef 10.x and 11.x.
38
+
39
+ ## Installation
40
+
41
+ Add this line to your application's Gemfile:
42
+
43
+ gem 'knife-cookbook-sync'
44
+
45
+ And then execute:
46
+
47
+ $ bundle
48
+
49
+ Or install it yourself as:
50
+
51
+ $ gem install knife-cookbook-sync
52
+
53
+ ## Usage
54
+
55
+ `knife cookbook sync` takes a list of cookbooks or `-a` to upload everything.
56
+ It uses your `cookbook_path` `knife.rb` settings to determine what's available
57
+ to upload which is overridable by `-o`.
58
+
59
+ If you pass `-d`, it'll perform a "dry run" and just show you what it would
60
+ upload.
61
+
62
+ See "Exit Statuses" below for information on how `knife cookbook sync` can let
63
+ you know what it did (you know, for scripting yo).
64
+
65
+ For more information, use `knife cookbook sync --help`.
66
+
67
+ ## Exit Statuses
68
+
69
+ The following exit codes are used in various situations:
70
+
71
+ * 0: nothing went wrong, but we did not see anything different. Works in dry run and sync mode.
72
+ * 1: Something is not right -- usually this means you have a broken
73
+ `cookbook_path` or did not supply `-o`. Whereever Chef is sending its UI
74
+ output will have the information you seek (usually Standard Error).
75
+ * 5: nothing went wrong, but there are differences between what's in your
76
+ `cookbook_path` and the chef server. Works in dry run and sync mode.
77
+
78
+ ## Chef-Workflow support
79
+
80
+ We support [chef-workflow](https://github.com/chef-workflow/chef-workflow) by
81
+ way of a task you can use.
82
+
83
+ Add the 'knife-cookbook-sync' gem to your `Gemfile` (version `0.1.0` or later),
84
+ and this to your `Rakefile`:
85
+
86
+ ```ruby
87
+ chef_workflow_task 'chef/cookbooks/sync'
88
+ ```
89
+
90
+ And you'll have a `chef:cookbooks:sync` rake target you can use.
91
+
92
+ ## Contributing
93
+
94
+ 1. Fork it
95
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
96
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
97
+ 4. Push to the branch (`git push origin my-new-feature`)
98
+ 5. Create new Pull Request
99
+
100
+ ## Authors
101
+
102
+ * Erik Hollensbe <erik+github@hollensbe.org>
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'knife-cookbook-sync/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "knife-cookbook-sync"
8
+ gem.version = KnifeCookbookSync::VERSION
9
+ gem.authors = ["Erik Hollensbe"]
10
+ gem.email = ["erik+github@hollensbe.org"]
11
+ gem.description = %q{Sync only what's changed -- faster than cookbook upload}
12
+ gem.summary = %q{Sync only what's changed -- faster than cookbook upload}
13
+ gem.homepage = "https://github.com/erikh/knife-cookbook-sync"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency 'chef'
21
+ end
@@ -0,0 +1,15 @@
1
+ require 'knife/dsl'
2
+ require 'chef-workflow/tasks/bootstrap/knife'
3
+
4
+ namespace :chef do
5
+ namespace :cookbooks do
6
+ desc "Upload your cookbooks to the chef server"
7
+ task :sync => [ "bootstrap:knife" ] do
8
+ resolve_task = Rake::Task["chef:cookbooks:resolve"] rescue nil
9
+ resolve_task.invoke if resolve_task
10
+
11
+ result = knife %W[cookbook sync -a]
12
+ fail unless [0, 5].include?(result)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module Knife
4
+ class CookbookSync < Chef::Knife
5
+
6
+ deps do
7
+ require 'chef/knife/cookbook_metadata'
8
+ require 'chef/rest'
9
+
10
+ begin
11
+ require 'chef/checksum_cache'
12
+ rescue LoadError
13
+ end
14
+
15
+ require 'chef/cookbook_loader'
16
+ require 'chef/cookbook_uploader'
17
+ require 'chef/cookbook_version'
18
+ require 'chef/log'
19
+ end
20
+
21
+ banner "knife cookbook sync [COOKBOOKS...]"
22
+
23
+ option :dry_run,
24
+ :short => '-d',
25
+ :long => '--dry-run',
26
+ :boolean => true,
27
+ :description => "Do a dry run -- do not sync anything, just show what would be synced"
28
+
29
+ option :all,
30
+ :short => '-a',
31
+ :long => '--all',
32
+ :boolean => true,
33
+ :description => "Sync all cookbooks"
34
+
35
+ option :cookbook_path,
36
+ :short => '-o [COOKBOOK PATH]',
37
+ :long => '--cookbook-path [COOKBOOK PATH]',
38
+ :description => "The path that cookbooks should be loaded from (path:path)",
39
+ :proc => proc { |x| x.split(":") }
40
+
41
+ option :quiet,
42
+ :short => '-q',
43
+ :long => '--quiet',
44
+ :description => "Make less noise",
45
+ :default => false
46
+
47
+ def make_noise(&block)
48
+ force_make_noise(&block) if block and !config[:quiet]
49
+ end
50
+
51
+ def force_make_noise(&block)
52
+ @print_mutex.synchronize(&block) if block
53
+ end
54
+
55
+ def distill_manifest(cookbook)
56
+ files = { }
57
+ cookbook.manifest.values.select { |x| x.kind_of?(Array) }.flatten.each { |f| files[f['path']] = f['checksum'] }
58
+ # don't check metadata.json since json output is indeterministic, metadata.rb should be all that's needed anw
59
+ files.delete('metadata.json')
60
+ return files
61
+ end
62
+
63
+ def sync_cookbooks(cookbooks, cl)
64
+ log_level = Chef::Log.level
65
+
66
+ # mutes the CookbookVersion noise when the cookbook doesn't exist on the server.
67
+ Chef::Log.level = :fatal
68
+
69
+ to_upload = Queue.new
70
+
71
+ cookbooks.map(&:to_s).map do |cookbook|
72
+ Thread.new do
73
+ upload = false
74
+
75
+ make_noise do
76
+ ui.msg "Checking cookbook '#{cookbook}' for sync necessity"
77
+ end
78
+
79
+ remote_cookbook = Chef::CookbookVersion.available_versions(cookbook) &&
80
+ (Chef::CookbookVersion.load(cookbook.to_s) rescue nil)
81
+ local_cookbook = cl[cookbook.to_s] rescue nil
82
+
83
+ unless local_cookbook
84
+ make_noise do
85
+ ui.fatal "Cookbook '#{cookbook}' does not exist locally."
86
+ end
87
+ exit 1
88
+ end
89
+
90
+ if local_cookbook and !remote_cookbook
91
+ upload = true
92
+ else
93
+ remote_files = distill_manifest(remote_cookbook) rescue { }
94
+ local_files = distill_manifest(local_cookbook) rescue { }
95
+
96
+ if local_files.keys.length != remote_files.keys.length
97
+ upload = true
98
+ else
99
+ (local_files.keys + remote_files.keys).uniq.each do |filename|
100
+ if local_files[filename] != remote_files[filename]
101
+ upload = true
102
+ break
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ if upload
109
+ make_noise do
110
+ ui.msg "sync necessary; uploading '#{cookbook}'"
111
+ end
112
+
113
+ to_upload << cl[cookbook]
114
+ end
115
+ end
116
+ end.each(&:join)
117
+
118
+ cookbooks_to_upload = []
119
+ loop { cookbooks_to_upload << to_upload.shift(true) } rescue nil
120
+
121
+ # exit 0 if there's nothing to upload
122
+ if cookbooks_to_upload.empty?
123
+ exit 0
124
+ end
125
+
126
+ Chef::Log.level = log_level # restore log level now that we're done checking
127
+ Chef::CookbookUploader.new(cookbooks_to_upload, Chef::Config[:cookbook_path]).upload_cookbooks
128
+
129
+ # exit with an exit status of 5 if we've uploaded anything.
130
+ exit 5
131
+ end
132
+
133
+ def run
134
+ Thread.abort_on_exception = true
135
+
136
+ @print_mutex = Mutex.new
137
+
138
+ Chef::Config[:cookbook_path] = config[:cookbook_path] if config[:cookbook_path]
139
+
140
+ if !Chef::Config[:cookbook_path] or Chef::Config[:cookbook_path].empty?
141
+ ui.msg "No cookbook path in config file or provided on command-line. Aborting."
142
+ exit 1
143
+ end
144
+
145
+ Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest) }
146
+
147
+ cl = Chef::CookbookLoader.new(Chef::Config[:cookbook_path])
148
+ cl.load_cookbooks if cl.respond_to?(:load_cookbooks)
149
+
150
+ if config[:all]
151
+ if cl.respond_to?(:cookbook_names)
152
+ names = cl.cookbook_names
153
+ else
154
+ names = cl.cookbooks.map(&:name)
155
+ end
156
+
157
+ if !names or names.empty?
158
+ ui.msg "No cookbooks found to upload in cookbook path: #{Chef::Config[:cookbook_path].inspect} -- not continuing"
159
+ ui.msg "Set cookbook_path in knife.rb or pass the -o option."
160
+ exit 1
161
+ end
162
+
163
+ sync_cookbooks names, cl
164
+ else
165
+ sync_cookbooks name_args, cl
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,4 @@
1
+ require "knife-cookbook-sync/version"
2
+
3
+ module KnifeCookbookSync
4
+ end
@@ -0,0 +1,3 @@
1
+ module KnifeCookbookSync
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: knife-cookbook-sync
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Erik Hollensbe
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-04-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: chef
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
+ description: Sync only what's changed -- faster than cookbook upload
28
+ email:
29
+ - erik+github@hollensbe.org
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - .gitignore
35
+ - CHANGELOG.md
36
+ - Gemfile
37
+ - LICENSE.txt
38
+ - README.md
39
+ - Rakefile
40
+ - knife-cookbook-sync.gemspec
41
+ - lib/chef-workflow/tasks/chef/cookbooks/sync.rb
42
+ - lib/chef/knife/cookbook_sync.rb
43
+ - lib/knife-cookbook-sync.rb
44
+ - lib/knife-cookbook-sync/version.rb
45
+ homepage: https://github.com/erikh/knife-cookbook-sync
46
+ licenses: []
47
+ metadata: {}
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirements: []
63
+ rubyforge_project:
64
+ rubygems_version: 2.0.3
65
+ signing_key:
66
+ specification_version: 4
67
+ summary: Sync only what's changed -- faster than cookbook upload
68
+ test_files: []