class Chef::ChefFS::ChefFSDataStore
Translation layer between chef-zero's DataStore (a place where it expects files to be stored) and ChefFS (the user's repository directory layout).
chef-zero expects the data store to store files its way–for example, it expects get(“nodes/blah”) to return the JSON text for the blah node, and it expects get(“cookbooks/blah/1.0.0”) to return the JSON definition of the blah cookbook version 1.0.0.
The repository is defined the way the user wants their layout. These two things are very similar in layout (for example, nodes are stored under the nodes/ directory and their filename is the name of the node).
However, there are a few differences that make this more than just a raw file store:
-
Cookbooks are stored much differently.
- chef-zero places JSON text with the checksums for the cookbook at /cookbooks/NAME/VERSION, and expects the JSON to contain URLs to the actual files, which are stored elsewhere. - The repository contains an actual directory with just the cookbook files and a metadata.rb containing a version #. There is no JSON to be found. - Further, if versioned_cookbooks is false, that directory is named /cookbooks/NAME and only one version exists. If versioned_cookbooks is true, the directory is named /cookbooks/NAME-VERSION. - Therefore, ChefFSDataStore calculates the cookbook JSON by looking at the files in the cookbook and checksumming them, and reading metadata.rb for the version and dependency information. - ChefFSDataStore also modifies the cookbook file URLs so that they point to /file_store/repo/<filename> (the path to the actual file under the repository root). For example, /file_store/repo/apache2/metadata.rb or /file_store/repo/cookbooks/apache2/recipes/default.rb).
-
Sandboxes don't exist in the repository.
- ChefFSDataStore lets cookbooks be uploaded into a temporary memory storage, and when the cookbook is committed, copies the files onto the disk in the correct place (/cookbooks/apache2/recipes/default.rb).
-
Data bags:
- The Chef server expects data bags in /data/BAG/ITEM - The repository stores data bags in /data_bags/BAG/ITEM
-
JSON filenames are generally NAME.json in the repository (e.g. /nodes/foo.json).
-
Org membership: chef-zero stores user membership in an org as a series of empty files. If an org has jkeiser and cdoherty as members, chef-zero expects these files to exist:
-
`users/jkeiser` (content: '{}')
-
`users/cdoherty` (content: '{}')
ChefFS, on the other hand, stores user membership in an org as a single file, `members.json`, with content:
```json [ { "user": { "username": "jkeiser" } }, { "user": { "username": "cdoherty" } } ] ```
To translate between the two, we need to intercept requests to `users` like so:
-
`list(users)` -> `get(/members.json)`
-
`get(users/NAME)` -> `get(/members.json)`, see if it's in there
-
`create(users/NAME)` -> `get(/members.json)`, add name, `set(/members.json)`
-
`delete(users/NAME)` -> `get(/members.json)`, remove name, `set(/members.json)`
-
-
Org invitations: chef-zero stores org membership invitations as a series of empty files. If an org has invited jkeiser and cdoherty (and they have not yet accepted the invite), chef-zero expects these files to exist:
-
`association_requests/jkeiser` (content: '{}')
-
`association_requests/cdoherty` (content: '{}')
ChefFS, on the other hand, stores invitations as a single file, `invitations.json`, with content:
```json [ { "id" => "jkeiser-chef", 'username' => 'jkeiser' }, { "id" => "cdoherty-chef", 'username' => 'cdoherty' } ] ```
To translate between the two, we need to intercept requests to `users` like so:
-
`list(association_requests)` -> `get(/invitations.json)`
-
`get(association_requests/NAME)` -> `get(/invitations.json)`, see if it's in there
-
`create(association_requests/NAME)` -> `get(/invitations.json)`, add name, `set(/invitations.json)`
-
`delete(association_requests/NAME)` -> `get(/invitations.json)`, remove name, `set(/invitations.json)`
-
Constants
- BASE_DIRNAMES
The base directories in a Chef Repo; even when these don't exist, a matching GET for these objects will return an empty list instead of a 404.
Attributes
Public Class Methods
Create a new ChefFSDataStore
Arguments¶ ↑
- #chef_fs
-
A
ChefFS::FileSystem
object representing the repository root. Generally will be aChefFS::FileSystem::ChefRepositoryFileSystemRoot
object, created fromChefFS::Config.local_fs
.
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 157 def initialize(chef_fs, chef_config = Chef::Config) @chef_fs = chef_fs @memory_store = ChefZero::DataStore::MemoryStore.new @repo_mode = chef_config[:repo_mode] end
Public Instance Methods
If you want to get the contents of /data/x/y from the server, you say chef_fs.child('data').child('x').child('y').read. It will make exactly one network request: GET /data/x/y And that will return 404 if it doesn't exist.
ChefFS objects do not go to the network until you ask them for data. This means you can construct a /data/x/y ChefFS entry early.
Alternative: chef_fs.child('data') could have done a GET /data preemptively, allowing it to know whether child('x') was valid (GET /data gives you a list of data bags). Then child('x') could have done a GET /data/x, allowing it to know whether child('y') (the item) existed. Finally, we would do the GET /data/x/y to read the contents. Three network requests instead of 1.
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 202 def create(path, name, data, *options) if use_memory_store?(path) @memory_store.create(path, name, data, *options) elsif path[0] == "cookbooks" && path.length == 2 # Do nothing. The entry gets created when the cookbook is created. # /policy_groups/GROUP/policies/NAME elsif path[0] == "policy_groups" && path[2] == "policies" # Just set or create the proper entry in the hash update_json(to_chef_fs_path(path[0..1]), {}, *options) do |group| if policies.has_key?(path[3]) raise ChefZero::DataStore::DataAlreadyExistsError.new(path, group) end group["policies"] ||= {} group["policies"][path[3]] = { "revision_id" => Chef::JSONCompat.parse(data) } group end # create [/organizations/ORG]/users/NAME (with content '{}') # Manipulate the `members.json` file that contains a list of all users elsif is_org? && path == [ "users" ] update_json("members.json", [], *options) do |members| # Format of each entry: { "user": { "username": "jkeiser" } } if members.any? { |member| member["user"]["username"] == name } raise ChefZero::DataStore::DataAlreadyExistsError.new(path, entry) end # Actually add the user members << { "user" => { "username" => name } } end # create [/organizations/ORG]/association_requests/NAME (with content '{}') # Manipulate the `invitations.json` file that contains a list of all users elsif is_org? && path == [ "association_requests" ] update_json("invitations.json", [], *options) do |invitations| # Format of each entry: { "id" => "jkeiser-chef", 'username' => 'jkeiser' } if invitations.any? { |member| member["username"] == name } raise ChefZero::DataStore::DataAlreadyExistsError.new(path) end # Actually add the user (TODO insert org name??) invitations << { "username" => name } end else if !data.is_a?(String) raise "set only works with strings" end with_parent_dir(path + [name], *options) do |parent, name| begin parent.create_child(name, data) rescue Chef::ChefFS::FileSystem::AlreadyExistsError => e raise ChefZero::DataStore::DataAlreadyExistsError.new(to_zero_path(e.entry), e) end end end end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 170 def create_dir(path, name, *options) if use_memory_store?(path) @memory_store.create_dir(path, name, *options) else with_parent_dir(path + [name], *options) do |parent, name| begin parent.create_child(name, nil) rescue Chef::ChefFS::FileSystem::AlreadyExistsError => e raise ChefZero::DataStore::DataAlreadyExistsError.new(to_zero_path(e.entry), e) end end end end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 382 def delete(path) if use_memory_store?(path) @memory_store.delete(path) # DELETE /policy_groups/GROUP/policies/POLICY elsif path[0] == "policy_groups" && path[2] == "policies" && path.length == 4 update_json(to_chef_fs_path(path[0..1]), {}) do |group| unless group["policies"] && group["policies"].has_key?(path[3]) raise ChefZero::DataStore::DataNotFoundError.new(path) end group["policies"].delete(path[3]) group end # DELETE [/organizations/ORG]/users/NAME # Manipulates members.json elsif is_org? && path[0] == "users" && path.length == 2 update_json("members.json", []) do |members| result = members.reject { |member| member["user"]["username"] == path[1] } if result.size == members.size raise ChefZero::DataStore::DataNotFoundError.new(path) end result end # DELETE [/organizations/ORG]/users/NAME # Manipulates members.json elsif is_org? && path[0] == "association_requests" && path.length == 2 update_json("invitations.json", []) do |invitations| result = invitations.reject { |invitation| invitation["username"] == path[1] } if result.size == invitations.size raise ChefZero::DataStore::DataNotFoundError.new(path) end result end else with_entry(path) do |entry| begin if %w{cookbooks cookbook_artifacts}.include?(path[0]) && path.length >= 3 entry.delete(true) else entry.delete(false) end rescue Chef::ChefFS::FileSystem::NotFoundError => e raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e) end end end end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 433 def delete_dir(path, *options) if use_memory_store?(path) @memory_store.delete_dir(path, *options) # DELETE /policies/POLICY elsif path[0] == "policies" && path.length == 2 with_entry(path[0..0]) do |policies| # /policies: # - a-1.0.0.json # - a-1.0.1.json # - b-2.0.0.json found_policy = false policies.children.each do |policy| # We want to delete just the ones that == POLICY next unless policy.name.rpartition("-")[0] == path[1] policy.delete(false) FileSystemCache.instance.delete!(policy.file_path) found_policy = true end raise ChefZero::DataStore::DataNotFoundError.new(path) if !found_policy end else with_entry(path) do |entry| begin entry.delete(options.include?(:recursive)) rescue Chef::ChefFS::FileSystem::NotFoundError => e raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e) end end end end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 551 def exists?(path) if use_memory_store?(path) @memory_store.exists?(path) # /policy_groups/NAME/policies/POLICYNAME elsif path[0] == "policy_groups" && path[2] == "policies" && path.length == 4 group = get_json(to_chef_fs_path(path[0..1]), {}) group["policies"] && group["policies"].has_key?(path[3]) else path_always_exists?(path) || Chef::ChefFS::FileSystem.resolve_path(chef_fs, to_chef_fs_path(path)).exists? end end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 565 def exists_dir?(path) if use_memory_store?(path) @memory_store.exists_dir?(path) elsif %w{cookbooks cookbook_artifacts}.include?(path[0]) && path.length == 2 list([ path[0] ]).include?(path[1]) # /policies/NAME elsif path[0] == "policies" && path.length == 2 list([ path[0] ]).include?(path[1]) # /policy_groups/NAME/policies elsif path[0] == "policy_groups" && path[2] == "policies" && path.length == 3 exists_dir?(path[0..1]) else Chef::ChefFS::FileSystem.resolve_path(chef_fs, to_chef_fs_path(path)).exists? end end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 263 def get(path, request = nil) if use_memory_store?(path) @memory_store.get(path) elsif path[0] == "file_store" && path[1] == "repo" entry = Chef::ChefFS::FileSystem.resolve_path(chef_fs, path[2..-1].join("/")) begin entry.read rescue Chef::ChefFS::FileSystem::NotFoundError => e raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e) end # /policy_groups/NAME/policies/POLICYNAME: return the revision of the given policy elsif path[0] == "policy_groups" && path[2] == "policies" && path.length == 4 # Just set or create the proper entry in the hash policy_group = get_json(to_chef_fs_path(path[0..1]), {}) if !policy_group["policies"] || !policy_group["policies"][path[3]] raise ChefZero::DataStore::DataNotFoundError.new(path, entry) end # The policy group looks like: # { # "policies": { # "x": { "revision_id": "10" } # } # } Chef::JSONCompat.to_json_pretty(policy_group["policies"][path[3]]["revision_id"]) # GET [/organizations/ORG]/users/NAME -> /users/NAME # Manipulates members.json elsif is_org? && path[0] == "users" && path.length == 2 if get_json("members.json", []).any? { |member| member["user"]["username"] == path[1] } "{}" else raise ChefZero::DataStore::DataNotFoundError.new(path) end # GET [/organizations/ORG]/association_requests/NAME -> /users/NAME # Manipulates invites.json elsif is_org? && path[0] == "association_requests" && path.length == 2 if get_json("invites.json", []).any? { |member| member["user"]["username"] == path[1] } "{}" else raise ChefZero::DataStore::DataNotFoundError.new(path) end # GET /cookbooks/NAME/VERSION or /cookbook_artifacts/NAME/IDENTIFIER elsif %w{cookbooks cookbook_artifacts}.include?(path[0]) && path.length == 3 with_cookbook_manifest(path) do |manifest, entry| cookbook_type = path[0] manifest.each_pair do |key, value| if value.is_a?(Array) value.each do |file| if file.is_a?(Hash) && file.has_key?("checksum") relative = ["file_store", "repo", cookbook_type, entry.name ] relative += file[:path].split("/") file["url"] = ChefZero::RestBase.build_uri(request.base_uri, relative) end end end end if cookbook_type == "cookbook_artifacts" manifest["metadata"] = manifest["metadata"].to_hash manifest["metadata"].delete_if do |key, value| value == [] || (value == {} && !%w{dependencies attributes recipes}.include?(key)) || (value == "" && %w{source_url issues_url}.include?(key)) || (value == false && key == "privacy") end end Chef::JSONCompat.to_json_pretty(manifest) end else with_entry(path) do |entry| begin entry.read rescue Chef::ChefFS::FileSystem::NotFoundError => e raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e) end end end end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 466 def list(path) if use_memory_store?(path) @memory_store.list(path) # LIST /policies elsif path == [ "policies" ] with_entry([ path[0] ]) do |policies| begin policies.children.map { |policy| policy.name[0..-6].rpartition("-")[0] }.uniq rescue Chef::ChefFS::FileSystem::NotFoundError [] end end # LIST /policies/POLICY/revisions elsif path[0] == "policies" && path[2] == "revisions" && path.length == 3 with_entry([ path[0] ]) do |policies| # /policies: # - a-1.0.0.json # - a-1.0.1.json # - b-2.0.0.json revisions = [] policies.children.each do |policy| name, dash, revision = policy.name[0..-6].rpartition("-") revisions << revision if name == path[1] end raise ChefZero::DataStore::DataNotFoundError.new(path) if revisions.empty? revisions end elsif path[0] == "policy_groups" && path.length == 2 with_entry(path) do |entry| [ "policies" ] end elsif path[0] == "policy_groups" && path[2] == "policies" && path.length == 3 with_entry(path[0..1]) do |entry| policies = Chef::JSONCompat.parse(entry.read)["policies"] || {} policies.keys end elsif %w{cookbooks cookbook_artifacts}.include?(path[0]) && path.length == 1 with_entry(path) do |entry| begin entry.children.map { |child| child.chef_object.name.to_s }.uniq rescue Chef::ChefFS::FileSystem::NotFoundError # If the cookbooks dir doesn't exist, we have no cookbooks (not 404) [] end end elsif %w{cookbooks cookbook_artifacts}.include?(path[0]) && path.length == 2 result = with_entry([ path[0] ]) do |entry| cookbooks = entry.children.map { |child| child.chef_object } cookbooks.select { |cookbook| cookbook.name.to_s == path[1] }. map { |cookbook| cookbook.version } end if result.empty? raise ChefZero::DataStore::DataNotFoundError.new(path) end result else result = with_entry(path) do |entry| begin entry.children.map { |c| zero_filename(c) }.sort rescue Chef::ChefFS::FileSystem::NotFoundError => e # /cookbooks, /data, etc. never return 404 if path_always_exists?(path) [] else raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e) end end end # Older versions of chef-zero do not understand policies and cookbook_artifacts, # don't give that stuff to them if path == [] && ChefZero::VERSION.to_f < 4.4 result.reject! { |child| %w{policies policy_data cookbook_artifacts}.include?(child) } end result end end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 163 def publish_description "Reading and writing data to #{chef_fs.fs_description}" end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 348 def set(path, data, *options) if use_memory_store?(path) @memory_store.set(path, data, *options) else if !data.is_a?(String) raise "set only works with strings: #{path} = #{data.inspect}" end # Write out the files! if %w{cookbooks cookbook_artifacts}.include?(path[0]) && path.length == 3 write_cookbook(path, data, *options) # Handle /policy_groups/some_policy_group/policies/some_policy_name elsif path[0] == "policy_groups" && path[2] == "policies" && path.length == 4 # Just set or create the proper entry in the hash update_json(to_chef_fs_path(path[0..1]), {}, *options) do |group| group["policies"] ||= {} group["policies"][path[3]] = { "revision_id" => Chef::JSONCompat.parse(data) } group end else with_parent_dir(path, *options) do |parent, name| child = parent.child(name) if child.exists? child.write(data) else parent.create_child(name, data) end end end end end
Private Instance Methods
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 640 def _to_chef_fs_path(path) path = path.dup # /data -> /data_bags # /data/BAG -> /data_bags/BAG # /data/BAG/ITEM -> /data_bags/BAG/ITEM.json if path[0] == "data" path[0] = "data_bags" if path.length >= 3 path[2] = "#{path[2]}.json" end # /client_keys/CLIENT/keys -> /client_keys/CLIENT # /client_keys/CLIENT/keys/KEYNAME -> /client_keys/CLIENT/KEYNAME.json elsif path[0] == "client_keys" path.delete_at(2) if path.length >= 3 path[-1] += ".json" end # /policies/POLICY/revisions/REVISION -> /policies/POLICY-REVISION.json elsif path[0] == "policies" && path[2] == "revisions" && path.length >= 4 path = [ "policies", "#{path[1]}-#{path[3]}.json" ] elsif %w{cookbooks cookbook_artifacts}.include?(path[0]) if path.length == 2 raise ChefZero::DataStore::DataNotFoundError.new(path) elsif path.length >= 3 if chef_fs.versioned_cookbooks || path[0] == "cookbook_artifacts" # cookbooks/name/version -> cookbooks/name-version path = [ path[0], "#{path[1]}-#{path[2]}" ] + path[3..-1] else # cookbooks/name/version/... -> /cookbooks/name/... iff metadata says so version = get_single_cookbook_version(path) if path[2] == version path = path[0..1] + path[3..-1] else raise ChefZero::DataStore::DataNotFoundError.new(path) end end end elsif path[0] == "acls" # /acls/data -> /acls/data_bags if path[1] == "data" path[1] = "data_bags" end # /acls/containers|nodes|.../x.json # /acls/organization.json if path.length == 3 || path == %w{acls organization} path[-1] = "#{path[-1]}.json" end # /acls/containers|nodes|... do NOT drop into the next elsif, and do # not get .json appended # /nodes|clients|.../x.json elsif path.length == 2 path[-1] = "#{path[-1]}.json" end path end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 636 def chef_fs_filename(path) _to_chef_fs_path(path)[-1] end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 841 def ensure_dir(entry) return entry if entry.exists? parent = entry.parent if parent ensure_dir(parent) parent.create_child(entry.name) end end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 809 def get_dir(path, create = false) result = Chef::ChefFS::FileSystem.resolve_path(chef_fs, path.join("/")) if result.exists? result elsif create || path.size == 1 get_dir(path[0..-2], create).create_child(result.name, nil) else raise ChefZero::DataStore::DataNotFoundError.new(path) end end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 850 def get_json(path, default_value) entry = Chef::ChefFS::FileSystem.resolve_path(chef_fs, path) begin Chef::JSONCompat.parse(entry.read) rescue Chef::ChefFS::FileSystem::NotFoundError default_value end end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 820 def get_single_cookbook_version(path) dir = Chef::ChefFS::FileSystem.resolve_path(chef_fs, path[0..1].join("/")) metadata = ChefZero::CookbookData.metadata_from(dir, path[1], nil, []) metadata[:version] || "0.0.0" end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 859 def is_org? repo_mode == "hosted_everything" end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 755 def path_always_exists?(path) path.length == 1 && BASE_DIRNAMES.include?(path[0]) end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 625 def split_name_version(entry_name) name_version = entry_name.split("-") name = name_version[0..-2].join("-") version = name_version[-1] [name, version] end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 632 def to_chef_fs_path(path) _to_chef_fs_path(path).join("/") end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 704 def to_zero_path(entry) path = entry.path.split("/")[1..-1] if path[0] == "data_bags" path[0] = "data" if path.length >= 3 path[2] = path[2][0..-6] end # /client_keys/CLIENT -> /client_keys/CLIENT/keys # /client_keys/CLIENT/KEYNAME.json -> /client_keys/CLIENT/keys/KEYNAME elsif path[0] == "client_keys" if path.size == 2 path << "keys" elsif path.size > 2 path[2..-1] = [ "keys", path[-1][0..-6] ] end elsif %w{cookbooks cookbook_artifacts}.include?(path[0]) if chef_fs.versioned_cookbooks || path[0] == "cookbook_artifacts" # cookbooks/name-version/... -> cookbooks/name/version/... if path.length >= 2 name, version = split_name_version(path[1]) path = [ path[0], name, version ] + path[2..-1] end else if path.length >= 2 # cookbooks/name/... -> cookbooks/name/version/... version = get_single_cookbook_version(path) path = path[0..1] + [version] + path[2..-1] end end # /policies/NAME-REVISION.json -> /policies/NAME/revisions/REVISION elsif path[0] == "policies" if path.length >= 2 name, dash, revision = path[1][0..-6].rpartition("-") path = [ "policies", name, "revisions", revision ] end elsif path.length == 2 && path[0] != "cookbooks" path[1] = path[1].gsub(/\.(rb|json)/, "") end path end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 826 def update_json(path, default_value, *options) entry = Chef::ChefFS::FileSystem.resolve_path(chef_fs, path) begin input = Chef::JSONCompat.parse(entry.read) output = yield input entry.write(Chef::JSONCompat.to_json_pretty(output)) if output != Chef::JSONCompat.parse(entry.read) rescue Chef::ChefFS::FileSystem::NotFoundError # Send the default value to the caller, and create the entry if the caller updates it output = yield default_value parent = entry.parent parent = ensure_dir(parent) if options.include?(:create_dir) parent.create_child(entry.name, Chef::JSONCompat.to_json_pretty(output)) if output != [] end end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 587 def use_memory_store?(path) path[0] == "sandboxes" || path[0] == "file_store" && path[1] == "checksums" || path == %w{environments _default} end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 759 def with_cookbook_manifest(path) cookbook_type = path[0] begin # this is fast and equivalent to with_entry() that also returns the cb manifest entry = Chef::ChefFS::FileSystem.resolve_path(chef_fs, to_chef_fs_path(path)) yield Chef::CookbookManifest.new(entry.chef_object, policy_mode: cookbook_type == "cookbook_artifacts").to_hash, entry rescue Chef::ChefFS::FileSystem::NotFoundError, ChefZero::DataStore::DataNotFoundError => e # this is very slow and we walk through all the cookbook versions to find ones that have the correct name in the metadata dir = Chef::ChefFS::FileSystem.resolve_path(chef_fs, to_chef_fs_path([path[0]])) entry = dir.children.select do |child| child.chef_object.full_name == "#{path[1]}-#{path[2]}" || (child.chef_object.name.to_s == path[1] && child.chef_object.identifier == path[2]) end[0] raise ChefZero::DataStore::DataNotFoundError.new(path) if entry.nil? yield Chef::CookbookManifest.new(entry.chef_object, policy_mode: cookbook_type == "cookbook_artifacts").to_hash, entry end rescue Chef::ChefFS::FileSystem::NotFoundError => e raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e) end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 796 def with_dir(path) # Do not automatically create data bags create = !(path[0] == "data" && path.size >= 2) begin yield get_dir(_to_chef_fs_path(path), create) rescue Chef::ChefFS::FileSystem::NotFoundError => e err = ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e) err.set_backtrace(e.backtrace) raise err end end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 779 def with_entry(path) yield Chef::ChefFS::FileSystem.resolve_path(chef_fs, to_chef_fs_path(path)) rescue Chef::ChefFS::FileSystem::NotFoundError => e raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e) end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 785 def with_parent_dir(path, *options) path = _to_chef_fs_path(path) begin yield get_dir(path[0..-2], options.include?(:create_dir)), path[-1] rescue Chef::ChefFS::FileSystem::NotFoundError => e err = ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e) err.set_backtrace(e.backtrace) raise err end end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 591 def write_cookbook(path, data, *options) cookbook_type = path[0] if chef_fs.versioned_cookbooks cookbook_path = File.join(cookbook_type, "#{path[1]}-#{path[2]}") else cookbook_path = File.join(cookbook_type, path[1]) end # Create a little Chef::ChefFS memory filesystem with the data cookbook_fs = Chef::ChefFS::FileSystem::Memory::MemoryRoot.new("uploading") cookbook = Chef::JSONCompat.parse(data) cookbook.each_pair do |key, value| if value.is_a?(Array) value.each do |file| if file.is_a?(Hash) && file.has_key?("checksum") file_data = @memory_store.get(["file_store", "checksums", file["checksum"]]) cookbook_fs.add_file(File.join(cookbook_path, file["path"]), file_data) end end end end # Create the .uploaded-cookbook-version.json cookbooks = chef_fs.child(cookbook_type) if !cookbooks.exists? cookbooks = chef_fs.create_child(cookbook_type) end # We are calling a cookbooks-specific API, so get multiplexed_dirs out of the way if it is there if cookbooks.respond_to?(:multiplexed_dirs) cookbooks = cookbooks.write_dir end cookbooks.write_cookbook(cookbook_path, data, cookbook_fs) end
# File lib/chef/chef_fs/chef_fs_data_store.rb, line 751 def zero_filename(entry) to_zero_path(entry)[-1] end