chef 0.9.0.a4 → 0.9.0.a6

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of chef might be problematic. Click here for more details.

@@ -24,6 +24,8 @@ require 'digest/md5'
24
24
  class Chef
25
25
  class Cache
26
26
  class Checksum < Chef::Cache
27
+ # singleton is inherited from Chef::Cache, but we like to be explicit.
28
+ include ::Singleton
27
29
 
28
30
  def self.checksum_for_file(*args)
29
31
  instance.checksum_for_file(*args)
@@ -62,6 +64,10 @@ class Chef
62
64
  checksum_file(file, Digest::MD5.new)
63
65
  end
64
66
 
67
+ def generate_md5_checksum(io)
68
+ checksum_io(io, Digest::MD5.new)
69
+ end
70
+
65
71
  private
66
72
 
67
73
  def file_unchanged?(cached, fstat)
@@ -69,9 +75,16 @@ class Chef
69
75
  end
70
76
 
71
77
  def checksum_file(file, digest)
72
- IO.foreach(file) {|line| digest.update(line) }
78
+ File.open(file) { |f| checksum_io(f, digest) }
79
+ end
80
+
81
+ def checksum_io(io, digest)
82
+ while chunk = io.read(1024 * 8)
83
+ digest.update(chunk)
84
+ end
73
85
  digest.hexdigest
74
86
  end
87
+
75
88
  end
76
89
  end
77
90
  end
@@ -206,5 +206,10 @@ class Chef
206
206
 
207
207
  # Arbitrary knife configuration data
208
208
  knife Hash.new
209
+
210
+ # Those lists of regular expressions define what chef considers a
211
+ # valid user and group name
212
+ user_valid_regex [ /^([-a-zA-Z0-9_.]+)$/, /^\d+$/ ]
213
+ group_valid_regex [ /^([-a-zA-Z0-9_.]+)$/, /^\d+$/ ]
209
214
  end
210
215
  end
@@ -104,6 +104,15 @@ class Chef
104
104
  if File.exists?(File.join(cookbook, "metadata.json"))
105
105
  cookbook_settings[cookbook_name][:metadata_filenames] << File.join(cookbook, "metadata.json")
106
106
  end
107
+
108
+ empty = cookbook_settings[cookbook_name].inject(true) do |all_empty, files|
109
+ all_empty && files.last.empty?
110
+ end
111
+
112
+ if empty
113
+ Chef::Log.warn "found a directory #{cookbook_name} in the cookbook path, but it contains no cookbook files. skipping."
114
+ cookbook_settings.delete(cookbook_name)
115
+ end
107
116
  end
108
117
  end
109
118
  remove_ignored_files_from(cookbook_settings)
@@ -144,6 +153,7 @@ class Chef
144
153
  def has_key?(cookbook_name)
145
154
  @cookbooks_by_name.has_key?(cookbook_name)
146
155
  end
156
+ alias :cookbook_exists? :has_key?
147
157
 
148
158
  def each
149
159
  @cookbooks_by_name.keys.sort { |a,b| a.to_s <=> b.to_s }.each do |cname|
@@ -151,9 +161,14 @@ class Chef
151
161
  end
152
162
  end
153
163
 
164
+ def cookbook_names
165
+ @cookbooks_by_name.keys.sort
166
+ end
167
+
154
168
  def values
155
169
  @cookbooks_by_name.values
156
170
  end
171
+ alias :cookbooks :values
157
172
 
158
173
  private
159
174
 
@@ -123,6 +123,49 @@ class Chef
123
123
  }
124
124
  @
125
125
  },
