runa-chef 0.8.0.1

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.
Files changed (216) hide show
  1. data/LICENSE +201 -0
  2. data/README.rdoc +136 -0
  3. data/bin/chef-client +26 -0
  4. data/bin/chef-solo +26 -0
  5. data/bin/knife +27 -0
  6. data/bin/shef +45 -0
  7. data/distro/README +2 -0
  8. data/distro/common/man/man1/chef-indexer.1 +42 -0
  9. data/distro/common/man/man1/chef-server.1 +108 -0
  10. data/distro/common/man/man8/chef-client.8 +61 -0
  11. data/distro/common/man/man8/chef-solo.8 +58 -0
  12. data/distro/common/man/man8/knife.8 +359 -0
  13. data/distro/debian/etc/init.d/chef-client +175 -0
  14. data/distro/debian/etc/init.d/chef-indexer +175 -0
  15. data/distro/debian/etc/init.d/chef-server +120 -0
  16. data/distro/redhat/etc/init.d/chef-client +78 -0
  17. data/distro/redhat/etc/init.d/chef-indexer +76 -0
  18. data/distro/redhat/etc/init.d/chef-server +78 -0
  19. data/distro/redhat/etc/sysconfig/chef-client +10 -0
  20. data/distro/redhat/etc/sysconfig/chef-indexer +8 -0
  21. data/distro/redhat/etc/sysconfig/chef-server +10 -0
  22. data/distro/suse/etc/init.d/chef-client +121 -0
  23. data/lib/chef.rb +49 -0
  24. data/lib/chef/api_client.rb +269 -0
  25. data/lib/chef/application.rb +98 -0
  26. data/lib/chef/application/agent.rb +18 -0
  27. data/lib/chef/application/client.rb +214 -0
  28. data/lib/chef/application/knife.rb +138 -0
  29. data/lib/chef/application/server.rb +19 -0
  30. data/lib/chef/application/solo.rb +214 -0
  31. data/lib/chef/cache.rb +61 -0
  32. data/lib/chef/cache/checksum.rb +70 -0
  33. data/lib/chef/certificate.rb +154 -0
  34. data/lib/chef/client.rb +323 -0
  35. data/lib/chef/compile.rb +158 -0
  36. data/lib/chef/config.rb +195 -0
  37. data/lib/chef/cookbook.rb +198 -0
  38. data/lib/chef/cookbook/metadata.rb +487 -0
  39. data/lib/chef/cookbook/metadata/version.rb +87 -0
  40. data/lib/chef/cookbook_loader.rb +180 -0
  41. data/lib/chef/couchdb.rb +273 -0
  42. data/lib/chef/daemon.rb +170 -0
  43. data/lib/chef/data_bag.rb +216 -0
  44. data/lib/chef/data_bag_item.rb +227 -0
  45. data/lib/chef/exceptions.rb +39 -0
  46. data/lib/chef/file_cache.rb +205 -0
  47. data/lib/chef/knife.rb +300 -0
  48. data/lib/chef/knife/client_bulk_delete.rb +41 -0
  49. data/lib/chef/knife/client_create.rb +55 -0
  50. data/lib/chef/knife/client_delete.rb +37 -0
  51. data/lib/chef/knife/client_edit.rb +37 -0
  52. data/lib/chef/knife/client_list.rb +40 -0
  53. data/lib/chef/knife/client_reregister.rb +48 -0
  54. data/lib/chef/knife/client_show.rb +42 -0
  55. data/lib/chef/knife/configure.rb +84 -0
  56. data/lib/chef/knife/cookbook_bulk_delete.rb +47 -0
  57. data/lib/chef/knife/cookbook_delete.rb +41 -0
  58. data/lib/chef/knife/cookbook_download.rb +57 -0
  59. data/lib/chef/knife/cookbook_list.rb +41 -0
  60. data/lib/chef/knife/cookbook_metadata.rb +87 -0
  61. data/lib/chef/knife/cookbook_show.rb +75 -0
  62. data/lib/chef/knife/cookbook_upload.rb +173 -0
  63. data/lib/chef/knife/data_bag_create.rb +43 -0
  64. data/lib/chef/knife/data_bag_delete.rb +43 -0
  65. data/lib/chef/knife/data_bag_edit.rb +49 -0
  66. data/lib/chef/knife/data_bag_list.rb +42 -0
  67. data/lib/chef/knife/data_bag_show.rb +40 -0
  68. data/lib/chef/knife/ec2_instance_data.rb +46 -0
  69. data/lib/chef/knife/node_bulk_delete.rb +44 -0
  70. data/lib/chef/knife/node_create.rb +39 -0
  71. data/lib/chef/knife/node_delete.rb +36 -0
  72. data/lib/chef/knife/node_edit.rb +36 -0
  73. data/lib/chef/knife/node_from_file.rb +42 -0
  74. data/lib/chef/knife/node_list.rb +41 -0
  75. data/lib/chef/knife/node_run_list_add.rb +64 -0
  76. data/lib/chef/knife/node_run_list_remove.rb +45 -0
  77. data/lib/chef/knife/node_show.rb +46 -0
  78. data/lib/chef/knife/role_bulk_delete.rb +45 -0
  79. data/lib/chef/knife/role_create.rb +44 -0
  80. data/lib/chef/knife/role_delete.rb +36 -0
  81. data/lib/chef/knife/role_edit.rb +37 -0
  82. data/lib/chef/knife/role_from_file.rb +46 -0
  83. data/lib/chef/knife/role_list.rb +40 -0
  84. data/lib/chef/knife/role_show.rb +43 -0
  85. data/lib/chef/knife/search.rb +94 -0
  86. data/lib/chef/log.rb +39 -0
  87. data/lib/chef/mixin/check_helper.rb +31 -0
  88. data/lib/chef/mixin/checksum.rb +32 -0
  89. data/lib/chef/mixin/command.rb +390 -0
  90. data/lib/chef/mixin/convert_to_class_name.rb +57 -0
  91. data/lib/chef/mixin/create_path.rb +56 -0
  92. data/lib/chef/mixin/deep_merge.rb +33 -0
  93. data/lib/chef/mixin/find_preferred_file.rb +92 -0
  94. data/lib/chef/mixin/from_file.rb +50 -0
  95. data/lib/chef/mixin/generate_url.rb +58 -0
  96. data/lib/chef/mixin/language.rb +107 -0
  97. data/lib/chef/mixin/language_include_attribute.rb +56 -0
  98. data/lib/chef/mixin/language_include_recipe.rb +53 -0
  99. data/lib/chef/mixin/params_validate.rb +197 -0
  100. data/lib/chef/mixin/recipe_definition_dsl_core.rb +79 -0
  101. data/lib/chef/mixin/template.rb +94 -0
  102. data/lib/chef/nanite.rb +100 -0
  103. data/lib/chef/node.rb +463 -0
  104. data/lib/chef/node/attribute.rb +412 -0
  105. data/lib/chef/openid_registration.rb +181 -0
  106. data/lib/chef/platform.rb +268 -0
  107. data/lib/chef/provider.rb +101 -0
  108. data/lib/chef/provider/breakpoint.rb +36 -0
  109. data/lib/chef/provider/cron.rb +184 -0
  110. data/lib/chef/provider/deploy.rb +314 -0
  111. data/lib/chef/provider/deploy/revision.rb +70 -0
  112. data/lib/chef/provider/deploy/timestamped.rb +33 -0
  113. data/lib/chef/provider/directory.rb +72 -0
  114. data/lib/chef/provider/erl_call.rb +72 -0
  115. data/lib/chef/provider/execute.rb +58 -0
  116. data/lib/chef/provider/file.rb +195 -0
  117. data/lib/chef/provider/git.rb +203 -0
  118. data/lib/chef/provider/group.rb +120 -0
  119. data/lib/chef/provider/group/dscl.rb +128 -0
  120. data/lib/chef/provider/group/gpasswd.rb +50 -0
  121. data/lib/chef/provider/group/groupadd.rb +78 -0
  122. data/lib/chef/provider/group/pw.rb +88 -0
  123. data/lib/chef/provider/group/usermod.rb +57 -0
  124. data/lib/chef/provider/http_request.rb +106 -0
  125. data/lib/chef/provider/ifconfig.rb +131 -0
  126. data/lib/chef/provider/link.rb +157 -0
  127. data/lib/chef/provider/mdadm.rb +88 -0
  128. data/lib/chef/provider/mount.rb +117 -0
  129. data/lib/chef/provider/mount/mount.rb +208 -0
  130. data/lib/chef/provider/package.rb +160 -0
  131. data/lib/chef/provider/package/apt.rb +110 -0
  132. data/lib/chef/provider/package/dpkg.rb +109 -0
  133. data/lib/chef/provider/package/easy_install.rb +106 -0
  134. data/lib/chef/provider/package/freebsd.rb +153 -0
  135. data/lib/chef/provider/package/macports.rb +105 -0
  136. data/lib/chef/provider/package/portage.rb +124 -0
  137. data/lib/chef/provider/package/rpm.rb +99 -0
  138. data/lib/chef/provider/package/rubygems.rb +136 -0
  139. data/lib/chef/provider/package/yum-dump.py +125 -0
  140. data/lib/chef/provider/package/yum.rb +175 -0
  141. data/lib/chef/provider/package/zypper.rb +132 -0
  142. data/lib/chef/provider/remote_directory.rb +126 -0
  143. data/lib/chef/provider/remote_file.rb +141 -0
  144. data/lib/chef/provider/route.rb +118 -0
  145. data/lib/chef/provider/ruby_block.rb +33 -0
  146. data/lib/chef/provider/script.rb +42 -0
  147. data/lib/chef/provider/service.rb +135 -0
  148. data/lib/chef/provider/service/debian.rb +64 -0
  149. data/lib/chef/provider/service/freebsd.rb +156 -0
  150. data/lib/chef/provider/service/gentoo.rb +54 -0
  151. data/lib/chef/provider/service/init.rb +71 -0
  152. data/lib/chef/provider/service/redhat.rb +62 -0
  153. data/lib/chef/provider/service/simple.rb +114 -0
  154. data/lib/chef/provider/subversion.rb +156 -0
  155. data/lib/chef/provider/template.rb +175 -0
  156. data/lib/chef/provider/user.rb +170 -0
  157. data/lib/chef/provider/user/dscl.rb +280 -0
  158. data/lib/chef/provider/user/pw.rb +113 -0
  159. data/lib/chef/provider/user/useradd.rb +108 -0
  160. data/lib/chef/recipe.rb +105 -0
  161. data/lib/chef/resource.rb +380 -0
  162. data/lib/chef/resource/apt_package.rb +34 -0
  163. data/lib/chef/resource/bash.rb +33 -0
  164. data/lib/chef/resource/breakpoint.rb +35 -0
  165. data/lib/chef/resource/cron.rb +179 -0
  166. data/lib/chef/resource/csh.rb +33 -0
  167. data/lib/chef/resource/deploy.rb +359 -0
  168. data/lib/chef/resource/deploy_revision.rb +35 -0
  169. data/lib/chef/resource/directory.rb +76 -0
  170. data/lib/chef/resource/dpkg_package.rb +34 -0
  171. data/lib/chef/resource/easy_install_package.rb +41 -0
  172. data/lib/chef/resource/erl_call.rb +83 -0
  173. data/lib/chef/resource/execute.rb +127 -0
  174. data/lib/chef/resource/file.rb +84 -0
  175. data/lib/chef/resource/gem_package.rb +41 -0
  176. data/lib/chef/resource/git.rb +36 -0
  177. data/lib/chef/resource/group.rb +70 -0
  178. data/lib/chef/resource/http_request.rb +52 -0
  179. data/lib/chef/resource/ifconfig.rb +134 -0
  180. data/lib/chef/resource/link.rb +78 -0
  181. data/lib/chef/resource/macports_package.rb +29 -0
  182. data/lib/chef/resource/mdadm.rb +82 -0
  183. data/lib/chef/resource/mount.rb +135 -0
  184. data/lib/chef/resource/package.rb +80 -0
  185. data/lib/chef/resource/perl.rb +33 -0
  186. data/lib/chef/resource/portage_package.rb +33 -0
  187. data/lib/chef/resource/python.rb +33 -0
  188. data/lib/chef/resource/remote_directory.rb +91 -0
  189. data/lib/chef/resource/remote_file.rb +60 -0
  190. data/lib/chef/resource/route.rb +135 -0
  191. data/lib/chef/resource/ruby.rb +33 -0
  192. data/lib/chef/resource/ruby_block.rb +39 -0
  193. data/lib/chef/resource/scm.rb +137 -0
  194. data/lib/chef/resource/script.rb +51 -0
  195. data/lib/chef/resource/service.rb +134 -0
  196. data/lib/chef/resource/subversion.rb +34 -0
  197. data/lib/chef/resource/template.rb +60 -0
  198. data/lib/chef/resource/timestamped_deploy.rb +31 -0
  199. data/lib/chef/resource/user.rb +101 -0
  200. data/lib/chef/resource_collection.rb +212 -0
  201. data/lib/chef/resource_collection/stepable_iterator.rb +124 -0
  202. data/lib/chef/resource_definition.rb +67 -0
  203. data/lib/chef/rest.rb +298 -0
  204. data/lib/chef/role.rb +301 -0
  205. data/lib/chef/run_list.rb +164 -0
  206. data/lib/chef/runner.rb +130 -0
  207. data/lib/chef/search/query.rb +71 -0
  208. data/lib/chef/shef.rb +220 -0
  209. data/lib/chef/shef/ext.rb +297 -0
  210. data/lib/chef/shef/shef_session.rb +175 -0
  211. data/lib/chef/streaming_cookbook_uploader.rb +185 -0
  212. data/lib/chef/tasks/chef_repo.rake +245 -0
  213. data/lib/chef/util/file_edit.rb +125 -0
  214. data/lib/chef/util/fileedit.rb +121 -0
  215. data/lib/chef/webui_user.rb +231 -0
  216. metadata +398 -0
