knife_cookbook_sync 0.0.1 → 0.0.2

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.
data/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ 0.0.2
2
+ =====
3
+ * honor knife.rb configuration of cookbook path
4
+ * add a quiet option (-q). dry run is unaffected.
5
+ * check if the cookbook exists before loading it. vastly improves upload performance for new cookbooks.
6
+ * exploit recent threaded upload improvements in Chef API: 0.1s slower than knife cookbook upload for all new content (worst case for sync).
7
+
8
+ 0.0.1
9
+ =====
10
+
11
+ * First public release.
data/README.md CHANGED
@@ -7,7 +7,7 @@ production work with careful coordination with an external cookbook resolver.
7
7
 
8
8
  Here's the meat. `knife cookbook sync` vs `knife cookbook upload` with a
9
9
  pre-uploaded corpus of 39 cookbooks, using the standard unix `time` utility to
10
- benchmark on MRI 1.9.3-p327:
10
+ benchmark on MRI 1.9.3-p327 and chef 10.16.2:
11
11
 
12
12
  * `knife cookbook sync -a`: 1.31s user 0.15s system 72% cpu **2.020 total**
13
13
  * `knife cookbook upload -a`: 1.34s user 0.15s system 15% cpu **9.684 total**
@@ -18,7 +18,7 @@ to be uploaded, and only uploads what's different.
18
18
 
19
19
  This means it **does not check versions and dependencies**. It cheats, so you
20
20
  should be sure you have your ducks in a row before uploading by using a