126
+ "all_latest_version_by_id" => {
127
+ "map" => %q@
128
+ function(doc) {
129
+ if (doc.chef_type == "cookbook_version") {
130
+ emit(doc.cookbook_name, doc.version);
131
+ }
132
+ }
133
+ @,
134
+ "reduce" => %q@
135
+ function(keys, values, rereduce) {
136
+ var result = null;
137
+
138
+ for (var idx in values) {
139
+ var value = values[idx];
140
+
141
+ if (idx == 0) {
142
+ result = value;
143
+ continue;
144
+ }
145
+
146
+ var valueParts = value[1].split('.').map(function(v) { return parseInt(v); });
147
+ var resultParts = result[1].split('.').map(function(v) { return parseInt(v); });
148
+
149
+ if (valueParts[0] != resultParts[0]) {
150
+ if (valueParts[0] > resultParts[0]) {
151
+ result = value;
152
+ }
153
+ }
154
+ else if (valueParts[1] != resultParts[1]) {
155
+ if (valueParts[1] > resultParts[1]) {
156
+ result = value;
157
+ }
158
+ }
159
+ else if (valueParts[2] != resultParts[2]) {
160
+ if (valueParts[2] > resultParts[2]) {
161
+ result = value;
162
+ }
163
+ }
164
+ }
165
+ return keys[idx][1];
166
+ }
167
+ @
168
+ },
126
169
  }
127
170
  }
128
171
 
@@ -595,10 +638,14 @@ class Chef
595
638
  ##
596
639
  # REST API
597
640
  ##
598
- def chef_server_rest
641
+ def self.chef_server_rest
599
642
  Chef::REST.new(Chef::Config[:chef_server_url])
600
643
  end
601
644
 
645
+ def chef_server_rest
646
+ self.class.chef_server_rest
647
+ end
648
+
602
649
  def save
603
650
  chef_server_rest.put_rest("cookbooks/#{name}/#{version}", self)
604
651
  self
@@ -612,7 +659,16 @@ class Chef
612
659
 
613
660
  def self.load(name, version="_latest")
614
661
  version = "_latest" if version == "latest"
615
- Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("cookbooks/#{name}/#{version}")
662
+ chef_server_rest.get_rest("cookbooks/#{name}/#{version}")
663
+ end
664
+
665
+ def self.list
666
+ chef_server_rest.get_rest('cookbooks')
667
+ end
668
+
669
+ # Get the newest version of all cookbooks
670
+ def self.latest_cookbooks
671
+ chef_server_rest.get_rest('cookbooks/_latest')
616
672
  end
617
673
 
618
674
  ##
@@ -620,7 +676,7 @@ class Chef
620
676
  ##
621
677
 
622
678
  def self.cdb_by_name(cookbook_name, couchdb=nil)
623
- cdb = couchdb || Chef::CouchDB.new
679
+ cdb = (couchdb || Chef::CouchDB.new)
624
680
  options = { :startkey => cookbook_name, :endkey => cookbook_name }
625
681
  rs = cdb.get_view("cookbooks", "all_with_version", options)
626
682
  rs["rows"].inject({}) { |memo, row| memo.has_key?(row["key"]) ? memo[row["key"]] << row["value"] : memo[row["key"]] = [ row["value"] ]; memo }
@@ -629,7 +685,24 @@ class Chef
629
685
  def self.create_design_document(couchdb=nil)
630
686
  (couchdb || Chef::CouchDB.new).create_design_document("cookbooks", DESIGN_DOCUMENT)
631
687
  end
632
-
688
+
689
+ def self.cdb_list_latest(inflate=false, couchdb=nil)
690
+ couchdb ||= Chef::CouchDB.new
691
+ if inflate
692
+ doc_ids = cdb_list_latest_ids
693
+ couchdb.bulk_get(doc_ids)
694
+ else
695
+ results = couchdb.get_view("cookbooks", "all_latest_version", :group=>true)["rows"]
696
+ results.inject({}) { |mapped, row| mapped[row["key"]] = row["value"]; mapped}
697
+ end
698
+ end
699
+
700
+ def self.cdb_list_latest_ids(inflate=false, couchdb=nil)
701
+ couchdb ||= Chef::CouchDB.new
702
+ results = couchdb.get_view("cookbooks", "all_latest_version_by_id", :group=>true)["rows"]
703
+ results.map { |name_and_id| name_and_id["value"]}
704
+ end
705
+
633
706
  def self.cdb_list(inflate=false, couchdb=nil)
