knife_cookbook_sync 0.0.1 → 0.0.2

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