21
- cookbook resolver. This only matters for resolution purposes -- cookbooks
21
+ cookbook resolver. This only matters for determining what to upload -- cookbooks
22
22
  uploaded with `knife cookbook sync` are no different otherwise (and in fact use
23
23
  chef's own cookbook uploading tooling to do it).
24
24
 
@@ -1,7 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- Thread.abort_on_exception = true
4
-
5
3
  module Knife
6
4
  class CookbookSync < Chef::Knife
7
5
 
@@ -12,6 +10,7 @@ module Knife
12
10
  require 'chef/cookbook_loader'
13
11
  require 'chef/cookbook_uploader'
14
12
  require 'chef/cookbook_version'
13
+ require 'chef/log'
15
14
  end
16
15
 
17
16
  banner "knife cookbook sync [COOKBOOKS...]"
@@ -31,36 +30,53 @@ module Knife
31
30
  option :cookbook_path,
32
31
  :short => '-o [COOKBOOK PATH]',
33
32
  :long => '--cookbook-path [COOKBOOK PATH]',
34
- :default => %w[cookbooks site-cookbooks],
35
33
  :description => "The path that cookbooks should be loaded from (path:path)",
36
34
  :proc => proc { |x| x.split(":") }
37
-
35
+
36
+ option :quiet,
37
+ :short => '-q',
38
+ :long => '--quiet',
39
+ :description => "Make less noise",
40
+ :default => false
41
+
42
+ def make_noise(&block)
43
+ force_make_noise(&block) if block and !config[:quiet]
44
+ end
45
+
46
+ def force_make_noise(&block)
47
+ @print_mutex.synchronize(&block) if block
48
+ end
38
49
 
39
50
  def distill_manifest(cookbook)
40
51
  files = { }
41
52
  cookbook.manifest.values.select { |x| x.kind_of?(Array) }.flatten.each { |f| files[f['path']] = f['checksum'] }
42
53
  # don't check metadata.json since json output is indeterministic, metadata.rb should be all that's needed anw
43
- files.delete('metadata.json')
54
+ files.delete('metadata.json')
44
55
  return files
45
56
  end
46
57
 
47
58
  def sync_cookbooks(cookbooks, cl)
48
- uploaded = false
59
+ log_level = Chef::Log.level
60
+
61
+ # mutes the CookbookVersion noise when the cookbook doesn't exist on the server.
62
+ Chef::Log.level = :fatal
49
63
 
50
- print_mutex = Mutex.new
64
+ to_upload = Queue.new
51
65
 
52
- cookbooks.each do |cookbook|
66
+ cookbooks.map(&:to_s).each do |cookbook|
53
67
  Thread.new do
54
- upload = false
55
- print_mutex.synchronize do
68
+ upload = false
69
+
70
+ make_noise do
56
71
  ui.msg "Checking cookbook '#{cookbook}' for sync necessity"
57
72
  end
58
73
 
59
- remote_cookbook = Chef::CookbookVersion.load(cookbook.to_s)
74
+ remote_cookbook = Chef::CookbookVersion.available_versions(cookbook) &&
75
+ (Chef::CookbookVersion.load(cookbook.to_s) rescue nil)
60
76
  local_cookbook = cl[cookbook.to_s] rescue nil
61
77
 
62
78
  unless local_cookbook
63
- print_mutex.synchronize do
79
+ make_noise do
64
80
  ui.fatal "Cookbook '#{cookbook}' does not exist locally."
65
81
  end
66
82
  exit 1
@@ -85,57 +101,33 @@ module Knife
85
101
  end
86
102
 
87
103
  if upload
88
- print_mutex.synchronize do
104
+ make_noise do
89
105
  ui.msg "sync necessary; uploading '#{cookbook}'"
90
106
  end
91
107
 
92
- retries_left = 5
93
-
94
- begin
95
- #
96
- # XXX
97
- #
98
- # For some godawful reason, if we use the local_cookbook referenced
99
- # above, the MD5 sums are off.
100
- #
101
- # So we reload the cookbooks here because it seems to work, with an
102
- # optional retry if the chef server is being pissy.
103
- #
104
- cl = Chef::CookbookLoader.new(Chef::Config[:cookbook_path])
105
-
106
- if config[:dry_run]
107
- print_mutex.synchronize do
108
- ui.warn "dry run: would sync '#{cookbook}'"
109
- end
110
- else
111
- Chef::CookbookUploader.new(cl[cookbook], Chef::Config[:cookbook_path]).upload_cookbooks
112
- end
113
-
114
- uploaded = true
115
- rescue Exception => e
116
- print_mutex.synchronize do
117
- ui.error "Failed to upload; retrying up to #{retries_left} times"
118
- end
119
-
120
- retries_left -= 1
121
- if retries_left > 0
122
- retry
123
- else
124
- raise e
125
- end
126
- end
108
+ to_upload << cl[cookbook]
127
109
  end
128
110
  end
129
111
  end
130
112
 
131
- Thread.list.reject { |x| x == Thread.current }.each(&:join)
113
+ Thread.list.reject { |x| x == Thread.current }.each(&:join) # wait for threads to settle
114
+
115
+ cookbooks_to_upload = []
116
+ loop { cookbooks_to_upload << to_upload.shift(true) } rescue nil
117
+
118
+ Chef::Log.level = log_level # restore log level now that we're done checking
119
+ Chef::CookbookUploader.new(cookbooks_to_upload, Chef::Config[:cookbook_path]).upload_cookbooks
132
120
 
133
121
  # exit with an exit status of 5 if we've uploaded anything.
134
- exit uploaded ? 5 : 0
122
+ exit cookbooks_to_upload.empty? ? 0 : 5
135
123
  end
136
124
 
137
125
  def run
138
- Chef::Config[:cookbook_path] = config[:cookbook_path]
126
+ Thread.abort_on_exception = true
127
+
128
+ @print_mutex = Mutex.new
129
+
130
+ Chef::Config[:cookbook_path] = config[:cookbook_path] if config[:cookbook_path]
139
131
 
140
132
  Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest) }
141
133
 
@@ -1,3 +1,3 @@
1
1
  module KnifeCookbookSync
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: knife_cookbook_sync
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-09 00:00:00.000000000 Z
12
+ date: 2013-01-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: chef
@@ -35,6 +35,7 @@ extensions: []
35
35
  extra_rdoc_files: []
36
36
  files:
37
37
  - .gitignore
38
+ - CHANGELOG.md
38
39
  - Gemfile
39
40
  - LICENSE.txt
40
41
  - README.md