634
707
  rs = (couchdb || Chef::CouchDB.new).list("cookbooks", inflate)
635
708
  lookup = (inflate ? "value" : "key")
@@ -220,8 +220,7 @@ class Chef
220
220
  def get_view(design, view, options={})
221
221
  view_string = view_uri(design, view)
222
222
  view_string << "?" if options.length != 0
223
- first = true;
224
- options.each { |k,v| view_string << "#{first ? '' : '&'}#{k}=#{URI.escape(v.to_json)}"; first = false }
223
+ view_string << options.map { |k,v| "#{k}=#{URI.escape(v.to_json)}"}.join('&')
225
224
  @rest.get_rest(view_string)
226
225
  end
227
226
 
@@ -121,10 +121,20 @@ class Chef
121
121
  break
122
122
  end
123
123
  end
124
- config[:config_file] ||= File.join(ENV['HOME'], '.chef', 'knife.rb')
124
+ # If we haven't set a config yet and $HOME is set, and the home
125
+ # knife.rb exists, use it:
126
+ if (!config[:config_file]) && ENV['HOME'] && File.exist?(File.join(ENV['HOME'], '.chef', 'knife.rb'))
127
+ config[:config_file] = File.join(ENV['HOME'], '.chef', 'knife.rb')
128
+ end
125
129
  end
126
130
 
127
- Chef::Config.from_file(config[:config_file])
131
+ # Don't try to load a knife.rb if it doesn't exist.
132
+ if config[:config_file]
133
+ Chef::Config.from_file(config[:config_file])
134
+ else
135
+ # ...but do log a message if no config was found.
136
+ Chef::Log.info("No knife configuration file found")
137
+ end
128
138
 
129
139
  Chef::Config[:log_level] = config[:log_level] if config[:log_level]
130
140
  Chef::Config[:log_location] = config[:log_location] if config[:log_location]
@@ -104,7 +104,9 @@ EOH
104
104
  end
105
105
 
106
106
  def ask_user_for_config_path
107
- config[:config_file] ||= ask_question("Where should I put the config file? ")
107
+ config[:config_file] ||= ask_question("Where should I put the config file? ", :default => '~/.chef/knife.rb')
108
+ # have to use expand path to expand the tilde character to the user's home
109
+ config[:config_file] = File.expand_path(config[:config_file])
108
110
  if File.exists?(config[:config_file])
109
111
  confirm("Overwrite #{config[:config_file]}")
110
112
  end
@@ -54,7 +54,7 @@ class Chef
54
54
  end
55
55
 
56
56
  def generate_metadata(cookbook)
57
- Chef::Log.info("Generating metadata for #{cookbook}")
57
+ Chef::Log.info("Generating Metadata")
58
58
  config[:cookbook_path].reverse.each do |path|
59
59
  file = File.expand_path(File.join(path, cookbook, 'metadata.rb'))
60
60
  generate_metadata_from_file(cookbook, file)
@@ -63,7 +63,7 @@ class Chef
63
63
 
64
64
  def generate_metadata_from_file(cookbook, file)
65
65
  if File.exists?(file)
66
- Chef::Log.info("Generating from #{file}")
66
+ Chef::Log.debug("Generating metadata for #{cookbook} from #{file}")
67
67
  md = Chef::Cookbook::Metadata.new
68
68
  md.name(cookbook)
69
69
  md.from_file(file)
@@ -72,7 +72,7 @@ class Chef
72
72
  f.write(JSON.pretty_generate(md))
73
73
  end
74
74
  generated = true
75
- Chef::Log.info("Generated #{json_file}")
75
+ Chef::Log.debug("Generated #{json_file}")
76
76
  else
77
77
  Chef::Log.debug("No #{file} found; skipping!")
78
78
  end