@@ -0,0 +1,87 @@
1
+ #
2
+ # Author:: AJ Christensen (<aj@opscode.com>)
3
+ # Copyright:: Copyright (c) 2008 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/cookbook'
20
+ require 'chef/cookbook/metadata'
21
+
22
+ class Chef::Cookbook::Metadata
23
+ class Version
24
+ include Comparable
25
+
26
+ attr_accessor :major,
27
+ :minor,
28
+ :patch
29
+
30
+ def initialize(str="")
31
+ @major, @minor, @patch = _parse(str)
32
+ end
33
+
34
+ def _parse(str="")
35
+ @major, @minor, @patch = case str
36
+ when /^(\d+)\.(\d+)\.(\d+)$/
37
+ [ $1.to_i, $2.to_i, $3.to_i ]
38
+ when /^(\d+)\.(\d+)$/
39
+ [ $1.to_i, $2.to_i, 0 ]
40
+ else
41
+ raise "Metadata version does not match 'x.y.z' or 'x.y'"
42
+ end
43
+ end
44
+
45
+ def inspect
46
+ "#{@major}.#{@minor}.#{@patch}"
47
+ end
48
+
49
+ def to_s
50
+ "#{@major}.#{@minor}.#{@patch}"
51
+ end
52
+
53
+ def <=>(v)
54
+ major, minor, patch = (
55
+ [ :major, :minor, :patch ].collect do |method|
56
+ self.send(method) <=> v.send(method)
57
+ end
58
+ )
59
+
60
+ Chef::Log.debug "(#{self.to_s}/#{v.to_s}) major,minor,patch: #{[major,minor,patch].join(',')}"
61
+
62
+ # all these returns feels like C, surely there is a better way!
63
+
64
+ if major == 0 && minor == 0 && patch == 0
65
+ comp = 0
66
+ end
67
+
68
+ if major == 1
69
+ comp = 1
70
+ end
71
+
72
+ if major == 0 && minor == 1 && patch == -1
73
+ comp = 1
74
+ end
75
+
76
+ if minor == 1 && major == 0 && patch == 0
77
+ comp = 1
78
+ end
79
+
80
+ if patch == 1 && major == 0 && minor == 0
81
+ comp = 1
82
+ end
83
+
84
+ return (comp || -1)
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,180 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Author:: Christopher Walters (<cw@opscode.com>)
4
+ # Author:: Daniel DeLeo (<dan@kallistec.com>)
5
+ # Copyright:: Copyright (c) 2008 Opscode, Inc.
6
+ # Copyright:: Copyright (c) 2009 Daniel DeLeo
7
+ # License:: Apache License, Version 2.0
8
+ #
9
+ # Licensed under the Apache License, Version 2.0 (the "License");
10
+ # you may not use this file except in compliance with the License.
11
+ # You may obtain a copy of the License at
12
+ #
13
+ # http://www.apache.org/licenses/LICENSE-2.0
14
+ #
15
+ # Unless required by applicable law or agreed to in writing, software
16
+ # distributed under the License is distributed on an "AS IS" BASIS,
17
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ # See the License for the specific language governing permissions and
19
+ # limitations under the License.
20
+
21
+ require 'chef/config'
22
+ require 'chef/cookbook'
23
+ require 'chef/cookbook/metadata'
24
+
25
+ class Chef
26
+ class CookbookLoader
27
+
28
+ attr_accessor :cookbook, :metadata
29
+
30
+ include Enumerable
31
+
32
+ def initialize()
33
+ @cookbook = Hash.new
34
+ @metadata = Hash.new
35
+ @ignore_regexes = Hash.new { |hsh, key| hsh[key] = Array.new }
36
+ load_cookbooks
37
+ end
38
+
39
+ def load_cookbooks
40
+ cookbook_settings = Hash.new
41
+ [Chef::Config.cookbook_path].flatten.each do |cb_path|
42
+ Dir[File.join(cb_path, "*")].each do |cookbook|
43
+ next unless File.directory?(cookbook)
44
+ cookbook_name = File.basename(cookbook).to_sym
45
+ unless cookbook_settings.has_key?(cookbook_name)
46
+ cookbook_settings[cookbook_name] = {
47
+ :attribute_files => Hash.new,
48
+ :definition_files => Hash.new,
49
+ :recipe_files => Hash.new,
50
+ :template_files => Hash.new,
51
+ :remote_files => Hash.new,
52
+ :lib_files => Hash.new,
53
+ :resource_files => Hash.new,
54
+ :provider_files => Hash.new,
55
+ :metadata_files => Array.new
56
+ }
57
+ end
58
+ ignore_regexes = load_ignore_file(File.join(cookbook, "ignore"))
59
+ @ignore_regexes[cookbook_name].concat(ignore_regexes)
60
+
61
+ load_files_unless_basename(
62
+ File.join(cookbook, "attributes", "*.rb"),
63
+ cookbook_settings[cookbook_name][:attribute_files]
64
+ )
65
+ load_files_unless_basename(
66
+ File.join(cookbook, "definitions", "*.rb"),
67
+ cookbook_settings[cookbook_name][:definition_files]
68
+ )
69
+ load_files_unless_basename(
70
+ File.join(cookbook, "recipes", "*.rb"),
71
+ cookbook_settings[cookbook_name][:recipe_files]
72
+ )
73
+ load_files_unless_basename(
74
+ File.join(cookbook, "libraries", "*.rb"),
75
+ cookbook_settings[cookbook_name][:lib_files]
76
+ )
77
+ load_cascading_files(
78
+ "*.erb",
79
+ File.join(cookbook, "templates"),
80
+ cookbook_settings[cookbook_name][:template_files]
81
+ )
82
+ load_cascading_files(
83
+ "*",
84
+ File.join(cookbook, "files"),
85
+ cookbook_settings[cookbook_name][:remote_files]
86
+ )
87
+ load_cascading_files(
88
+ "*.rb",
89
+ File.join(cookbook, "resources"),
90
+ cookbook_settings[cookbook_name][:resource_files]
91
+ )
92
+ load_cascading_files(
93
+ "*.rb",
94
+ File.join(cookbook, "providers"),
95
+ cookbook_settings[cookbook_name][:provider_files]
96
+ )
97
+
98
+ if File.exists?(File.join(cookbook, "metadata.json"))
99
+ cookbook_settings[cookbook_name][:metadata_files] << File.join(cookbook, "metadata.json")
100
+ end
101
+ end
102
+ end
103
+ remove_ignored_files_from(cookbook_settings)
104
+
105
+ cookbook_settings.each_key do |cookbook|
106
+ @cookbook[cookbook] = Chef::Cookbook.new(cookbook)
107
+ @cookbook[cookbook].attribute_files = cookbook_settings[cookbook][:attribute_files].values
108
+ @cookbook[cookbook].definition_files = cookbook_settings[cookbook][:definition_files].values
109
+ @cookbook[cookbook].recipe_files = cookbook_settings[cookbook][:recipe_files].values
110
+ @cookbook[cookbook].template_files = cookbook_settings[cookbook][:template_files].values
111
+ @cookbook[cookbook].remote_files = cookbook_settings[cookbook][:remote_files].values
112
+ @cookbook[cookbook].lib_files = cookbook_settings[cookbook][:lib_files].values
113
+ @cookbook[cookbook].resource_files = cookbook_settings[cookbook][:resource_files].values
114
+ @cookbook[cookbook].provider_files = cookbook_settings[cookbook][:provider_files].values
115
+ @metadata[cookbook] = Chef::Cookbook::Metadata.new(@cookbook[cookbook])
116
+ cookbook_settings[cookbook][:metadata_files].each do |meta_json|
117
+ @metadata[cookbook].from_json(IO.read(meta_json))
118
+ end
119
+ end
120
+ end
121
+
122
+ def [](cookbook)
123
+ if @cookbook.has_key?(cookbook.to_sym)
124
+ @cookbook[cookbook.to_sym]
125
+ else
126
+ raise ArgumentError, "Cannot find a cookbook named #{cookbook.to_s}; did you forget to add metadata to a cookbook? (http://wiki.opscode.com/display/chef/Metadata)"
127
+ end
128
+ end
129
+
130
+ def each
131
+ @cookbook.keys.sort { |a,b| a.to_s <=> b.to_s }.each do |cname|
132
+ yield @cookbook[cname]
133
+ end
134
+ end
135
+
136
+ private
137
+
138
+ def load_ignore_file(ignore_file)
139
+ results = Array.new
140
+ if File.exists?(ignore_file) && File.readable?(ignore_file)
141
+ IO.foreach(ignore_file) do |line|
142
+ next if line =~ /^#/
143
+ next if line =~ /^\w*$/
144
+ line.chomp!
145
+ results << Regexp.new(line)
146
+ end
147
+ end
148
+ results
149
+ end
150
+
151
+ def remove_ignored_files_from(cookbook_settings)
152
+ file_types_to_inspect = [ :attribute_files, :definition_files, :recipe_files, :template_files,
153
+ :remote_files, :lib_files, :resource_files, :provider_files]
154
+
155
+ @ignore_regexes.each do |cookbook_name, regexes|
156
+ regexes.each do |regex|
157
+ settings = cookbook_settings[cookbook_name]
158
+ file_types_to_inspect.each do |file_type|
159
+ settings[file_type].delete_if { |uniqname, fullpath| fullpath.match(regex) }
160
+ end
161
+ end
162
+ end
163
+ end
164
+
165
+ def load_cascading_files(file_glob, base_path, result_hash)
166
+ rm_base_path = /^#{base_path}\/(.+)$/
167
+ # To handle dotfiles like .ssh
168
+ Dir.glob(File.join(base_path, "**/#{file_glob}"), File::FNM_DOTMATCH).each do |file|
169
+ result_hash[rm_base_path.match(file)[1]] = file
170
+ end
171
+ end
172
+
173
+ def load_files_unless_basename(file_glob, result_hash)
174
+ Dir[file_glob].each do |file|
175
+ result_hash[File.basename(file)] = file
176
+ end
177
+ end
178
+
179
+ end
180
+ end
@@ -0,0 +1,273 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Copyright:: Copyright (c) 2008 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ require 'chef/mixin/params_validate'
19
+ require 'chef/config'
20
+ require 'chef/rest'
21
+ require 'chef/log'
22
+ require 'digest/sha2'
23
+ require 'json'
24
+ require 'chef/nanite'
25
+
26
+ # We want to fail on create if uuidtools isn't installed
27
+ begin
28
+ require 'uuidtools'
29
+ rescue LoadError
30
+ end
31
+
32
+ class Chef
33
+ class CouchDB
34
+ include Chef::Mixin::ParamsValidate
35
+
36
+ def initialize(url=nil, db=Chef::Config[:couchdb_database])
37
+ url ||= Chef::Config[:couchdb_url]
38
+ @db = db
39
+ @rest = Chef::REST.new(url, nil, nil)
40
+ end
41
+
42
+ def couchdb_database(args=nil)
43
+ if args
44
+ @db = args
45
+ else
46
+ @db
47
+ end
48
+ end
49
+
50
+ def create_id_map
51
+ create_design_document(
52
+ "id_map",
53
+ {
54
+ "version" => 1,
55
+ "language" => "javascript",
56
+ "views" => {
57
+ "name_to_id" => {
58
+ "map" => <<-EOJS
59
+ function(doc) {
60
+ emit([ doc.chef_type, doc.name], doc._id);
61
+ }
62
+ EOJS
63
+ },
64
+ "id_to_name" => {
65
+ "map" => <<-EOJS
66
+ function(doc) {
67
+ emit(doc._id, [ doc.chef_type, doc.name ]);
68
+ }
69
+ EOJS
70
+ }
71
+ }
72
+ }
73
+ )
74
+ end
75
+
76
+ def create_db
77
+ @database_list = @rest.get_rest("_all_dbs")
78
+ unless @database_list.detect { |db| db == couchdb_database }
79
+ response = @rest.put_rest(couchdb_database, Hash.new)
80
+ end
81
+ couchdb_database
82
+ end
83
+
84
+ def create_design_document(name, data)
85
+ create_db
86
+ to_update = true
87
+ begin
88
+ old_doc = @rest.get_rest("#{couchdb_database}/_design/#{name}")
89
+ if data["version"] != old_doc["version"]
90
+ data["_rev"] = old_doc["_rev"]
91
+ Chef::Log.debug("Updating #{name} views")
92
+ else
93
+ to_update = false
94
+ end
95
+ rescue
96
+ Chef::Log.debug("Creating #{name} views for the first time because: #{$!}")
97
+ end
98
+ if to_update
99
+ @rest.put_rest("#{couchdb_database}/_design%2F#{name}", data)
100
+ end
101
+ true
102
+ end
103
+
104
+ def store(obj_type, name, object)
105
+ validate(
106
+ {
107
+ :obj_type => obj_type,
108
+ :name => name,
109
+ :object => object,
110
+ },
111
+ {
112
+ :object => { :respond_to => :to_json },
113
+ }
114
+ )
115
+ r = get_view("id_map", "name_to_id", :key => [ obj_type, name ])
116
+ uuid = nil
117
+ if r["rows"].length == 1
118
+ uuid = r["rows"][0]["id"]
119
+ else
120
+ uuid = UUIDTools::UUID.random_create.to_s
121
+ end
122
+
123
+ r = @rest.put_rest("#{couchdb_database}/#{uuid}", object)
124
+ Chef::Log.info("Sending #{uuid} to Nanite for indexing..")
125
+ n = Chef::Nanite.push(
126
+ "/index/add",
127
+ {
128
+ :id => uuid,
129
+ :database => couchdb_database,
130
+ :type => obj_type,
131
+ :item => object
132
+ },
133
+ :persistent => true
134
+ )
135
+ r
136
+ end
137
+
138
+ def load(obj_type, name)
139
+ validate(
140
+ {
141
+ :obj_type => obj_type,
142
+ :name => name,
143
+ },
144
+ {
145
+ :obj_type => { :kind_of => String },
146
+ :name => { :kind_of => String },
147
+ }
148
+ )
149
+ doc = find_by_name(obj_type, name)
150
+ doc.couchdb = self if doc.respond_to?(:couchdb)
151
+ doc
152
+ end
153
+
154
+ def delete(obj_type, name, rev=nil)
155
+ validate(
156
+ {
157
+ :obj_type => obj_type,
158
+ :name => name,
159
+ },
160
+ {
161
+ :obj_type => { :kind_of => String },
162
+ :name => { :kind_of => String },
163
+ }
164
+ )
165
+ del_id = nil
166
+ last_obj, obj_id = find_by_name(obj_type, name, true)
167
+ unless rev
168
+ if last_obj.respond_to?(:couchdb_rev)
169
+ rev = last_obj.couchdb_rev
170
+ else
171
+ rev = last_obj['_rev']
172
+ end
173
+ end
174
+ r = @rest.delete_rest("#{couchdb_database}/#{obj_id}?rev=#{rev}")
175
+ r.couchdb = self if r.respond_to?(:couchdb)
176
+ Chef::Log.info("Sending #{obj_id} to Nanite for deletion..")
177
+ n = Chef::Nanite.push(
178
+ "/index/delete",
179
+ {
180
+ :id => obj_id,
181
+ :database => couchdb_database,
182
+ :type => obj_type
183
+ },
184
+ :persistent => true
185
+ )
186
+ r
187
+ end
188
+
189
+ def list(view, inflate=false)
190
+ validate(
191
+ {
192
+ :view => view,
193
+ },
194
+ {
195
+ :view => { :kind_of => String }
196
+ }
197
+ )
198
+ if inflate
199
+ r = @rest.get_rest(view_uri(view, "all"))
200
+ r["rows"].each { |i| i["value"].couchdb = self if i["value"].respond_to?(:couchdb=) }
201
+ r
202
+ else
203
+ r = @rest.get_rest(view_uri(view, "all_id"))
204
+ end
205
+ r
206
+ end
207
+
208
+ def has_key?(obj_type, name)
209
+ validate(
210
+ {
211
+ :obj_type => obj_type,
212
+ :name => name,
213
+ },
214
+ {
215
+ :obj_type => { :kind_of => String },
216
+ :name => { :kind_of => String },
217
+ }
218
+ )
219
+ begin
220
+ find_by_name(obj_type, name)
221
+ true
222
+ rescue
223
+ false
224
+ end
225
+ end
226
+
227
+ def find_by_name(obj_type, name, with_id=false)
228
+ r = get_view("id_map", "name_to_id", :key => [ obj_type, name ], :include_docs => true)
229
+ if r["rows"].length == 0
230
+ raise Chef::Exceptions::CouchDBNotFound, "Cannot find #{obj_type} #{name} in CouchDB!"
231
+ end
232
+ if with_id
233
+ [ r["rows"][0]["doc"], r["rows"][0]["id"] ]
234
+ else
235
+ r["rows"][0]["doc"]
236
+ end
237
+ end
238
+
239
+ def get_view(design, view, options={})
240
+ view_string = view_uri(design, view)
241
+ view_string << "?" if options.length != 0
242
+ first = true;
243
+ options.each { |k,v| view_string << "#{first ? '' : '&'}#{k}=#{URI.escape(v.to_json)}"; first = false }
244
+ @rest.get_rest(view_string)
245
+ end
246
+
247
+ def bulk_get(*to_fetch)
248
+ response = @rest.post_rest("#{couchdb_database}/_all_docs?include_docs=true", { "keys" => to_fetch.flatten })
249
+ response["rows"].collect { |r| r["doc"] }
250
+ end
251
+
252
+ def view_uri(design, view)
253
+ Chef::Config[:couchdb_version] ||= @rest.run_request(:GET,
254
+ URI.parse(@rest.url + "/"),
255
+ {},
256
+ 10,
257
+ false)["version"].gsub(/-.+/,"").to_f
258
+ case Chef::Config[:couchdb_version]
259
+ when 0.8
260
+ "#{couchdb_database}/_view/#{design}/#{view}"
261
+ else
262
+ "#{couchdb_database}/_design/#{design}/_view/#{view}"
263
+ end
264
+ end
265
+
266
+ private
267
+
268
+ def safe_name(name)
269
+ name.gsub(/\./, "_")
270
+ end
271
+
272
+ end
273
+ end