class Chef::ChefFS::FileSystem::ChefServer::PolicyGroupEntry

Represents an entire policy group. Server path: /organizations/ORG/policy_groups/GROUP Repository path: policy_groupsGROUP.json Format: {

"policies": {
  "a": { "revision_id": "1.0.0" }
}

}

Public Instance Methods

write(file_contents) click to toggle source

write is different. For each policy:

  • PUT /organizations/ORG/policy_groups/GROUP/policies/POLICY

For each policy on the server but not the client:

  • DELETE /organizations/ORG/policy_groups/GROUP/policies/POLICY

If the server has associations for a, b and c, And the client wants associations for a, x and y, We must PUT a, x and y And DELETE b and c

# File lib/chef/chef_fs/file_system/chef_server/policy_group_entry.rb, line 50
def write(file_contents)
  # Parse the contents to ensure they are valid JSON
  begin
    object = Chef::JSONCompat.parse(file_contents)
  rescue Chef::Exceptions::JSON::ParseError => e
    raise Chef::ChefFS::FileSystem::OperationFailedError.new(:create_child, self, e, "Parse error reading JSON creating child '#{name}': #{e}")
  end

  if data_handler
    object = data_handler.normalize_for_put(object, self)
    data_handler.verify_integrity(object, self) do |error|
      raise Chef::ChefFS::FileSystem::OperationFailedError.new(:create_child, self, nil, "Error creating '#{name}': #{error}")
    end
  end

  begin

    # this should all get carted to PolicyGroupEntry#write.

    # the server demands the full policy data, but we want users' local policy_group documents to just
    # have the data you'd get from GET /policy_groups/POLICY_GROUP. so we try to fetch that.

    # ordinarily this would be POST to the normal URL, but we do PUT to
    # /organizations/{organization}/policy_groups/{policy_group}/policies/{policy_name} with the full
    # policy data, for each individual policy.
    policy_datas = {}

    object["policies"].each do |policy_name, policy_data|
      policy_path = "/policies/#{policy_name}/revisions/#{policy_data["revision_id"]}"

      get_data =
        begin
          rest.get(policy_path)
        rescue Net::HTTPClientException => e
          raise "Could not find policy '#{policy_name}'' with revision '#{policy_data["revision_id"]}'' on the server"
        end

      # GET policy data
      server_policy_data = Chef::JSONCompat.parse(get_data)

      # if it comes back 404, raise an Exception with "Policy file X does not exist with revision Y on the server"

      # otherwise, add it to the list of policyfile datas.
      policy_datas[policy_name] = server_policy_data
    end

    begin
      existing_group = Chef::JSONCompat.parse(read)
    rescue NotFoundError
      # It's OK if the group doesn't already exist, just means no existing policies
    end

    # now we have the fullpolicy data for each policies, which is what the PUT endpoint demands.
    policy_datas.each do |policy_name, policy_data|
      # PUT /organizations/ORG/policy_groups/GROUP/policies/NAME
      rest.put("#{api_path}/policies/#{policy_name}", policy_data)
    end

    # Now we need to remove any policies that are *not* in our current group.
    if existing_group && existing_group["policies"]
      (existing_group["policies"].keys - policy_datas.keys).each do |policy_name|
        rest.delete("#{api_path}/policies/#{policy_name}")
      end
    end

  rescue Timeout::Error => e
    raise Chef::ChefFS::FileSystem::OperationFailedError.new(:create_child, self, e, "Timeout creating '#{name}': #{e}")
  rescue Net::HTTPClientException => e
    # 404 = NotFoundError
    if e.response.code == "404"
      raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e)
    # 409 = AlreadyExistsError
    elsif $!.response.code == "409"
      raise Chef::ChefFS::FileSystem::AlreadyExistsError.new(:create_child, self, e, "Failure creating '#{name}': #{path}/#{name} already exists")
    # Anything else is unexpected (OperationFailedError)
    else
      raise Chef::ChefFS::FileSystem::OperationFailedError.new(:create_child, self, e, "Failure creating '#{name}': #{e.message}")
    end
  end

  self
end