@@ -29,6 +29,7 @@ require 'chef/cookbook/file_system_file_vendor'
29
29
  class Chef
30
30
  class Knife
31
31
  class CookbookUpload < Knife
32
+ include Chef::Mixin::ShellOut
32
33
 
33
34
  banner "Sub-Command: cookbook upload [COOKBOOKS...] (options)"
34
35
 
@@ -59,23 +60,27 @@ class Chef
59
60
  upload_cookbook(cookbook)
60
61
  end
61
62
  else
62
- @name_args.each{|cookbook_name| upload_cookbook(cl[cookbook_name]) }
63
+ @name_args.each do |cookbook_name|
64
+ if cl.cookbook_exists?(cookbook_name)
65
+ upload_cookbook(cl[cookbook_name])
66
+ else
67
+ Chef::Log.error("Could not find cookbook #{cookbook_name} in your cookbook path, skipping it")
68
+ end
69
+ end
63
70
  end
64
71
  end
65
72
 
66
73
  def test_ruby(cookbook_dir)
67
74
  Chef::Log.info("Validating ruby files")
68
75
  Dir[File.join(cookbook_dir, '**', '*.rb')].each do |ruby_file|
69
- Chef::Log.info("Testing #{ruby_file} for syntax errors...")
70
- Chef::Mixin::Command.run_command(:command => "ruby -c #{ruby_file}")
76
+ test_ruby_file(cookbook_dir, ruby_file)
71
77
  end
72
78
  end
73
79
 
74
80
  def test_templates(cookbook_dir)
75
81
  Chef::Log.info("Validating templates")
76
82
  Dir[File.join(cookbook_dir, '**', '*.erb')].each do |erb_file|
77
- Chef::Log.info("Testing template #{erb_file} for syntax errors...")
78
- Chef::Mixin::Command.run_command(:command => "sh -c 'erubis -x #{erb_file} | ruby -c'")
83
+ test_template_file(cookbook_dir, erb_file)
79
84
  end
80
85
  end
81
86
 
@@ -187,6 +192,30 @@ class Chef
187
192
  end
188
193
  end
189
194
  end
