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.
- checksums.yaml +15 -0
- data/.gitignore +17 -0
- data/CHANGELOG.md +36 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +102 -0
- data/Rakefile +1 -0
- data/knife-cookbook-sync.gemspec +21 -0
- data/lib/chef-workflow/tasks/chef/cookbooks/sync.rb +15 -0
- data/lib/chef/knife/cookbook_sync.rb +169 -0
- data/lib/knife-cookbook-sync.rb +4 -0
- data/lib/knife-cookbook-sync/version.rb +3 -0
- metadata +68 -0
checksums.yaml
ADDED
@@ -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=
|
data/.gitignore
ADDED
data/CHANGELOG.md
ADDED
@@ -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
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -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>
|
data/Rakefile
ADDED
@@ -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
|
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: []
|