195
+
196
+ private
197
+
198
+ def test_template_file(cookbook_dir, erb_file)
199
+ Chef::Log.debug("Testing template #{erb_file} for syntax errors...")
200
+ result = shell_out("sh -c 'erubis -x #{erb_file} | ruby -c'")
201
+ result.error!
202
+ rescue Chef::Exceptions::ShellCommandFailed
203
+ file_relative_path = erb_file[/^#{Regexp.escape(cookbook_dir)}#{File::Separator}(.*)/, 1]
204
+ Chef::Log.fatal("Erb template #{file_relative_path} has a syntax error:")
205
+ result.stderr.each_line { |l| Chef::Log.fatal(l.chomp) }
206
+ exit
207
+ end
208
+
209
+ def test_ruby_file(cookbook_dir, ruby_file)
210
+ Chef::Log.debug("Testing #{ruby_file} for syntax errors...")
211
+ result = shell_out("ruby -c #{ruby_file}")
212
+ result.error!
213
+ rescue Chef::Exceptions::ShellCommandFailed
214
+ file_relative_path = ruby_file[/^#{Regexp.escape(cookbook_dir)}#{File::Separator}(.*)/, 1]
215
+ Chef::Log.fatal("Cookbook file #{file_relative_path} has a syntax error:")
216
+ result.stderr.each_line { |l| Chef::Log.fatal(l.chomp) }
217
+ exit
218
+ end
190
219
 
191
220
  end
192
221
  end
@@ -51,14 +51,13 @@ class Chef
51
51
  @current_resource.owner(cstats.uid)
52
52
  @current_resource.group(cstats.gid)
53
53
  @current_resource.mode(octal_mode(cstats.mode))
54
- @current_resource.checksum(checksum(@current_resource.path))
55
54
  end
56
55
  @current_resource
57
56
  end
58
57
 
59
58
  # Compare the content of a file. Returns true if they are the same, false if they are not.
60
59
  def compare_content
61
- @current_resource.checksum == new_resource_content_checksum
60
+ checksum(@current_resource.path) == new_resource_content_checksum
62
61
  end
63
62
 
64
63
  # Set the content of the file, assuming it is not set correctly already.
@@ -29,6 +29,11 @@ class Chef
29
29
 
30
30
  include Chef::Mixin::FindPreferredFile
31
31
 
32
+ def load_current_resource
33
+ super
34
+ @current_resource.checksum(checksum(@current_resource.path)) if ::File.exist?(@current_resource.path)
35
+ end
36
+
32
37
  def action_create
33
38
  Chef::Log.debug("Checking #{@new_resource} for changes")
34
39
 
@@ -33,10 +33,15 @@ class Chef
33
33
 
34
34
  class Template < Chef::Provider::File
35
35
 
36
- #include Chef::Mixin::Checksum
36
+ include Chef::Mixin::Checksum
37
37
  include Chef::Mixin::Template
38
38
  #include Chef::Mixin::FindPreferredFile
39
39
 
40
+ def load_current_resource
41
+ super
42
+ @current_resource.checksum(checksum(@current_resource.path)) if ::File.exist?(@current_resource.path)
43
+ end
44
+
40
45
  def action_create
41
46
  render_with_context(template_location) do |rendered_template|
42
47
  rendered(rendered_template)
@@ -36,7 +36,7 @@ class Chef
36
36
 
37
37
  def remove_user
38
38
  command = "userdel"
39
- command << " -r" if @new_resource.supports[:manage_home]
39
+ command << " -r" if @new_resource.manage_home || @new_resource.supports[:manage_home]
40
40
  command << " #{@new_resource.username}"
41
41
  run_command(:command => command)
42
42
  end
@@ -89,7 +89,7 @@ class Chef
89
89
  end
90
90
  end
91
91
  if @current_resource.home != @new_resource.home && @new_resource.home
92
- if @new_resource.supports[:manage_home]
92
+ if @new_resource.manage_home || @new_resource.supports[:manage_home]
93
93
  Chef::Log.debug("Managing the home directory for #{@new_resource}")
94
94
  opts << " -d '#{@new_resource.home}' -m"
95
95
  else
@@ -97,7 +97,8 @@ class Chef
97
97
  opts << " -d '#{@new_resource.home}'"
98
98
  end
99
99
  end
100
- opts << " -o" if @new_resource.supports[:non_unique]
100
+ opts << " -r" if @new_resource.system
101
+ opts << " -o" if @new_resource.non_unique || @new_resource.supports[:non_unique]
101
102
  opts << " #{@new_resource.username}"
102
103
  opts
103
104
  end
@@ -43,7 +43,7 @@ class Chef
43
43
  set_or_return(
44
44
  :group,
45
45
  arg,
46
- :regex => [ /^([a-z]|[A-Z]|[0-9]|_|-)+$/, /^\d+$/ ]
46
+ :regex => Chef::Config[:group_valid_regex]
47
47
  )
48
48
  end
49
49
 
@@ -59,7 +59,7 @@ class Chef
59
59
  set_or_return(
60
60
  :owner,
61
61
  arg,
62
- :regex => [ /^([a-z]|[A-Z]|[0-9]|_|-)+$/, /^\d+$/ ]
62
+ :regex => Chef::Config[:user_valid_regex]
63
63
  )
64
64
  end
65
65
 
@@ -59,7 +59,7 @@ class Chef
59
59
  set_or_return(
60
60
  :group,
61
61
  arg,
62
- :regex => [ /^([a-z]|[A-Z]|[0-9]|_|-)+$/, /^\d+$/ ]
62
+ :regex => Chef::Config[:group_valid_regex]
63
63
  )
64
64
  end
65
65
 
@@ -75,7 +75,7 @@ class Chef
75
75
  set_or_return(
76
76
  :owner,
77
77
  arg,
78
- :regex => [ /^([a-z]|[A-Z]|[0-9]|_|-)+$/, /^\d+$/ ]
78
+ :regex => Chef::Config[:user_valid_regex]
79
79
  )
80
80
  end
81
81
 
@@ -61,7 +61,7 @@ class Chef
61
61
  set_or_return(
62
62
  :group,
63
63
  arg,
64
- :regex => [ /^([a-z]|[A-Z]|[0-9]|_|-)+$/, /^\d+$/ ]
64
+ :regex => Chef::Config[:group_valid_regex]
65
65
  )
66
66
  end
67
67
 
@@ -69,7 +69,7 @@ class Chef
69
69
  set_or_return(
70
70
  :owner,
71
71
  arg,
72
- :regex => [ /^([a-z]|[A-Z]|[0-9]|_|-)+$/, /^\d+$/ ]
72
+ :regex => Chef::Config[:user_valid_regex]
73
73
  )
74
74
  end
75
75
 
@@ -67,7 +67,7 @@ class Chef
67
67
  set_or_return(
68
68
  :files_group,
69
69
  arg,
70
- :regex => [ /^([a-z]|[A-Z]|[0-9]|_|-)+$/, /^\d+$/ ]
70
+ :regex => Chef::Config[:group_valid_regex]
71
71
  )
72
72
  end
73
73
 
@@ -83,7 +83,7 @@ class Chef
83
83
  set_or_return(
84
84
  :files_owner,
85
85
  arg,
86
- :regex => [ /^([a-z]|[A-Z]|[0-9]|_|-)+$/, /^\d+$/ ]
86
+ :regex => Chef::Config[:user_valid_regex]
87
87
  )
88
88
  end
89
89
 
@@ -32,6 +32,9 @@ class Chef
32
32
  @home = nil
33
33
  @shell = nil
34
34
  @password = nil
35
+ @system = false
36
+ @manage_home = false
37
+ @non_unique = false
35
38
  @action = :create
36
39
  @supports = {
37
40
  :manage_home => false,
@@ -97,6 +100,30 @@ class Chef
97
100
  :kind_of => [ String ]
98
101
  )
99
102
  end
103
+
104
+ def system(arg=nil)
105
+ set_or_return(
106
+ :system,
107
+ arg,
108
+ :kind_of => [ TrueClass, FalseClass ]
109
+ )
110
+ end
111
+
112
+ def manage_home(arg=nil)
113
+ set_or_return(
114
+ :manage_home,
115
+ arg,
116
+ :kind_of => [ TrueClass, FalseClass ]
117
+ )
118
+ end
119
+
120
+ def non_unique(arg=nil)
121
+ set_or_return(
122
+ :non_unique,
123
+ arg,
124
+ :kind_of => [ TrueClass, FalseClass ]
125
+ )
126
+ end
100
127
 
101
128
  end
102
129
  end
@@ -84,7 +84,13 @@ class Chef
84
84
  @create_time = Time.now.iso8601
85
85
  @checksums = Array.new
86
86
  end
87
-
87
+
88
+ def include?(checksum)
89
+ @checksums.include?(checksum)
90
+ end
91
+
92
+ alias :member? :include?
93
+
88
94
  def to_json(*a)
89
95
  result = {
90
96
  :guid => guid,
@@ -16,5 +16,5 @@
16
16
  # limitations under the License.
17
17
 
18
18
  class Chef
19
- VERSION = '0.9.0.a4'
19
+ VERSION = '0.9.0.a6'
20
20
  end
metadata CHANGED
@@ -6,8 +6,8 @@ version: !ruby/object:Gem::Version
6
6
  - 0
7
7
  - 9
8
8
  - 0
9
- - a4
10
- version: 0.9.0.a4
9
+ - a6
10
+ version: 0.9.0.a6
11
11
  platform: ruby
12
12
  authors:
13
13
  - Adam Jacob
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-06-04 00:00:00 -07:00
18
+ date: 2010-06-07 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency